/*
* qemu_command.c: QEMU command generation
*
* Copyright (C) 2006-2016 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 "qemu_interface.h"
#include "qemu_alias.h"
#include "cpu/cpu.h"
#include "dirname.h"
#include "viralloc.h"
#include "virlog.h"
#include "virarch.h"
#include "virerror.h"
#include "virfile.h"
#include "virnetdev.h"
#include "virnetdevbridge.h"
#include "virqemu.h"
#include "virstring.h"
#include "virtime.h"
#include "viruuid.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 "device_conf.h"
#include "virstoragefile.h"
#include "virtpm.h"
#include "virscsi.h"
#include "virnuma.h"
#include "virgic.h"
#if defined(__linux__)
# include
#endif
#include "logging/log_manager.h"
#include
#include
#define VIR_FROM_THIS VIR_FROM_QEMU
VIR_LOG_INIT("qemu.qemu_command");
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(qemuDiskCacheV2)
VIR_ENUM_IMPL(qemuDiskCacheV2, VIR_DOMAIN_DISK_CACHE_LAST,
"default",
"none",
"writethrough",
"writeback",
"directsync",
"unsafe");
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 */
"" /* no need for virtio */);
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 */
"virtio-vga");
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");
/**
* qemuBuildHasMasterKey:
* @qemuCaps: QEMU binary capabilities
*
* Return true if this binary supports the secret -object, false otherwise.
*/
static bool
qemuBuildHasMasterKey(virQEMUCapsPtr qemuCaps)
{
return virQEMUCapsGet(qemuCaps, QEMU_CAPS_OBJECT_SECRET);
}
/**
* qemuBuildMasterKeyCommandLine:
* @cmd: the command to modify
* @qemuCaps qemu capabilities object
* @domainLibDir: location to find the master key
* Formats the command line for a master key if available
*
* Returns 0 on success, -1 w/ error message on failure
*/
static int
qemuBuildMasterKeyCommandLine(virCommandPtr cmd,
virQEMUCapsPtr qemuCaps,
const char *domainLibDir)
{
int ret = -1;
char *alias = NULL;
char *path = NULL;
virBuffer buf = VIR_BUFFER_INITIALIZER;
/* If the -object secret does not exist, then just return. This just
* means the domain won't be able to use a secret master key and is
* not a failure.
*/
if (!qemuBuildHasMasterKey(qemuCaps)) {
VIR_INFO("secret object is not supported by this QEMU binary");
return 0;
}
if (!(alias = qemuDomainGetMasterKeyAlias()))
return -1;
/* Get the path. NB, the mocked test will not have the created
* file so we cannot check for existence, which is no different
* than other command line options which do not check for the
* existence of socket files before using.
*/
if (!(path = qemuDomainGetMasterKeyFilePath(domainLibDir)))
goto cleanup;
virCommandAddArg(cmd, "-object");
virBufferAsprintf(&buf, "secret,id=%s,format=raw,file=", alias);
virQEMUBuildBufferEscapeComma(&buf, path);
virCommandAddArgBuffer(cmd, &buf);
ret = 0;
cleanup:
virBufferFreeAndReset(&buf);
VIR_FREE(alias);
VIR_FREE(path);
return ret;
}
/**
* 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;
}
char *qemuDeviceDriveHostAlias(virDomainDiskDefPtr disk)
{
char *ret;
if (virAsprintf(&ret, "%s%s",
QEMU_DRIVE_HOST_PREFIX, disk->info.alias) < 0)
return NULL;
return ret;
}
static int
qemuBuildDeviceAddressStr(virBufferPtr buf,
const virDomainDef *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", contAlias);
if (virDomainUSBAddressPortIsValid(info->addr.usb.port)) {
virBufferAddLit(buf, ",port=");
virDomainUSBAddressPortFormatBuf(buf, 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)
{
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;
}
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 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_SSH:
return 22;
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;
}
/**
* qemuBuildSecretInfoProps:
* @secinfo: pointer to the secret info object
* @props: json properties to return
*
* Build the JSON properties for the secret info type.
*
* Returns 0 on success with the filled in JSON property; otherwise,
* returns -1 on failure error message set.
*/
int
qemuBuildSecretInfoProps(qemuDomainSecretInfoPtr secinfo,
virJSONValuePtr *propsret)
{
int ret = -1;
char *keyid = NULL;
if (!(keyid = qemuDomainGetMasterKeyAlias()))
return -1;
if (virJSONValueObjectCreate(propsret,
"s:data", secinfo->s.aes.ciphertext,
"s:keyid", keyid,
"s:iv", secinfo->s.aes.iv,
"s:format", "base64", NULL) < 0)
goto cleanup;
ret = 0;
cleanup:
VIR_FREE(keyid);
return ret;
}
/**
* qemuBuildObjectSecretCommandLine:
* @cmd: the command to modify
* @secinfo: pointer to the secret info object
*
* If the secinfo is available and associated with an AES secret,
* then format the command line for the secret object. This object
* will be referenced by the device that needs/uses it, so it needs
* to be in place first.
*
* Returns 0 on success, -1 w/ error message on failure
*/
static int
qemuBuildObjectSecretCommandLine(virCommandPtr cmd,
qemuDomainSecretInfoPtr secinfo)
{
int ret = -1;
virJSONValuePtr props = NULL;
char *tmp = NULL;
if (qemuBuildSecretInfoProps(secinfo, &props) < 0)
return -1;
if (!(tmp = virQEMUBuildObjectCommandlineFromJSON("secret",
secinfo->s.aes.alias,
props)))
goto cleanup;
virCommandAddArgList(cmd, "-object", tmp, NULL);
ret = 0;
cleanup:
virJSONValueFree(props);
VIR_FREE(tmp);
return ret;
}
/* qemuBuildDiskSecinfoCommandLine:
* @cmd: Pointer to the command string
* @secinfo: Pointer to a possible secinfo
*
* Add the secret object for the disks that will be using it to perform
* their authentication.
*
* Returns 0 on success, -1 w/ error on some sort of failure.
*/
static int
qemuBuildDiskSecinfoCommandLine(virCommandPtr cmd,
qemuDomainSecretInfoPtr secinfo)
{
/* Not necessary for non AES secrets */
if (!secinfo || secinfo->type != VIR_DOMAIN_SECRET_INFO_TYPE_AES)
return 0;
return qemuBuildObjectSecretCommandLine(cmd, secinfo);
}
/* qemuBuildGeneralSecinfoURI:
* @uri: Pointer to the URI structure to add to
* @secinfo: Pointer to the secret info data (if present)
*
* If we have a secinfo, then build the command line options for
* the secret info for the "general" case (somewhat a misnomer since
* an iscsi disk is the only one with a secinfo).
*
* Returns 0 on success or if no secinfo,
* -1 and error message if fail to add secret information
*/
static int
qemuBuildGeneralSecinfoURI(virURIPtr uri,
qemuDomainSecretInfoPtr secinfo)
{
if (!secinfo)
return 0;
switch ((qemuDomainSecretInfoType) secinfo->type) {
case VIR_DOMAIN_SECRET_INFO_TYPE_PLAIN:
if (secinfo->s.plain.secret) {
if (!virStringBufferIsPrintable(secinfo->s.plain.secret,
secinfo->s.plain.secretlen)) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("found non printable characters in secret"));
return -1;
}
if (virAsprintf(&uri->user, "%s:%s",
secinfo->s.plain.username,
secinfo->s.plain.secret) < 0)
return -1;
} else {
if (VIR_STRDUP(uri->user, secinfo->s.plain.username) < 0)
return -1;
}
break;
case VIR_DOMAIN_SECRET_INFO_TYPE_AES:
case VIR_DOMAIN_SECRET_INFO_TYPE_LAST:
return -1;
}
return 0;
}
/* qemuBuildRBDSecinfoURI:
* @uri: Pointer to the URI structure to add to
* @secinfo: Pointer to the secret info data (if present)
*
* If we have a secinfo, then build the command line options for
* the secret info for the RBD network storage. Assumption for this
* is both username and secret exist for plaintext
*
* Returns 0 on success or if no secinfo,
* -1 and error message if fail to add secret information
*/
static int
qemuBuildRBDSecinfoURI(virBufferPtr buf,
qemuDomainSecretInfoPtr secinfo)
{
char *base64secret = NULL;
if (!secinfo) {
virBufferAddLit(buf, ":auth_supported=none");
return 0;
}
switch ((qemuDomainSecretInfoType) secinfo->type) {
case VIR_DOMAIN_SECRET_INFO_TYPE_PLAIN:
if (!(base64secret = virStringEncodeBase64(secinfo->s.plain.secret,
secinfo->s.plain.secretlen)))
return -1;
virBufferEscape(buf, '\\', ":", ":id=%s", secinfo->s.plain.username);
virBufferEscape(buf, '\\', ":",
":key=%s:auth_supported=cephx\\;none",
base64secret);
VIR_DISPOSE_STRING(base64secret);
break;
case VIR_DOMAIN_SECRET_INFO_TYPE_AES:
virBufferEscape(buf, '\\', ":", ":id=%s:auth_supported=cephx\\;none",
secinfo->s.aes.username);
break;
case VIR_DOMAIN_SECRET_INFO_TYPE_LAST:
return -1;
}
return 0;
}
#define QEMU_DEFAULT_NBD_PORT "10809"
#define QEMU_DEFAULT_GLUSTER_PORT "24007"
/* builds the hosts array */
static virJSONValuePtr
qemuBuildGlusterDriveJSONHosts(virStorageSourcePtr src)
{
virJSONValuePtr servers = NULL;
virJSONValuePtr server = NULL;
virJSONValuePtr ret = NULL;
virStorageNetHostDefPtr host;
const char *transport;
const char *portstr;
size_t i;
if (!(servers = virJSONValueNewArray()))
goto cleanup;
for (i = 0; i < src->nhosts; i++) {
host = src->hosts + i;
transport = virStorageNetHostTransportTypeToString(host->transport);
portstr = host->port;
if (virJSONValueObjectCreate(&server, "s:type", transport, NULL) < 0)
goto cleanup;
if (!portstr)
portstr = QEMU_DEFAULT_GLUSTER_PORT;
switch ((virStorageNetHostTransport) host->transport) {
case VIR_STORAGE_NET_HOST_TRANS_TCP:
if (virJSONValueObjectAdd(server,
"s:host", host->name,
"s:port", portstr,
NULL) < 0)
goto cleanup;
break;
case VIR_STORAGE_NET_HOST_TRANS_UNIX:
if (virJSONValueObjectAdd(server,
"s:socket", host->socket,
NULL) < 0)
goto cleanup;
break;
case VIR_STORAGE_NET_HOST_TRANS_RDMA:
case VIR_STORAGE_NET_HOST_TRANS_LAST:
virReportError(VIR_ERR_INTERNAL_ERROR,
_("transport protocol '%s' is not yet supported"),
transport);
goto cleanup;
}
if (virJSONValueArrayAppend(servers, server) < 0)
goto cleanup;
server = NULL;
}
ret = servers;
servers = NULL;
cleanup:
virJSONValueFree(servers);
virJSONValueFree(server);
return ret;
}
static virJSONValuePtr
qemuBuildGlusterDriveJSON(virStorageSourcePtr src)
{
const char *protocol = virStorageNetProtocolTypeToString(src->protocol);
virJSONValuePtr servers = NULL;
virJSONValuePtr ret = NULL;
if (!(servers = qemuBuildGlusterDriveJSONHosts(src)))
return NULL;
/* { driver:"gluster",
* volume:"testvol",
* path:"/a.img",
* server :[{type:"tcp", host:"1.2.3.4", port:24007},
* {type:"unix", socket:"/tmp/glusterd.socket"}, ...]}
*/
if (virJSONValueObjectCreate(&ret,
"s:driver", protocol,
"s:volume", src->volume,
"s:path", src->path,
"a:server", servers, NULL) < 0)
virJSONValueFree(servers);
return ret;
}
static char *
qemuBuildNetworkDriveURI(virStorageSourcePtr src,
qemuDomainSecretInfoPtr secinfo)
{
virURIPtr uri = NULL;
char *ret = NULL;
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 (qemuBuildGeneralSecinfoURI(uri, secinfo) < 0)
goto cleanup;
if (VIR_STRDUP(uri->server, src->hosts->name) < 0)
goto cleanup;
ret = virURIFormat(uri);
cleanup:
virURIFree(uri);
return ret;
}
static char *
qemuBuildNetworkDriveStr(virStorageSourcePtr src,
qemuDomainSecretInfoPtr secinfo)
{
char *ret = NULL;
virBuffer buf = VIR_BUFFER_INITIALIZER;
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;
}
/* NBD code uses URI formatting scheme as others in some cases */
ret = qemuBuildNetworkDriveURI(src, secinfo);
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_ISCSI:
case VIR_STORAGE_NET_PROTOCOL_GLUSTER:
ret = qemuBuildNetworkDriveURI(src, secinfo);
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 (qemuBuildRBDSecinfoURI(&buf, secinfo) < 0)
goto cleanup;
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_SSH:
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("'ssh' protocol is not yet supported"));
goto cleanup;
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);
return ret;
}
static int
qemuGetDriveSourceProps(virStorageSourcePtr src,
virJSONValuePtr *props)
{
int actualType = virStorageSourceGetActualType(src);
virJSONValuePtr fileprops = NULL;
*props = NULL;
switch ((virStorageType) actualType) {
case VIR_STORAGE_TYPE_BLOCK:
case VIR_STORAGE_TYPE_FILE:
case VIR_STORAGE_TYPE_DIR:
case VIR_STORAGE_TYPE_VOLUME:
case VIR_STORAGE_TYPE_NONE:
case VIR_STORAGE_TYPE_LAST:
break;
case VIR_STORAGE_TYPE_NETWORK:
if (src->protocol == VIR_STORAGE_NET_PROTOCOL_GLUSTER &&
src->nhosts > 1) {
if (!(fileprops = qemuBuildGlusterDriveJSON(src)))
return -1;
}
break;
}
if (fileprops &&
virJSONValueObjectCreate(props, "a:file", fileprops, NULL) < 0) {
virJSONValueFree(fileprops);
return -1;
}
return 0;
}
int
qemuGetDriveSourceString(virStorageSourcePtr src,
qemuDomainSecretInfoPtr secinfo,
char **source)
{
int actualType = virStorageSourceGetActualType(src);
int ret = -1;
*source = NULL;
/* return 1 for empty sources */
if (virStorageSourceIsEmpty(src))
return 1;
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 = qemuBuildNetworkDriveStr(src, secinfo)))
goto cleanup;
break;
case VIR_STORAGE_TYPE_VOLUME:
case VIR_STORAGE_TYPE_NONE:
case VIR_STORAGE_TYPE_LAST:
break;
}
ret = 0;
cleanup:
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);
return -1;
}
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"));
return -1;
}
}
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"));
return -1;
}
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));
return -1;
}
if (qemuDomainDefValidateDiskLunSource(disk->src) < 0)
return -1;
if (disk->wwn) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("Setting wwn is not supported for lun device"));
return -1;
}
if (disk->vendor || disk->product) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("Setting vendor or product is not supported "
"for lun device"));
return -1;
}
}
return 0;
}
/* 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(const virDomainDef *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;
}
/* Unfortunately it is not possible to use
-device for floppies, or SD
devices. Fortunately, those don't need
static PCI addresses, so we don't really
care that we can't use -device */
static bool
qemuDiskBusNeedsDeviceArg(int bus)
{
return bus != VIR_DOMAIN_DISK_BUS_SD;
}
static int
qemuBuildDriveSourceStr(virDomainDiskDefPtr disk,
virBufferPtr buf)
{
int actualType = virStorageSourceGetActualType(disk->src);
qemuDomainDiskPrivatePtr diskPriv = QEMU_DOMAIN_DISK_PRIVATE(disk);
qemuDomainSecretInfoPtr secinfo = diskPriv->secinfo;
qemuDomainSecretInfoPtr encinfo = diskPriv->encinfo;
virJSONValuePtr srcprops = NULL;
char *source = NULL;
int ret = -1;
if (qemuGetDriveSourceProps(disk->src, &srcprops) < 0)
goto cleanup;
if (!srcprops &&
qemuGetDriveSourceString(disk->src, secinfo, &source) < 0)
goto cleanup;
/* nothing to format if the drive is empty */
if (!(source || srcprops) ||
((disk->device == VIR_DOMAIN_DISK_DEVICE_FLOPPY ||
disk->device == VIR_DOMAIN_DISK_DEVICE_CDROM) &&
disk->tray_status == VIR_DOMAIN_DISK_TRAY_OPEN)) {
ret = 0;
goto cleanup;
}
if (actualType == VIR_STORAGE_TYPE_BLOCK &&
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 cleanup;
}
if (source) {
virBufferAddLit(buf, "file=");
/* for now the DIR based storage is handled by the magic FAT format */
if (actualType == VIR_STORAGE_TYPE_DIR) {
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 cleanup;
}
if (!disk->src->readonly) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("cannot create virtual FAT disks in read-write mode"));
goto cleanup;
}
virBufferAddLit(buf, "fat:");
if (disk->device == VIR_DOMAIN_DISK_DEVICE_FLOPPY)
virBufferAddLit(buf, "floppy:");
}
virQEMUBuildBufferEscapeComma(buf, source);
} else {
if (!(source = virQEMUBuildDriveCommandlineFromJSON(srcprops)))
goto cleanup;
virBufferAdd(buf, source, -1);
}
virBufferAddLit(buf, ",");
if (secinfo && secinfo->type == VIR_DOMAIN_SECRET_INFO_TYPE_AES) {
virBufferAsprintf(buf, "password-secret=%s,",
secinfo->s.aes.alias);
}
if (encinfo)
virQEMUBuildLuksOpts(buf, &disk->src->encryption->encinfo,
encinfo->s.aes.alias);
if (disk->src->format > 0 &&
disk->src->type != VIR_STORAGE_TYPE_DIR) {
const char *qemuformat = virStorageFileFormatTypeToString(disk->src->format);
if (disk->src->encryption &&
disk->src->encryption->format == VIR_STORAGE_ENCRYPTION_FORMAT_LUKS)
qemuformat = "luks";
virBufferAsprintf(buf, "format=%s,", qemuformat);
}
ret = 0;
cleanup:
VIR_FREE(source);
virJSONValueFree(srcprops);
return ret;
}
char *
qemuBuildDriveStr(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;
bool emitDeviceSyntax = qemuDiskBusNeedsDeviceArg(disk->bus);
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 (qemuBuildDriveSourceStr(disk, &opt) < 0)
goto error;
if (emitDeviceSyntax)
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 (emitDeviceSyntax) {
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) {
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;
}
/* 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;
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;
}
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=none");
}
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 (disk->detect_zeroes) {
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DRIVE_DETECT_ZEROES)) {
int detect_zeroes = disk->detect_zeroes;
/*
* As a convenience syntax, if discards are ignored and
* zero detection is set to 'unmap', then simply behave
* like zero detection is set to 'on'. But don't change
* it in the XML for easier adjustments. This behaviour
* is documented.
*/
if (disk->discard != VIR_DOMAIN_DISK_DISCARD_UNMAP &&
detect_zeroes == VIR_DOMAIN_DISK_DETECT_ZEROES_UNMAP)
detect_zeroes = VIR_DOMAIN_DISK_DETECT_ZEROES_ON;
virBufferAsprintf(&opt, ",detect-zeroes=%s",
virDomainDiskDetectZeroesTypeToString(detect_zeroes));
} else {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("detect_zeroes 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 > QEMU_BLOCK_IOTUNE_MAX ||
disk->blkdeviotune.read_bytes_sec > QEMU_BLOCK_IOTUNE_MAX ||
disk->blkdeviotune.write_bytes_sec > QEMU_BLOCK_IOTUNE_MAX ||
disk->blkdeviotune.total_iops_sec > QEMU_BLOCK_IOTUNE_MAX ||
disk->blkdeviotune.read_iops_sec > QEMU_BLOCK_IOTUNE_MAX ||
disk->blkdeviotune.write_iops_sec > QEMU_BLOCK_IOTUNE_MAX ||
disk->blkdeviotune.total_bytes_sec_max > QEMU_BLOCK_IOTUNE_MAX ||
disk->blkdeviotune.read_bytes_sec_max > QEMU_BLOCK_IOTUNE_MAX ||
disk->blkdeviotune.write_bytes_sec_max > QEMU_BLOCK_IOTUNE_MAX ||
disk->blkdeviotune.total_iops_sec_max > QEMU_BLOCK_IOTUNE_MAX ||
disk->blkdeviotune.read_iops_sec_max > QEMU_BLOCK_IOTUNE_MAX ||
disk->blkdeviotune.write_iops_sec_max > QEMU_BLOCK_IOTUNE_MAX ||
disk->blkdeviotune.size_iops_sec > QEMU_BLOCK_IOTUNE_MAX) {
virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED,
_("block I/O throttle limit must "
"be no more than %llu using QEMU"), QEMU_BLOCK_IOTUNE_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:
virBufferFreeAndReset(&opt);
return NULL;
}
static bool
qemuCheckIOThreads(const virDomainDef *def,
virDomainDiskDefPtr disk)
{
/* Right "type" of disk" */
switch ((virDomainDiskBus)disk->bus) {
case VIR_DOMAIN_DISK_BUS_VIRTIO:
if (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;
}
break;
case VIR_DOMAIN_DISK_BUS_IDE:
case VIR_DOMAIN_DISK_BUS_FDC:
case VIR_DOMAIN_DISK_BUS_SCSI:
case VIR_DOMAIN_DISK_BUS_XEN:
case VIR_DOMAIN_DISK_BUS_USB:
case VIR_DOMAIN_DISK_BUS_UML:
case VIR_DOMAIN_DISK_BUS_SATA:
case VIR_DOMAIN_DISK_BUS_SD:
case VIR_DOMAIN_DISK_BUS_LAST:
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("IOThreads not available for bus %s target %s"),
virDomainDiskBusTypeToString(disk->bus), disk->dst);
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(const virDomainDef *def,
virDomainDiskDefPtr disk,
unsigned int bootindex,
virQEMUCapsPtr qemuCaps)
{
virBuffer opt = VIR_BUFFER_INITIALIZER;
const char *bus = virDomainDiskQEMUBusTypeToString(disk->bus);
const char *contAlias;
int controllerModel;
if (qemuCheckDiskConfig(disk) < 0)
goto error;
if (!qemuCheckCCWS390AddressSupport(def, disk->info, qemuCaps, disk->dst))
goto error;
if (disk->iothread && !qemuCheckIOThreads(def, 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 ((qemuDomainSetSCSIControllerModel(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=%u", 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;
}
static int
qemuBuildDiskDriveCommandLine(virCommandPtr cmd,
const virDomainDef *def,
virQEMUCapsPtr qemuCaps)
{
size_t i;
unsigned int bootCD = 0;
unsigned int bootFloppy = 0;
unsigned int bootDisk = 0;
virBuffer fdc_opts = VIR_BUFFER_INITIALIZER;
char *fdc_opts_str = NULL;
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DRIVE_BOOT) ||
virQEMUCapsGet(qemuCaps, QEMU_CAPS_BOOTINDEX)) {
/* 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;
unsigned int bootindex = 0;
bool driveBoot = false;
virDomainDiskDefPtr disk = def->disks[i];
qemuDomainDiskPrivatePtr diskPriv = QEMU_DOMAIN_DISK_PRIVATE(disk);
qemuDomainSecretInfoPtr secinfo = diskPriv->secinfo;
qemuDomainSecretInfoPtr encinfo = diskPriv->encinfo;
/* PowerPC pseries based VMs do not support floppy device */
if (disk->device == VIR_DOMAIN_DISK_DEVICE_FLOPPY &&
qemuDomainMachineIsPSeries(def)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("PowerPC pseries machines do not support floppy device"));
return -1;
}
if (disk->info.bootIndex) {
bootindex = disk->info.bootIndex;
} else {
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;
}
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_BOOTINDEX)) {
driveBoot = !!bootindex;
bootindex = 0;
}
}
if (qemuBuildDiskSecinfoCommandLine(cmd, secinfo) < 0)
return -1;
if (qemuBuildDiskSecinfoCommandLine(cmd, encinfo) < 0)
return -1;
virCommandAddArg(cmd, "-drive");
if (!(optstr = qemuBuildDriveStr(disk, driveBoot, qemuCaps)))
return -1;
virCommandAddArg(cmd, optstr);
VIR_FREE(optstr);
if (qemuDiskBusNeedsDeviceArg(disk->bus)) {
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)
return -1;
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=%u",
disk->info.addr.drive.unit
? 'B' : 'A',
bootindex) < 0)
return -1;
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)))
return -1;
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);
}
return 0;
}
static char *
qemuBuildFSStr(virDomainFSDefPtr fs,
virQEMUCapsPtr qemuCaps)
{
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->path);
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;
}
static char *
qemuBuildFSDevStr(const virDomainDef *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
qemuBuildFSDevCommandLine(virCommandPtr cmd,
const virDomainDef *def,
virQEMUCapsPtr qemuCaps)
{
size_t i;
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_FSDEV) && def->nfss) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("filesystem passthrough not supported by this QEMU"));
return -1;
}
for (i = 0; i < def->nfss; i++) {
char *optstr;
virDomainFSDefPtr fs = def->fss[i];
virCommandAddArg(cmd, "-fsdev");
if (!(optstr = qemuBuildFSStr(fs, qemuCaps)))
return -1;
virCommandAddArg(cmd, optstr);
VIR_FREE(optstr);
virCommandAddArg(cmd, "-device");
if (!(optstr = qemuBuildFSDevStr(def, fs, qemuCaps)))
return -1;
virCommandAddArg(cmd, optstr);
VIR_FREE(optstr);
}
return 0;
}
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(const virDomainDef *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->opts.usbopts.ports != -1) {
if (model != VIR_DOMAIN_CONTROLLER_MODEL_USB_NEC_XHCI ||
!virQEMUCapsGet(qemuCaps, QEMU_CAPS_NEC_USB_XHCI_PORTS)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("usb controller type %s doesn't support 'ports' "
"with this QEMU binary"), smodel);
return -1;
}
virBufferAsprintf(buf, ",p2=%d,p3=%d",
def->opts.usbopts.ports, def->opts.usbopts.ports);
}
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;
}
/* qemuCheckSCSIControllerIOThreads:
* @domainDef: Pointer to domain def
* @def: Pointer to controller def
* @qemuCaps: Capabilities
*
* If this controller definition has iothreads set, let's make sure the
* configuration is right before adding to the command line
*
* Returns true if either supported or there are no iothreads for controller;
* otherwise, returns false if configuration is not quite right.
*/
static bool
qemuCheckSCSIControllerIOThreads(const virDomainDef *domainDef,
virDomainControllerDefPtr def,
virQEMUCapsPtr qemuCaps)
{
if (!def->iothread)
return true;
/* By this time QEMU_CAPS_OBJECT_IOTHREAD was already checked.
* We just need to check if the QEMU_CAPS_VIRTIO_SCSI_IOTHREAD
* capability is set.
*/
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_VIRTIO_SCSI_IOTHREAD)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("IOThreads for virtio-scsi not supported for "
"this QEMU"));
return false;
}
if (def->model != VIR_DOMAIN_CONTROLLER_MODEL_SCSI_VIRTIO_SCSI) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("IOThreads only supported for virtio-scsi "
"controllers model is '%s'"),
virDomainControllerModelSCSITypeToString(def->model));
return false;
}
if (def->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI &&
def->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_CCW) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("IOThreads only available for virtio pci and "
"virtio ccw controllers"));
return false;
}
/* Can we find the controller iothread in the iothreadid list? */
if (!virDomainIOThreadIDFind(domainDef, def->iothread)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("controller iothread '%u' not defined in iothreadid"),
def->iothread);
return false;
}
return true;
}
char *
qemuBuildControllerDevStr(const virDomainDef *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 ((qemuDomainSetSCSIControllerModel(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");
if (def->iothread) {
if (!qemuCheckSCSIControllerIOThreads(domainDef,
def, qemuCaps))
goto error;
virBufferAsprintf(&buf, ",iothread=iothread%u",
def->iothread);
}
} 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");
if (def->iothread) {
if (!qemuCheckSCSIControllerIOThreads(domainDef,
def, qemuCaps))
goto error;
virBufferAsprintf(&buf, ",iothread=iothread%u",
def->iothread);
}
}
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_LSISAS1068:
virBufferAddLit(&buf, "mptsas1068");
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_PCI_EXPANDER_BUS:
if (def->opts.pciopts.modelName
== VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_NONE ||
def->opts.pciopts.busNr == -1) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("autogenerated pci-expander-bus options not set"));
goto error;
}
modelName = virDomainControllerPCIModelNameTypeToString(def->opts.pciopts.modelName);
if (!modelName) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("unknown pci-expander-bus model name value %d"),
def->opts.pciopts.modelName);
goto error;
}
if (def->opts.pciopts.modelName
!= VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_PXB) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("PCI controller model name '%s' "
"is not valid for a pci-expander-bus"),
modelName);
goto error;
}
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_PXB)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("the pxb controller "
"is not supported in this QEMU binary"));
goto error;
}
virBufferAsprintf(&buf, "%s,bus_nr=%d,id=%s",
modelName, def->opts.pciopts.busNr,
def->info.alias);
if (def->opts.pciopts.numaNode != -1)
virBufferAsprintf(&buf, ",numa_node=%d",
def->opts.pciopts.numaNode);
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;
case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_EXPANDER_BUS:
if (def->opts.pciopts.modelName
== VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_NONE ||
def->opts.pciopts.busNr == -1) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("autogenerated pcie-expander-bus options not set"));
goto error;
}
modelName = virDomainControllerPCIModelNameTypeToString(def->opts.pciopts.modelName);
if (!modelName) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("unknown pcie-expander-bus model name value %d"),
def->opts.pciopts.modelName);
goto error;
}
if (def->opts.pciopts.modelName
!= VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_PXB_PCIE) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("PCI controller model name '%s' "
"is not valid for a pcie-expander-bus"),
modelName);
goto error;
}
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_PXB_PCIE)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("the pxb-pcie controller "
"is not supported in this QEMU binary"));
goto error;
}
virBufferAsprintf(&buf, "%s,bus_nr=%d,id=%s",
modelName, def->opts.pciopts.busNr,
def->info.alias);
if (def->opts.pciopts.numaNode != -1)
virBufferAsprintf(&buf, ",numa_node=%d",
def->opts.pciopts.numaNode);
break;
}
break;
case VIR_DOMAIN_CONTROLLER_TYPE_IDE:
/* Since we currently only support the integrated IDE
* controller on various boards, if we ever get to here, it's
* because some other machinetype had an IDE controller
* specified, or one with a single IDE contraller had multiple
* ide controllers specified.
*/
if (qemuDomainMachineHasBuiltinIDE(domainDef))
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("Only a single IDE controller is supported "
"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;
}
static int
qemuBuildControllerDevCommandLine(virCommandPtr cmd,
const virDomainDef *def,
virQEMUCapsPtr qemuCaps)
{
size_t i, j;
int usbcontroller = 0;
bool usblegacy = 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,
};
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 is implicit on various machines */
if (cont->type == VIR_DOMAIN_CONTROLLER_TYPE_IDE &&
cont->idx == 0 && qemuDomainMachineHasBuiltinIDE(def))
continue;
if (cont->type == VIR_DOMAIN_CONTROLLER_TYPE_USB &&
cont->model == -1 &&
!qemuDomainMachineIsQ35(def)) {
bool need_legacy = false;
/* We're not using legacy usb controller for q35 */
if (ARCH_IS_PPC64(def->os.arch)) {
/* For ppc64 the legacy was OHCI */
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_PCI_OHCI))
need_legacy = true;
} else {
/* For anything else, we used PIIX3_USB_UHCI */
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_PIIX3_USB_UHCI))
need_legacy = true;
}
if (need_legacy) {
if (usblegacy) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("Multiple legacy USB controllers are "
"not supported"));
return -1;
}
usblegacy = true;
continue;
}
}
virCommandAddArg(cmd, "-device");
if (!(devstr = qemuBuildControllerDevStr(def, cont, qemuCaps,
&usbcontroller)))
return -1;
virCommandAddArg(cmd, devstr);
VIR_FREE(devstr);
}
}
if (usbcontroller == 0 &&
!qemuDomainMachineIsQ35(def) &&
!qemuDomainMachineIsVirt(def) &&
!ARCH_IS_S390(def->os.arch))
virCommandAddArg(cmd, "-usb");
return 0;
}
/**
* 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, or -1 if NUMA is not used in the guest
* @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;
bool nodeSpecified = virDomainNumatuneNodeSpecified(def->numa, guestNode);
*backendProps = NULL;
*backendType = NULL;
if (guestNode >= 0) {
/* 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;
}
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;
}
/* just find the master hugepage in case we don't use NUMA */
if (guestNode < 0)
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 (!(props = virJSONValueNewObject()))
return -1;
if (pagesize || hugepage) {
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 none of the following is requested... */
if (!pagesize && !userNodeset && !memAccess && !nodeSpecified && !force) {
/* report back that using the new backend is not necessary
* to achieve the desired configuration */
ret = 1;
} else {
/* otherwise check the required capability */
if (STREQ(*backendType, "memory-backend-file") &&
!virQEMUCapsGet(qemuCaps, QEMU_CAPS_OBJECT_MEMORY_FILE)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("this qemu doesn't support the "
"memory-backend-file object"));
goto cleanup;
} else if (STREQ(*backendType, "memory-backend-ram") &&
!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;
}
ret = 0;
}
*backendProps = props;
props = NULL;
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;
unsigned long long memsize = virDomainNumaGetNodeMemorySize(def->numa,
cell);
*backendStr = NULL;
if (virAsprintf(&alias, "ram-node%zu", cell) < 0)
goto cleanup;
if ((rc = qemuBuildMemoryBackendStr(memsize, 0, cell, NULL, auto_nodeset,
def, qemuCaps, cfg, &backendType,
&props, false)) < 0)
goto cleanup;
if (!(*backendStr = virQEMUBuildObjectCommandlineFromJSON(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,
virBitmapPtr auto_nodeset)
{
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, auto_nodeset,
def, qemuCaps, cfg,
&backendType, &props, true) < 0)
goto cleanup;
ret = virQEMUBuildObjectCommandlineFromJSON(backendType, alias, props);
cleanup:
VIR_FREE(alias);
virJSONValueFree(props);
return ret;
}
char *
qemuBuildMemoryDeviceStr(virDomainMemoryDefPtr mem)
{
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:
virBufferAddLit(&buf, "pc-dimm,");
if (mem->targetNode >= 0)
virBufferAsprintf(&buf, "node=%d,", mem->targetNode);
virBufferAsprintf(&buf, "memdev=mem%s,id=%s",
mem->info.alias, mem->info.alias);
if (mem->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DIMM) {
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:
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,
unsigned 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) < 0)
goto error;
if (bootindex && virQEMUCapsGet(qemuCaps, QEMU_CAPS_BOOTINDEX))
virBufferAsprintf(&buf, ",bootindex=%u", 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:
case VIR_DOMAIN_NET_TYPE_ETHERNET:
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_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);
}
static char *
qemuBuildWatchdogDevStr(const virDomainDef *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;
}
static int
qemuBuildWatchdogCommandLine(virCommandPtr cmd,
const virDomainDef *def,
virQEMUCapsPtr qemuCaps)
{
virDomainWatchdogDefPtr watchdog = def->watchdog;
char *optstr;
const char *action;
int actualAction;
if (!def->watchdog)
return 0;
virCommandAddArg(cmd, "-device");
optstr = qemuBuildWatchdogDevStr(def, watchdog, qemuCaps);
if (!optstr)
return -1;
virCommandAddArg(cmd, optstr);
VIR_FREE(optstr);
/* qemu doesn't have a 'dump' action; we tell qemu to 'pause', then
libvirt listens for the watchdog event, and we perform the dump
ourselves. so convert 'dump' to 'pause' for the qemu cli */
actualAction = watchdog->action;
if (watchdog->action == VIR_DOMAIN_WATCHDOG_ACTION_DUMP)
actualAction = VIR_DOMAIN_WATCHDOG_ACTION_PAUSE;
action = virDomainWatchdogActionTypeToString(actualAction);
if (!action) {
virReportError(VIR_ERR_INTERNAL_ERROR,
"%s", _("invalid watchdog action"));
return -1;
}
virCommandAddArgList(cmd, "-watchdog-action", action, NULL);
return 0;
}
static int
qemuBuildMemballoonCommandLine(virCommandPtr cmd,
const virDomainDef *def,
virQEMUCapsPtr qemuCaps)
{
virBuffer buf = VIR_BUFFER_INITIALIZER;
if (STRPREFIX(def->os.machine, "s390-virtio") &&
virQEMUCapsGet(qemuCaps, QEMU_CAPS_VIRTIO_S390) && def->memballoon)
def->memballoon->model = VIR_DOMAIN_MEMBALLOON_MODEL_NONE;
if (!virDomainDefHasMemballoon(def))
return 0;
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));
return -1;
}
switch (def->memballoon->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(def->memballoon->info.type));
goto error;
}
virBufferAsprintf(&buf, ",id=%s", def->memballoon->info.alias);
if (qemuBuildDeviceAddressStr(&buf, def, &def->memballoon->info, qemuCaps) < 0)
goto error;
if (def->memballoon->autodeflate != VIR_TRISTATE_SWITCH_ABSENT) {
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_VIRTIO_BALLOON_AUTODEFLATE)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("deflate-on-oom is not supported by this QEMU binary"));
goto error;
}
virBufferAsprintf(&buf, ",deflate-on-oom=%s",
virTristateSwitchTypeToString(def->memballoon->autodeflate));
}
virCommandAddArg(cmd, "-device");
virCommandAddArgBuffer(cmd, &buf);
return 0;
error:
virBufferFreeAndReset(&buf);
return -1;
}
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;
}
static int
qemuBuildNVRAMCommandLine(virCommandPtr cmd,
const virDomainDef *def,
virQEMUCapsPtr qemuCaps)
{
if (!def->nvram)
return 0;
if (qemuDomainMachineIsPSeries(def)) {
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_NVRAM)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("nvram device is not supported by "
"this QEMU binary"));
return -1;
}
char *optstr;
virCommandAddArg(cmd, "-global");
optstr = qemuBuildNVRAMDevStr(def->nvram);
if (!optstr)
return -1;
if (optstr)
virCommandAddArg(cmd, optstr);
VIR_FREE(optstr);
} else {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("nvram device is only supported for PPC64"));
return -1;
}
return 0;
}
static char *
qemuBuildVirtioInputDevStr(const virDomainDef *def,
virDomainInputDefPtr dev,
virQEMUCapsPtr qemuCaps)
{
virBuffer buf = VIR_BUFFER_INITIALIZER;
const char *suffix;
if (dev->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) {
suffix = "-pci";
} else if (dev->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_MMIO) {
suffix = "-device";
} else {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("unsupported address type %s for virtio input device"),
virDomainDeviceAddressTypeToString(dev->info.type));
goto error;
}
switch ((virDomainInputType) dev->type) {
case VIR_DOMAIN_INPUT_TYPE_MOUSE:
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_VIRTIO_MOUSE)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("virtio-mouse is not supported by this QEMU binary"));
goto error;
}
virBufferAsprintf(&buf, "virtio-mouse%s,id=%s", suffix, dev->info.alias);
break;
case VIR_DOMAIN_INPUT_TYPE_TABLET:
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_VIRTIO_TABLET)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("virtio-tablet is not supported by this QEMU binary"));
goto error;
}
virBufferAsprintf(&buf, "virtio-tablet%s,id=%s", suffix, dev->info.alias);
break;
case VIR_DOMAIN_INPUT_TYPE_KBD:
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_VIRTIO_KEYBOARD)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("virtio-keyboard is not supported by this QEMU binary"));
goto error;
}
virBufferAsprintf(&buf, "virtio-keyboard%s,id=%s", suffix, dev->info.alias);
break;
case VIR_DOMAIN_INPUT_TYPE_PASSTHROUGH:
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_VIRTIO_INPUT_HOST)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("virtio-input-host is not supported by this QEMU binary"));
goto error;
}
virBufferAsprintf(&buf, "virtio-input-host%s,id=%s,evdev=", suffix, dev->info.alias);
virQEMUBuildBufferEscapeComma(&buf, dev->source.evdev);
break;
case VIR_DOMAIN_INPUT_TYPE_LAST:
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;
}
static char *
qemuBuildUSBInputDevStr(const virDomainDef *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;
}
static int
qemuBuildInputCommandLine(virCommandPtr cmd,
const virDomainDef *def,
virQEMUCapsPtr qemuCaps)
{
size_t i;
for (i = 0; i < def->ninputs; i++) {
virDomainInputDefPtr input = def->inputs[i];
if (input->bus == VIR_DOMAIN_INPUT_BUS_USB) {
char *optstr;
virCommandAddArg(cmd, "-device");
if (!(optstr = qemuBuildUSBInputDevStr(def, input, qemuCaps)))
return -1;
virCommandAddArg(cmd, optstr);
VIR_FREE(optstr);
} else if (input->bus == VIR_DOMAIN_INPUT_BUS_VIRTIO) {
char *optstr;
virCommandAddArg(cmd, "-device");
if (!(optstr = qemuBuildVirtioInputDevStr(def, input, qemuCaps)))
return -1;
virCommandAddArg(cmd, optstr);
VIR_FREE(optstr);
}
}
return 0;
}
static char *
qemuBuildSoundDevStr(const virDomainDef *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 int
qemuBuildSoundCommandLine(virCommandPtr cmd,
const virDomainDef *def,
virQEMUCapsPtr qemuCaps)
{
size_t i, j;
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)))
return -1;
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))) {
return -1;
}
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))) {
return -1;
}
virCommandAddArg(cmd, codecstr);
VIR_FREE(codecstr);
}
}
}
}
return 0;
}
static char *
qemuBuildDeviceVideoStr(const virDomainDef *def,
virDomainVideoDefPtr video,
virQEMUCapsPtr qemuCaps)
{
virBuffer buf = VIR_BUFFER_INITIALIZER;
const char *model;
if (video->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->accel && video->accel->accel2d == VIR_TRISTATE_SWITCH_ON) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("qemu does not support the accel2d setting"));
goto error;
}
if (video->accel && video->accel->accel3d == VIR_TRISTATE_SWITCH_ON) {
if (video->type != VIR_DOMAIN_VIDEO_TYPE_VIRTIO ||
!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_VIRTIO_GPU_VIRGL)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("%s 3d acceleration is not supported"),
virDomainVideoTypeToString(video->type));
goto error;
}
virBufferAsprintf(&buf, ",virgl=%s",
virTristateSwitchTypeToString(video->accel->accel3d));
}
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 ((video->primary &&
virQEMUCapsGet(qemuCaps, QEMU_CAPS_QXL_VGA_VRAM64)) ||
(!video->primary &&
virQEMUCapsGet(qemuCaps, QEMU_CAPS_QXL_VRAM64))) {
/* QEMU accepts mebibytes for vram64_size_mb. */
virBufferAsprintf(&buf, ",vram64_size_mb=%u", video->vram64 / 1024);
}
if ((video->primary &&
virQEMUCapsGet(qemuCaps, QEMU_CAPS_QXL_VGA_VGAMEM)) ||
(!video->primary &&
virQEMUCapsGet(qemuCaps, QEMU_CAPS_QXL_VGAMEM))) {
/* QEMU accepts mebibytes for vgamem_mb. */
virBufferAsprintf(&buf, ",vgamem_mb=%u", video->vgamem / 1024);
}
if ((video->primary &&
virQEMUCapsGet(qemuCaps, QEMU_CAPS_QXL_VGA_MAX_OUTPUTS)) ||
(!video->primary &&
virQEMUCapsGet(qemuCaps, QEMU_CAPS_QXL_MAX_OUTPUTS))) {
if (video->heads)
virBufferAsprintf(&buf, ",max_outputs=%u", video->heads);
} else {
video->heads = 0;
}
} 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;
}
static int
qemuBuildVideoCommandLine(virCommandPtr cmd,
const virDomainDef *def,
virQEMUCapsPtr qemuCaps)
{
size_t i;
int primaryVideoType;
if (!def->videos)
return 0;
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)) ||
(primaryVideoType == VIR_DOMAIN_VIDEO_TYPE_VIRTIO &&
virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_VIRTIO_GPU)))) {
for (i = 0; i < def->nvideos; i++) {
char *str;
virCommandAddArg(cmd, "-device");
if (!(str = qemuBuildDeviceVideoStr(def, def->videos[i],
qemuCaps)))
return -1;
virCommandAddArg(cmd, str);
VIR_FREE(str);
}
} else {
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"));
return -1;
}
const char *vgastr = qemuVideoTypeToString(primaryVideoType);
if (!vgastr || STREQ(vgastr, "")) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("video type %s is not supported with QEMU"),
virDomainVideoTypeToString(primaryVideoType));
return -1;
}
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)) {
unsigned int ram = def->videos[0]->ram;
unsigned int vram = def->videos[0]->vram;
unsigned int vram64 = def->videos[0]->vram64;
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);
return -1;
}
if (ram > (UINT_MAX / 1024)) {
virReportError(VIR_ERR_OVERFLOW,
_("value for 'ram' must be less than '%u'"),
UINT_MAX / 1024);
return -1;
}
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 (vram64 &&
virQEMUCapsGet(qemuCaps, QEMU_CAPS_QXL_VGA_VRAM64)) {
virCommandAddArg(cmd, "-global");
virCommandAddArgFormat(cmd, "%s.vram64_size_mb=%u",
dev, vram64 / 1024);
}
if (vgamem &&
virQEMUCapsGet(qemuCaps, QEMU_CAPS_QXL_VGA_VGAMEM)) {
virCommandAddArg(cmd, "-global");
virCommandAddArgFormat(cmd, "%s.vgamem_mb=%u",
dev, vgamem / 1024);
}
}
if (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)"));
return -1;
}
virCommandAddArg(cmd, "-global");
virCommandAddArgFormat(cmd, "%s.vgamem_mb=%u",
dev, vram / 1024);
}
}
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));
return -1;
}
virCommandAddArg(cmd, "-device");
if (!(str = qemuBuildDeviceVideoStr(def, def->videos[i],
qemuCaps)))
return -1;
virCommandAddArg(cmd, str);
VIR_FREE(str);
}
}
return 0;
}
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(const virDomainDef *def,
virDomainHostdevDefPtr dev,
unsigned int bootIndex, /* used iff dev->info->bootIndex == 0 */
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;
case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_DEFAULT:
case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_XEN:
case VIR_DOMAIN_HOSTDEV_PCI_BACKEND_TYPE_LAST:
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)
bootIndex = dev->info->bootIndex;
if (bootIndex)
virBufferAsprintf(&buf, ",bootindex=%u", bootIndex);
if (qemuBuildDeviceAddressStr(&buf, def, dev->info, qemuCaps) < 0)
goto error;
if (qemuBuildRomStr(&buf, dev->info) < 0)
goto error;
if (virBufferCheckError(&buf) < 0)
goto error;
return virBufferContentAndReset(&buf);
error:
virBufferFreeAndReset(&buf);
return NULL;
}
char *
qemuBuildUSBHostdevDevStr(const virDomainDef *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=%u", 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;
}
static char *
qemuBuildHubDevStr(const virDomainDef *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;
}
static int
qemuBuildHubCommandLine(virCommandPtr cmd,
const virDomainDef *def,
virQEMUCapsPtr qemuCaps)
{
size_t i;
for (i = 0; i < def->nhubs; i++) {
virDomainHubDefPtr hub = def->hubs[i];
char *optstr;
virCommandAddArg(cmd, "-device");
if (!(optstr = qemuBuildHubDevStr(def, hub, qemuCaps)))
return -1;
virCommandAddArg(cmd, optstr);
VIR_FREE(optstr);
}
return 0;
}
static char *
qemuBuildSCSIHostHostdevDrvStr(virDomainHostdevDefPtr dev)
{
virDomainHostdevSubsysSCSIPtr scsisrc = &dev->source.subsys.u.scsi;
virDomainHostdevSubsysSCSIHostPtr scsihostsrc = &scsisrc->u.host;
return virSCSIDeviceGetSgName(NULL,
scsihostsrc->adapter,
scsihostsrc->bus,
scsihostsrc->target,
scsihostsrc->unit);
}
static char *
qemuBuildSCSIiSCSIHostdevDrvStr(virDomainHostdevDefPtr dev)
{
char *source = NULL;
virStorageSource src;
qemuDomainHostdevPrivatePtr hostdevPriv = QEMU_DOMAIN_HOSTDEV_PRIVATE(dev);
memset(&src, 0, sizeof(src));
virDomainHostdevSubsysSCSIPtr scsisrc = &dev->source.subsys.u.scsi;
virDomainHostdevSubsysSCSIiSCSIPtr iscsisrc = &scsisrc->u.iscsi;
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 = qemuBuildNetworkDriveStr(&src, hostdevPriv->secinfo);
return source;
}
char *
qemuBuildSCSIHostdevDrvStr(virDomainHostdevDefPtr dev)
{
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(dev)))
goto error;
virBufferAsprintf(&buf, "file=%s,if=none,format=raw", source);
} else {
if (!(source = qemuBuildSCSIHostHostdevDrvStr(dev)))
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)
virBufferAddLit(&buf, ",readonly=on");
if (virBufferCheckError(&buf) < 0)
goto error;
VIR_FREE(source);
return virBufferContentAndReset(&buf);
error:
VIR_FREE(source);
virBufferFreeAndReset(&buf);
return NULL;
}
char *
qemuBuildSCSIHostdevDevStr(const virDomainDef *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 (qemuDomainSetSCSIControllerModel(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=%u", dev->info->bootIndex);
if (virBufferCheckError(&buf) < 0)
goto error;
return virBufferContentAndReset(&buf);
error:
virBufferFreeAndReset(&buf);
return NULL;
}
static int
qemuBuildChrChardevFileStr(virLogManagerPtr logManager,
virCommandPtr cmd,
const virDomainDef *def,
virBufferPtr buf,
const char *filearg, const char *fileval,
const char *appendarg, int appendval)
{
if (logManager) {
char *fdset, *fdpath;
int flags = 0;
int logfd;
if (appendval == VIR_TRISTATE_SWITCH_OFF)
flags |= VIR_LOG_MANAGER_PROTOCOL_DOMAIN_OPEN_LOG_FILE_TRUNCATE;
if ((logfd = virLogManagerDomainOpenLogFile(logManager,
"qemu",
def->uuid,
def->name,
fileval,
flags,
NULL, NULL)) < 0)
return -1;
virCommandPassFD(cmd, logfd, VIR_COMMAND_PASS_FD_CLOSE_PARENT);
if (!(fdset = qemuVirCommandGetFDSet(cmd, logfd)))
return -1;
virCommandAddArg(cmd, "-add-fd");
virCommandAddArg(cmd, fdset);
VIR_FREE(fdset);
if (!(fdpath = qemuVirCommandGetDevSet(cmd, logfd)))
return -1;
virBufferAsprintf(buf, ",%s=%s,%s=on", filearg, fdpath, appendarg);
VIR_FREE(fdpath);
} else {
virBufferAsprintf(buf, ",%s=%s", filearg, fileval);
if (appendval != VIR_TRISTATE_SWITCH_ABSENT) {
virBufferAsprintf(buf, ",%s=%s", appendarg,
virTristateSwitchTypeToString(appendval));
}
}
return 0;
}
/* This function outputs a -chardev command line option which describes only the
* host side of the character device */
static char *
qemuBuildChrChardevStr(virLogManagerPtr logManager,
virCommandPtr cmd,
virQEMUDriverConfigPtr cfg ATTRIBUTE_UNUSED,
const virDomainDef *def,
const virDomainChrSourceDef *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", alias);
if (dev->data.file.append != VIR_TRISTATE_SWITCH_ABSENT &&
!virQEMUCapsGet(qemuCaps, QEMU_CAPS_CHARDEV_FILE_APPEND)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("append not supported in this QEMU binary"));
goto error;
}
if (qemuBuildChrChardevFileStr(virQEMUCapsGet(qemuCaps, QEMU_CAPS_CHARDEV_FILE_APPEND) ?
logManager : NULL, cmd, def, &buf,
"path", dev->data.file.path,
"append", dev->data.file.append) < 0)
goto error;
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=", alias);
virQEMUBuildBufferEscapeComma(&buf, dev->data.nix.path);
if (dev->data.nix.listen)
virBufferAddLit(&buf, ",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 (dev->logfile) {
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_CHARDEV_LOGFILE)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("logfile not supported in this QEMU binary"));
goto error;
}
if (qemuBuildChrChardevFileStr(logManager, cmd, def, &buf,
"logfile", dev->logfile,
"logappend", dev->logappend) < 0)
goto error;
}
if (virBufferCheckError(&buf) < 0)
goto error;
return virBufferContentAndReset(&buf);
error:
virBufferFreeAndReset(&buf);
return NULL;
}
static int
qemuBuildHostdevCommandLine(virCommandPtr cmd,
const virDomainDef *def,
virQEMUCapsPtr qemuCaps,
unsigned int *bootHostdevNet)
{
size_t i;
for (i = 0; i < def->nhostdevs; i++) {
virDomainHostdevDefPtr hostdev = def->hostdevs[i];
virDomainHostdevSubsysPtr subsys = &hostdev->source.subsys;
char *devstr;
if (hostdev->info->bootIndex) {
if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS ||
(subsys->type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI &&
subsys->type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB &&
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"));
return -1;
} else {
if (subsys->type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI) {
if (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"));
return -1;
}
} 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"));
return -1;
}
}
}
if (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"));
return -1;
}
if (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"));
return -1;
}
}
}
/* USB */
if (hostdev->mode == VIR_DOMAIN_HOSTDEV_MODE_SUBSYS &&
subsys->type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB) {
virCommandAddArg(cmd, "-device");
if (!(devstr =
qemuBuildUSBHostdevDevStr(def, hostdev, qemuCaps)))
return -1;
virCommandAddArg(cmd, devstr);
VIR_FREE(devstr);
}
/* PCI */
if (hostdev->mode == VIR_DOMAIN_HOSTDEV_MODE_SUBSYS &&
subsys->type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI) {
int backend = 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"));
return -1;
}
}
char *configfd_name = NULL;
unsigned int bootIndex = hostdev->info->bootIndex;
/* bootNet will be non-0 if boot order was set and no other
* net devices were encountered
*/
if (hostdev->parent.type == VIR_DOMAIN_DEVICE_NET &&
bootIndex == 0) {
bootIndex = *bootHostdevNet;
*bootHostdevNet = 0;
}
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);
return -1;
}
virCommandPassFD(cmd, configfd,
VIR_COMMAND_PASS_FD_CLOSE_PARENT);
}
}
virCommandAddArg(cmd, "-device");
devstr = qemuBuildPCIHostdevDevStr(def, hostdev, bootIndex,
configfd_name, qemuCaps);
VIR_FREE(configfd_name);
if (!devstr)
return -1;
virCommandAddArg(cmd, devstr);
VIR_FREE(devstr);
}
/* SCSI */
if (hostdev->mode == VIR_DOMAIN_HOSTDEV_MODE_SUBSYS &&
subsys->type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI) {
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_SCSI_GENERIC)) {
char *drvstr;
virCommandAddArg(cmd, "-drive");
if (!(drvstr = qemuBuildSCSIHostdevDrvStr(hostdev)))
return -1;
virCommandAddArg(cmd, drvstr);
VIR_FREE(drvstr);
virCommandAddArg(cmd, "-device");
if (!(devstr = qemuBuildSCSIHostdevDevStr(def, hostdev,
qemuCaps)))
return -1;
virCommandAddArg(cmd, devstr);
VIR_FREE(devstr);
} else {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("SCSI passthrough is not supported by this version of qemu"));
return -1;
}
}
}
return 0;
}
static char *
qemuBuildChrArgStr(const virDomainChrSourceDef *dev,
const char *prefix)
{
virBuffer buf = VIR_BUFFER_INITIALIZER;
if (dev->logfile) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("logfile not supported in this QEMU binary"));
goto error;
}
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 int
qemuBuildMonitorCommandLine(virLogManagerPtr logManager,
virCommandPtr cmd,
virQEMUDriverConfigPtr cfg,
virDomainDefPtr def,
virQEMUCapsPtr qemuCaps,
const virDomainChrSourceDef *monitor_chr,
bool monitor_json)
{
char *chrdev;
if (!monitor_chr)
return 0;
/* Use -chardev if it's available */
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_CHARDEV)) {
if (!(chrdev = qemuBuildChrChardevStr(logManager, cmd, cfg, def,
monitor_chr, "monitor",
qemuCaps)))
return -1;
virCommandAddArg(cmd, "-chardev");
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)))
return -1;
virCommandAddArg(cmd, chrdev);
VIR_FREE(chrdev);
}
return 0;
}
static char *
qemuBuildVirtioSerialPortDevStr(const virDomainDef *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(virLogManagerPtr logManager,
virCommandPtr cmd,
virQEMUDriverConfigPtr cfg,
const virDomainDef *def,
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(logManager, cmd, cfg, def,
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 = virQEMUBuildObjectCommandlineFromJSON(type, alias, props);
cleanup:
VIR_FREE(alias);
virJSONValueFree(props);
return ret;
}
char *
qemuBuildRNGDevStr(const virDomainDef *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 int
qemuBuildRNGCommandLine(virLogManagerPtr logManager,
virCommandPtr cmd,
virQEMUDriverConfigPtr cfg,
const virDomainDef *def,
virQEMUCapsPtr qemuCaps)
{
size_t i;
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"));
return -1;
}
/* possibly add character device for backend */
if (qemuBuildRNGBackendChrdevStr(logManager, cmd, cfg, def,
rng, qemuCaps, &tmp) < 0)
return -1;
if (tmp) {
virCommandAddArgList(cmd, "-chardev", tmp, NULL);
VIR_FREE(tmp);
}
/* add the RNG source backend */
if (!(tmp = qemuBuildRNGBackendStr(rng, qemuCaps)))
return -1;
virCommandAddArgList(cmd, "-object", tmp, NULL);
VIR_FREE(tmp);
/* add the device */
if (!(tmp = qemuBuildRNGDevStr(def, rng, qemuCaps)))
return -1;
virCommandAddArgList(cmd, "-device", tmp, NULL);
VIR_FREE(tmp);
}
return 0;
}
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 int
qemuBuildSmbiosCommandLine(virCommandPtr cmd,
virQEMUDriverPtr driver,
const virDomainDef *def,
virQEMUCapsPtr qemuCaps)
{
size_t i;
virSysinfoDefPtr source = NULL;
bool skip_uuid = false;
if (def->os.smbios_mode == VIR_DOMAIN_SMBIOS_NONE ||
def->os.smbios_mode == VIR_DOMAIN_SMBIOS_EMULATE)
return 0;
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_SMBIOS_TYPE)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("the QEMU binary %s does not support smbios settings"),
def->emulator);
return -1;
}
/* 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"));
return -1;
}
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);
return -1;
}
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"));
return -1;
}
for (i = 0; i < source->nbaseBoard; i++) {
if (!(smbioscmd =
qemuBuildSmbiosBaseBoardStr(source->baseBoard + i)))
return -1;
virCommandAddArgList(cmd, "-smbios", smbioscmd, NULL);
VIR_FREE(smbioscmd);
}
}
return 0;
}
static int
qemuBuildSgaCommandLine(virCommandPtr cmd,
const virDomainDef *def,
virQEMUCapsPtr qemuCaps)
{
/* Serial graphics adapter */
if (def->os.bios.useserial == VIR_TRISTATE_BOOL_YES) {
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_SGA)) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("qemu does not support SGA"));
return -1;
}
if (!def->nserials) {
virReportError(VIR_ERR_XML_ERROR, "%s",
_("need at least one serial port to use SGA"));
return -1;
}
virCommandAddArgList(cmd, "-device", "sga", NULL);
}
return 0;
}
static char *
qemuBuildClockArgStr(virDomainClockDefPtr def)
{
size_t i;
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= */
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;
}
/* NOTE: Building of commands can change def->clock->data.* values, so
* virDomainDef is not const here.
*/
static int
qemuBuildClockCommandLine(virCommandPtr cmd,
virDomainDefPtr def,
virQEMUCapsPtr qemuCaps)
{
size_t i;
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_RTC)) {
char *rtcopt;
virCommandAddArg(cmd, "-rtc");
if (!(rtcopt = qemuBuildClockArgStr(&def->clock)))
return -1;
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));
return -1;
}
}
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));
return -1;
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));
return -1;
}
} 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));
return -1;
}
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));
return -1;
}
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));
return -1;
}
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"));
return -1;
}
}
break;
}
}
return 0;
}
static int
qemuBuildPMCommandLine(virCommandPtr cmd,
const virDomainDef *def,
virQEMUCapsPtr qemuCaps,
bool monitor_json)
{
bool allowReboot = true;
/* 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");
}
/* We fall back to PIIX4_PM even for q35, since it's what we did
pre-q35-pm support. QEMU starts up fine (with a warning) if
mixing PIIX PM and -M q35. Starting to reject things here
could mean we refuse to start existing configs in the wild.*/
if (def->pm.s3) {
const char *pm_object = "PIIX4_PM";
if (qemuDomainMachineIsQ35(def) &&
virQEMUCapsGet(qemuCaps, QEMU_CAPS_ICH9_DISABLE_S3)) {
pm_object = "ICH9-LPC";
} else if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_PIIX_DISABLE_S3)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
"%s", _("setting ACPI S3 not supported"));
return -1;
}
virCommandAddArg(cmd, "-global");
virCommandAddArgFormat(cmd, "%s.disable_s3=%d",
pm_object, def->pm.s3 == VIR_TRISTATE_BOOL_NO);
}
if (def->pm.s4) {
const char *pm_object = "PIIX4_PM";
if (qemuDomainMachineIsQ35(def) &&
virQEMUCapsGet(qemuCaps, QEMU_CAPS_ICH9_DISABLE_S4)) {
pm_object = "ICH9-LPC";
} else if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_PIIX_DISABLE_S4)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
"%s", _("setting ACPI S4 not supported"));
return -1;
}
virCommandAddArg(cmd, "-global");
virCommandAddArgFormat(cmd, "%s.disable_s4=%d",
pm_object, def->pm.s4 == VIR_TRISTATE_BOOL_NO);
}
return 0;
}
static int
qemuBuildBootCommandLine(virCommandPtr cmd,
const virDomainDef *def,
virQEMUCapsPtr qemuCaps)
{
size_t i;
virBuffer boot_buf = VIR_BUFFER_INITIALIZER;
char *boot_order_str = NULL, *boot_opts_str = NULL;
/*
* We prefer using explicit bootindex=N parameters for predictable
* results even though domain XML doesn't use per device boot elements.
*/
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_BOOTINDEX)) {
char boot[VIR_DOMAIN_BOOT_LAST+1];
if (def->os.nBootDevs == 0) {
/* def->os.nBootDevs is guaranteed to be > 0 unless per-device boot
* configuration is used
*/
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("hypervisor lacks deviceboot feature"));
goto error;
}
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;
}
}
if (def->os.slic_table) {
virBuffer buf = VIR_BUFFER_INITIALIZER;
virCommandAddArg(cmd, "-acpitable");
virBufferAddLit(&buf, "sig=SLIC,file=");
virQEMUBuildBufferEscapeComma(&buf, def->os.slic_table);
virCommandAddArgBuffer(cmd, &buf);
}
return 0;
error:
VIR_FREE(boot_order_str);
VIR_FREE(boot_opts_str);
virBufferFreeAndReset(&boot_buf);
return -1;
}
static int
qemuBuildIOMMUCommandLine(virCommandPtr cmd,
const virDomainDef *def,
virQEMUCapsPtr qemuCaps)
{
if (!def->iommu)
return 0;
switch (def->iommu->model) {
case VIR_DOMAIN_IOMMU_MODEL_INTEL:
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_INTEL_IOMMU)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("IOMMU device: '%s' is not supported with "
"this QEMU binary"),
virDomainIOMMUModelTypeToString(def->iommu->model));
return -1;
}
if (!qemuDomainMachineIsQ35(def)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("IOMMU device: '%s' is only supported with "
"Q35 machines"),
virDomainIOMMUModelTypeToString(def->iommu->model));
return -1;
}
virCommandAddArgList(cmd, "-device", "intel-iommu", NULL);
case VIR_DOMAIN_IOMMU_MODEL_LAST:
break;
}
return 0;
}
static int
qemuBuildGlobalControllerCommandLine(virCommandPtr cmd,
const virDomainDef *def,
virQEMUCapsPtr qemuCaps)
{
size_t i;
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"));
return -1;
}
if (!machine) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("Setting the 64-bit PCI hole size is not "
"supported for machine '%s'"), def->os.machine);
return -1;
}
if (!cap) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("64-bit PCI hole size setting is not supported "
"with this QEMU binary"));
return -1;
}
virCommandAddArg(cmd, "-global");
virCommandAddArgFormat(cmd, "%s.pci-hole64-size=%luK", hoststr,
cont->opts.pciopts.pcihole64size);
}
}
return 0;
}
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))) {
if (def->virtType != VIR_DOMAIN_VIRT_KVM) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("CPU mode '%s' is only supported with kvm"),
virCPUModeTypeToString(cpu->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
qemuBuildCpuCommandLine(virCommandPtr cmd,
virQEMUDriverPtr driver,
const virDomainDef *def,
virQEMUCapsPtr qemuCaps,
bool migrating)
{
virArch hostarch = virArchFromHost();
char *cpu = NULL;
bool hasHwVirt = false;
const char *default_model;
bool have_cpu = false;
int ret = -1;
virBuffer buf = VIR_BUFFER_INITIALIZER;
size_t i;
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(def->emulator, "kvm")) ||
strstr(def->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:
case VIR_DOMAIN_HYPERV_VPINDEX:
case VIR_DOMAIN_HYPERV_RUNTIME:
case VIR_DOMAIN_HYPERV_SYNIC:
case VIR_DOMAIN_HYPERV_STIMER:
case VIR_DOMAIN_HYPERV_RESET:
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;
case VIR_DOMAIN_HYPERV_VENDOR_ID:
if (def->hyperv_features[i] == VIR_TRISTATE_SWITCH_ON)
virBufferAsprintf(&buf, ",hv_vendor_id=%s",
def->hyperv_vendor_id);
break;
/* coverity[dead_error_begin] */
case VIR_DOMAIN_HYPERV_LAST:
break;
}
}
}
for (i = 0; i < def->npanics; i++) {
if (def->panics[i]->model == VIR_DOMAIN_PANIC_MODEL_HYPERV) {
if (!have_cpu) {
virBufferAdd(&buf, default_model, -1);
have_cpu = true;
}
virBufferAddLit(&buf, ",hv_crash");
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 (virBufferCheckError(&buf) < 0)
goto cleanup;
cpu = virBufferContentAndReset(&buf);
if (cpu) {
virCommandAddArgList(cmd, "-cpu", cpu, NULL);
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_NESTING) && hasHwVirt)
virCommandAddArg(cmd, "-enable-nesting");
}
ret = 0;
cleanup:
VIR_FREE(cpu);
virBufferFreeAndReset(&buf);
return ret;
}
static int
qemuBuildObsoleteAccelArg(virCommandPtr cmd,
const virDomainDef *def,
virQEMUCapsPtr qemuCaps)
{
bool disableKVM = false;
bool enableKVM = false;
switch (def->virtType) {
case VIR_DOMAIN_VIRT_QEMU:
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_KVM))
disableKVM = true;
break;
case VIR_DOMAIN_VIRT_KQEMU:
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("the QEMU binary does not support kqemu"));
break;
case VIR_DOMAIN_VIRT_KVM:
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 (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
qemuBuildNameCommandLine(virCommandPtr cmd,
virQEMUDriverConfigPtr cfg,
const virDomainDef *def,
virQEMUCapsPtr qemuCaps)
{
virBuffer buf = VIR_BUFFER_INITIALIZER;
virCommandAddArg(cmd, "-name");
/* The 'guest' option let's us handle a name with '=' embedded in it */
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_NAME_GUEST))
virBufferAddLit(&buf, "guest=");
virQEMUBuildBufferEscapeComma(&buf, def->name);
if (cfg->setProcessName &&
virQEMUCapsGet(qemuCaps, QEMU_CAPS_NAME_PROCESS))
virBufferAsprintf(&buf, ",process=qemu:%s", def->name);
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_NAME_DEBUG_THREADS))
virBufferAddLit(&buf, ",debug-threads=on");
virCommandAddArgBuffer(cmd, &buf);
return 0;
}
static int
qemuBuildMachineCommandLine(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;
}
if (def->features[VIR_DOMAIN_FEATURE_GIC] == VIR_TRISTATE_SWITCH_ON) {
if (def->gic_version != VIR_GIC_VERSION_NONE) {
if (!qemuDomainMachineIsVirt(def)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("gic-version option is available "
"only for ARM virt machine"));
virBufferFreeAndReset(&buf);
return -1;
}
/* The default GIC version should not be specified on the
* QEMU commandline for backwards compatibility reasons */
if (def->gic_version != VIR_GIC_VERSION_DEFAULT) {
if (!virQEMUCapsGet(qemuCaps,
QEMU_CAPS_MACH_VIRT_GIC_VERSION)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("gic-version option is not available "
"with this QEMU binary"));
virBufferFreeAndReset(&buf);
return -1;
}
virBufferAsprintf(&buf, ",gic-version=%s",
virGICVersionTypeToString(def->gic_version));
}
}
}
virCommandAddArgBuffer(cmd, &buf);
}
if (obsoleteAccel &&
qemuBuildObsoleteAccelArg(cmd, def, qemuCaps) < 0)
return -1;
return 0;
}
static int
qemuBuildSmpCommandLine(virCommandPtr cmd,
const virDomainDef *def)
{
char *smp;
virBuffer buf = VIR_BUFFER_INITIALIZER;
virCommandAddArg(cmd, "-smp");
virBufferAsprintf(&buf, "%u", virDomainDefGetVcpus(def));
if (virDomainDefHasVcpusOffline(def))
virBufferAsprintf(&buf, ",maxcpus=%u", virDomainDefGetVcpusMax(def));
/* 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", virDomainDefGetVcpusMax(def));
virBufferAsprintf(&buf, ",cores=%u", 1);
virBufferAsprintf(&buf, ",threads=%u", 1);
}
if (virBufferCheckError(&buf) < 0)
return -1;
smp = virBufferContentAndReset(&buf);
virCommandAddArg(cmd, smp);
VIR_FREE(smp);
return 0;
}
static int
qemuBuildMemPathStr(virQEMUDriverConfigPtr cfg,
const virDomainDef *def,
virQEMUCapsPtr qemuCaps,
virCommandPtr cmd)
{
const long system_page_size = virGetSystemPageSizeKB();
char *mem_path = NULL;
size_t i = 0;
/*
* No-op if hugepages were not requested.
*/
if (!def->mem.nhugepages)
return 0;
/* There is one special case: if user specified "huge"
* pages of regular system pages size.
* And there is nothing to do in this case.
*/
if (def->mem.hugepages[0].size == system_page_size)
return 0;
if (!cfg->nhugetlbfs) {
virReportError(VIR_ERR_INTERNAL_ERROR,
"%s", _("hugetlbfs filesystem is not mounted "
"or disabled by administrator config"));
return -1;
}
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_MEM_PATH)) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("hugepage backing not supported by '%s'"),
def->emulator);
return -1;
}
if (!def->mem.hugepages[0].size) {
if (!(mem_path = qemuGetDefaultHugepath(cfg->hugetlbfs,
cfg->nhugetlbfs)))
return -1;
} else {
for (i = 0; i < cfg->nhugetlbfs; i++) {
if (cfg->hugetlbfs[i].size == def->mem.hugepages[0].size)
break;
}
if (i == cfg->nhugetlbfs) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Unable to find any usable hugetlbfs "
"mount for %llu KiB"),
def->mem.hugepages[0].size);
return -1;
}
if (!(mem_path = qemuGetHugepagePath(&cfg->hugetlbfs[i])))
return -1;
}
virCommandAddArgList(cmd, "-mem-prealloc", "-mem-path", mem_path, NULL);
VIR_FREE(mem_path);
return 0;
}
static int
qemuBuildMemCommandLine(virCommandPtr cmd,
virQEMUDriverConfigPtr cfg,
const virDomainDef *def,
virQEMUCapsPtr qemuCaps)
{
if (qemuDomainDefValidateMemoryHotplug(def, qemuCaps, NULL) < 0)
return -1;
virCommandAddArg(cmd, "-m");
if (virDomainDefHasMemoryHotplug(def)) {
/* 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);
}
/*
* Add '-mem-path' (and '-mem-prealloc') parameter here only if
* there is no numa node specified.
*/
if (!virDomainNumaGetNodeCount(def->numa) &&
qemuBuildMemPathStr(cfg, def, qemuCaps, cmd) < 0)
return -1;
if (def->mem.locked && !virQEMUCapsGet(qemuCaps, QEMU_CAPS_REALTIME_MLOCK)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("memory locking not supported by QEMU binary"));
return -1;
}
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_REALTIME_MLOCK)) {
virCommandAddArg(cmd, "-realtime");
virCommandAddArgFormat(cmd, "mlock=%s",
def->mem.locked ? "on" : "off");
}
return 0;
}
static int
qemuBuildIOThreadCommandLine(virCommandPtr cmd,
const virDomainDef *def,
virQEMUCapsPtr qemuCaps)
{
size_t i;
if (def->niothreadids == 0)
return 0;
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_OBJECT_IOTHREAD)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("IOThreads not supported for this QEMU"));
return -1;
}
/* 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);
}
return 0;
}
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;
}
}
}
if (!needBackend &&
qemuBuildMemPathStr(cfg, def, qemuCaps, cmd) < 0)
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
qemuBuildMemoryDeviceCommandLine(virCommandPtr cmd,
virQEMUDriverConfigPtr cfg,
virDomainDefPtr def,
virQEMUCapsPtr qemuCaps,
virBitmapPtr nodeset)
{
size_t i;
/* memory hotplug requires NUMA to be enabled - we already checked
* that memory devices are present only when NUMA is */
for (i = 0; i < def->nmems; i++) {
char *backStr;
char *dimmStr;
if (!(backStr = qemuBuildMemoryDimmBackendStr(def->mems[i], def,
qemuCaps, cfg, nodeset)))
return -1;
if (!(dimmStr = qemuBuildMemoryDeviceStr(def->mems[i]))) {
VIR_FREE(backStr);
return -1;
}
virCommandAddArgList(cmd, "-object", backStr, "-device", dimmStr, NULL);
VIR_FREE(backStr);
VIR_FREE(dimmStr);
}
return 0;
}
static int
qemuBuildGraphicsVNCCommandLine(virQEMUDriverConfigPtr cfg,
virCommandPtr cmd,
virQEMUCapsPtr qemuCaps,
virDomainGraphicsDefPtr graphics)
{
virBuffer opt = VIR_BUFFER_INITIALIZER;
virDomainGraphicsListenDefPtr glisten = NULL;
bool escapeAddr;
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_VNC)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("vnc graphics are not supported with this QEMU"));
goto error;
}
if (!(glisten = virDomainGraphicsGetListen(graphics, 0))) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("missing listen element"));
goto error;
}
switch (glisten->type) {
case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_SOCKET:
virBufferAddLit(&opt, "unix:");
virQEMUBuildBufferEscapeComma(&opt, glisten->socket);
break;
case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_ADDRESS:
case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_NETWORK:
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 (glisten->address) {
escapeAddr = strchr(glisten->address, ':') != NULL;
if (escapeAddr)
virBufferAsprintf(&opt, "[%s]", glisten->address);
else
virBufferAdd(&opt, glisten->address, -1);
}
virBufferAsprintf(&opt, ":%d",
graphics->data.vnc.port - 5900);
if (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);
}
break;
case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_NONE:
virBufferAddLit(&opt, "none");
break;
case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_LAST:
break;
}
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:
virBufferFreeAndReset(&opt);
return -1;
}
static int
qemuBuildGraphicsSPICECommandLine(virQEMUDriverConfigPtr cfg,
virCommandPtr cmd,
virQEMUCapsPtr qemuCaps,
virDomainGraphicsDefPtr graphics)
{
virBuffer opt = VIR_BUFFER_INITIALIZER;
virDomainGraphicsListenDefPtr glisten = NULL;
int port = graphics->data.spice.port;
int tlsPort = graphics->data.spice.tlsPort;
size_t i;
bool hasSecure = false;
bool hasInsecure = false;
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_SPICE)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("spice graphics are not supported with this QEMU"));
goto error;
}
if (!(glisten = virDomainGraphicsGetListen(graphics, 0))) {
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("missing listen element"));
goto error;
}
switch (glisten->type) {
case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_SOCKET:
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_SPICE_UNIX)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("unix socket for spice graphics are not supported "
"with this QEMU"));
goto error;
}
virBufferAsprintf(&opt, "unix,addr=%s,", glisten->socket);
hasInsecure = true;
break;
case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_ADDRESS:
case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_NETWORK:
if (port > 0) {
virBufferAsprintf(&opt, "port=%u,", port);
hasInsecure = true;
}
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;
}
virBufferAsprintf(&opt, "tls-port=%u,", tlsPort);
hasSecure = true;
}
if (port > 0 || tlsPort > 0) {
if (glisten->address)
virBufferAsprintf(&opt, "addr=%s,", glisten->address);
}
break;
case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_NONE:
/* QEMU requires either port or tls-port to be specified if there is no
* other argument. Use a dummy port=0. */
virBufferAddLit(&opt, "port=0,");
hasInsecure = true;
break;
case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_LAST:
break;
}
if (cfg->spiceSASL) {
virBufferAddLit(&opt, "sasl,");
if (cfg->spiceSASLdir)
virCommandAddEnvPair(cmd, "SASL_CONF_PATH",
cfg->spiceSASLdir);
/* TODO: Support ACLs later */
}
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 (hasSecure)
virBufferAsprintf(&opt, "x509-dir=%s,", cfg->spiceTLSx509certdir);
switch (graphics->data.spice.defaultMode) {
case VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_MODE_SECURE:
if (!hasSecure) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("spice defaultMode secure requested in XML "
"configuration, but TLS connection is not "
"available"));
goto error;
}
virBufferAddLit(&opt, "tls-channel=default,");
break;
case VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_MODE_INSECURE:
if (!hasInsecure) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("spice defaultMode insecure requested in XML "
"configuration, but plaintext connection is not "
"available"));
goto error;
}
virBufferAddLit(&opt, "plaintext-channel=default,");
break;
case VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_MODE_ANY:
case VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_MODE_LAST:
/* 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 (!hasSecure) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("spice secure channels set in XML "
"configuration, but TLS connection is not "
"available"));
goto error;
}
virBufferAsprintf(&opt, "tls-channel=%s,",
virDomainGraphicsSpiceChannelNameTypeToString(i));
break;
case VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_MODE_INSECURE:
if (!hasInsecure) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("spice insecure channels set in XML "
"configuration, but plaintext connection "
"is not available"));
goto error;
}
virBufferAsprintf(&opt, "plaintext-channel=%s,",
virDomainGraphicsSpiceChannelNameTypeToString(i));
break;
case VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_MODE_ANY:
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 (graphics->data.spice.gl == VIR_TRISTATE_BOOL_YES) {
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_SPICE_GL)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("This QEMU doesn't support spice OpenGL"));
goto error;
}
/* spice.gl is a TristateBool, but qemu expects on/off: use
* TristateSwitch helper */
virBufferAsprintf(&opt, "gl=%s,",
virTristateSwitchTypeToString(graphics->data.spice.gl));
}
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,");
}
virBufferTrim(&opt, ",", -1);
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:
virBufferFreeAndReset(&opt);
return -1;
}
static int
qemuBuildGraphicsCommandLine(virQEMUDriverConfigPtr cfg,
virCommandPtr cmd,
virDomainDefPtr def,
virQEMUCapsPtr qemuCaps,
virDomainGraphicsDefPtr graphics)
{
switch (graphics->type) {
case VIR_DOMAIN_GRAPHICS_TYPE_SDL:
if (!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 :-( */
virCommandAddArg(cmd, "-sdl");
break;
case VIR_DOMAIN_GRAPHICS_TYPE_VNC:
return qemuBuildGraphicsVNCCommandLine(cfg, cmd, 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,
unsigned 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,
unsigned 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 ||
actualType == VIR_DOMAIN_NET_TYPE_DIRECT ||
actualType == VIR_DOMAIN_NET_TYPE_ETHERNET)) {
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 ||
actualType == VIR_DOMAIN_NET_TYPE_ETHERNET)) {
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 ||
actualType == VIR_DOMAIN_NET_TYPE_ETHERNET)) {
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 (qemuInterfaceBridgeConnect(def, driver, net,
tapfd, &tapfdSize) < 0)
goto cleanup;
} else if (actualType == VIR_DOMAIN_NET_TYPE_DIRECT) {
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 (qemuInterfaceDirectConnect(def, driver, net,
tapfd, tapfdSize, vmop) < 0)
goto cleanup;
} else if (actualType == VIR_DOMAIN_NET_TYPE_ETHERNET) {
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 (qemuInterfaceEthernetConnect(def, driver, net,
tapfd, tapfdSize) < 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 (qemuInterfaceOpenVhostNet(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, 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;
}
/* NOTE: Not using const virDomainDef here since eventually a call is made
* into virSecurityManagerSetTapFDLabel which calls it's driver
* API domainSetSecurityTapFDLabel that doesn't use the const format.
*/
static int
qemuBuildNetCommandLine(virCommandPtr cmd,
virQEMUDriverPtr driver,
virDomainDefPtr def,
virQEMUCapsPtr qemuCaps,
virNetDevVPortProfileOp vmop,
bool standalone,
size_t *nnicindexes,
int **nicindexes,
unsigned int *bootHostdevNet)
{
size_t i;
int last_good_net = -1;
virErrorPtr originalError = NULL;
if (def->nnets) {
unsigned int bootNet = 0;
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_BOOTINDEX)) {
/* 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;
/* if this interface is a type='hostdev' interface and we
* haven't yet added a "bootindex" parameter to an
* emulated network device, save the bootindex - hostdev
* interface commandlines will be built later on when we
* cycle through all the hostdevs, and we'll use it then.
*/
if (virDomainNetGetActualType(net) == VIR_DOMAIN_NET_TYPE_HOSTDEV &&
*bootHostdevNet == 0) {
*bootHostdevNet = bootNet;
}
bootNet = 0;
}
}
return 0;
error:
/* 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);
return -1;
}
static int
qemuBuildSmartcardCommandLine(virLogManagerPtr logManager,
virCommandPtr cmd,
virQEMUDriverConfigPtr cfg,
const virDomainDef *def,
virQEMUCapsPtr qemuCaps)
{
size_t i;
virDomainSmartcardDefPtr smartcard;
char *devstr;
virBuffer opt = VIR_BUFFER_INITIALIZER;
const char *database;
if (!def->nsmartcards)
return 0;
smartcard = def->smartcards[0];
/* -device usb-ccid was already emitted along with other
* controllers. For now, qemu handles only one smartcard. */
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);
return -1;
}
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"));
return -1;
}
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"));
return -1;
}
virBufferAddLit(&opt, "ccid-card-emulated,backend=certificates");
for (i = 0; i < VIR_DOMAIN_SMARTCARD_NUM_CERTIFICATES; i++) {
if (strchr(smartcard->data.cert.file[i], ',')) {
virBufferFreeAndReset(&opt);
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("invalid certificate name: %s"),
smartcard->data.cert.file[i]);
return -1;
}
virBufferAsprintf(&opt, ",cert%zu=%s", i + 1,
smartcard->data.cert.file[i]);
}
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);
return -1;
}
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"));
return -1;
}
if (!(devstr = qemuBuildChrChardevStr(logManager, cmd, cfg, def,
&smartcard->data.passthru,
smartcard->info.alias,
qemuCaps))) {
virBufferFreeAndReset(&opt);
return -1;
}
virCommandAddArg(cmd, "-chardev");
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);
return -1;
}
virCommandAddArg(cmd, "-device");
virBufferAsprintf(&opt, ",id=%s,bus=ccid0.0", smartcard->info.alias);
virCommandAddArgBuffer(cmd, &opt);
return 0;
}
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;
}
static char *
qemuBuildShmemBackendStr(virLogManagerPtr logManager,
virCommandPtr cmd,
virQEMUDriverConfigPtr cfg,
virDomainDefPtr def,
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(logManager, cmd, cfg, def,
&shmem->server.chr,
shmem->info.alias, qemuCaps);
return devstr;
}
static int
qemuBuildShmemCommandLine(virLogManagerPtr logManager,
virCommandPtr cmd,
virQEMUDriverConfigPtr cfg,
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(logManager, cmd, cfg, def,
shmem, qemuCaps)))
return -1;
virCommandAddArgList(cmd, "-chardev", devstr, NULL);
VIR_FREE(devstr);
}
return 0;
}
static int
qemuBuildChrDeviceCommandLine(virCommandPtr cmd,
const virDomainDef *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
qemuBuildSerialCommandLine(virLogManagerPtr logManager,
virCommandPtr cmd,
virQEMUDriverConfigPtr cfg,
const virDomainDef *def,
virQEMUCapsPtr qemuCaps)
{
size_t i;
bool havespice = false;
if (def->nserials) {
for (i = 0; i < def->ngraphics && !havespice; i++) {
if (def->graphics[i]->type == VIR_DOMAIN_GRAPHICS_TYPE_SPICE)
havespice = true;
}
}
for (i = 0; i < def->nserials; i++) {
virDomainChrDefPtr serial = def->serials[i];
char *devstr;
if (serial->source.type == VIR_DOMAIN_CHR_TYPE_SPICEPORT && !havespice)
continue;
/* Use -chardev with -device if they are available */
if (virQEMUCapsSupportsChardev(def, qemuCaps, serial)) {
if (!(devstr = qemuBuildChrChardevStr(logManager, cmd, cfg, def,
&serial->source,
serial->info.alias,
qemuCaps)))
return -1;
virCommandAddArg(cmd, "-chardev");
virCommandAddArg(cmd, devstr);
VIR_FREE(devstr);
if (qemuBuildChrDeviceCommandLine(cmd, def, serial, qemuCaps) < 0)
return -1;
} else {
virCommandAddArg(cmd, "-serial");
if (!(devstr = qemuBuildChrArgStr(&serial->source, NULL)))
return -1;
virCommandAddArg(cmd, devstr);
VIR_FREE(devstr);
}
}
return 0;
}
static int
qemuBuildParallelsCommandLine(virLogManagerPtr logManager,
virCommandPtr cmd,
virQEMUDriverConfigPtr cfg,
const virDomainDef *def,
virQEMUCapsPtr qemuCaps)
{
size_t i;
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)) {
if (!(devstr = qemuBuildChrChardevStr(logManager, cmd, cfg, def,
¶llel->source,
parallel->info.alias,
qemuCaps)))
return -1;
virCommandAddArg(cmd, "-chardev");
virCommandAddArg(cmd, devstr);
VIR_FREE(devstr);
if (qemuBuildChrDeviceCommandLine(cmd, def, parallel,
qemuCaps) < 0)
return -1;
} else {
virCommandAddArg(cmd, "-parallel");
if (!(devstr = qemuBuildChrArgStr(¶llel->source, NULL)))
return -1;
virCommandAddArg(cmd, devstr);
VIR_FREE(devstr);
}
}
return 0;
}
static int
qemuBuildChannelsCommandLine(virLogManagerPtr logManager,
virCommandPtr cmd,
virQEMUDriverConfigPtr cfg,
const virDomainDef *def,
virQEMUCapsPtr qemuCaps)
{
size_t i;
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)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
"%s", _("guestfwd requires QEMU to support -chardev & -device"));
return -1;
}
if (!(devstr = qemuBuildChrChardevStr(logManager, cmd, cfg, def,
&channel->source,
channel->info.alias,
qemuCaps)))
return -1;
virCommandAddArg(cmd, "-chardev");
virCommandAddArg(cmd, devstr);
VIR_FREE(devstr);
if (qemuBuildChrDeviceStr(&devstr, def, channel, qemuCaps) < 0)
return -1;
virCommandAddArgList(cmd, "-netdev", devstr, NULL);
VIR_FREE(devstr);
break;
case VIR_DOMAIN_CHR_CHANNEL_TARGET_TYPE_VIRTIO:
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 {
if (!(devstr = qemuBuildChrChardevStr(logManager, cmd, cfg, def,
&channel->source,
channel->info.alias,
qemuCaps)))
return -1;
virCommandAddArg(cmd, "-chardev");
virCommandAddArg(cmd, devstr);
VIR_FREE(devstr);
}
if (qemuBuildChrDeviceCommandLine(cmd, def, channel, qemuCaps) < 0)
return -1;
break;
}
}
return 0;
}
static int
qemuBuildConsoleCommandLine(virLogManagerPtr logManager,
virCommandPtr cmd,
virQEMUDriverConfigPtr cfg,
const virDomainDef *def,
virQEMUCapsPtr qemuCaps)
{
size_t i;
/* 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_SCLP_S390)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("sclp console requires QEMU to support s390-sclp"));
return -1;
}
if (!(devstr = qemuBuildChrChardevStr(logManager, cmd, cfg, def,
&console->source,
console->info.alias,
qemuCaps)))
return -1;
virCommandAddArg(cmd, "-chardev");
virCommandAddArg(cmd, devstr);
VIR_FREE(devstr);
if (qemuBuildChrDeviceCommandLine(cmd, def, console, qemuCaps) < 0)
return -1;
break;
case VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_VIRTIO:
if (!(devstr = qemuBuildChrChardevStr(logManager, cmd, cfg, def,
&console->source,
console->info.alias,
qemuCaps)))
return -1;
virCommandAddArg(cmd, "-chardev");
virCommandAddArg(cmd, devstr);
VIR_FREE(devstr);
if (qemuBuildChrDeviceCommandLine(cmd, def, console, qemuCaps) < 0)
return -1;
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)));
return -1;
}
}
return 0;
}
char *
qemuBuildRedirdevDevStr(const virDomainDef *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=%u", 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;
}
static int
qemuBuildRedirdevCommandLine(virLogManagerPtr logManager,
virCommandPtr cmd,
virQEMUDriverConfigPtr cfg,
const virDomainDef *def,
virQEMUCapsPtr qemuCaps)
{
size_t i;
for (i = 0; i < def->nredirdevs; i++) {
virDomainRedirdevDefPtr redirdev = def->redirdevs[i];
char *devstr;
if (!(devstr = qemuBuildChrChardevStr(logManager, cmd, cfg, def,
&redirdev->source.chr,
redirdev->info.alias,
qemuCaps))) {
return -1;
}
virCommandAddArg(cmd, "-chardev");
virCommandAddArg(cmd, devstr);
VIR_FREE(devstr);
virCommandAddArg(cmd, "-device");
if (!(devstr = qemuBuildRedirdevDevStr(def, redirdev, qemuCaps)))
return -1;
virCommandAddArg(cmd, devstr);
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_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) {
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 char *
qemuBuildTPMDevStr(const virDomainDef *def,
virQEMUCapsPtr qemuCaps)
{
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"),
def->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 *
qemuBuildTPMBackendStr(const virDomainDef *def,
virCommandPtr cmd,
virQEMUCapsPtr qemuCaps,
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=");
virQEMUBuildBufferEscapeComma(&buf, devset ? devset : tpmdev);
virBufferAddLit(&buf, ",cancel-path=");
virQEMUBuildBufferEscapeComma(&buf, 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"),
def->emulator, type);
error:
VIR_FREE(devset);
VIR_FREE(cancel_path);
virBufferFreeAndReset(&buf);
return NULL;
}
static int
qemuBuildTPMCommandLine(virCommandPtr cmd,
const virDomainDef *def,
virQEMUCapsPtr qemuCaps)
{
char *optstr;
int tpmfd = -1;
int cancelfd = -1;
char *fdset;
if (!def->tpm)
return 0;
if (!(optstr = qemuBuildTPMBackendStr(def, cmd, qemuCaps,
&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)))
return -1;
virCommandAddArgList(cmd, "-device", optstr, NULL);
VIR_FREE(optstr);
return 0;
}
static int
qemuBuildPanicCommandLine(virCommandPtr cmd,
const virDomainDef *def,
virQEMUCapsPtr qemuCaps)
{
size_t i;
for (i = 0; i < def->npanics; i++) {
switch ((virDomainPanicModel) def->panics[i]->model) {
case VIR_DOMAIN_PANIC_MODEL_S390:
/* For s390 guests, the hardware provides the same
* functionality as the pvpanic device. The address
* cannot be configured by the user */
if (!ARCH_IS_S390(def->os.arch)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("only S390 guests support "
"panic device of model 's390'"));
return -1;
}
if (def->panics[i]->info.type !=
VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("setting the panic device address is not "
"supported for model 's390'"));
return -1;
}
break;
case VIR_DOMAIN_PANIC_MODEL_HYPERV:
/* Panic with model 'hyperv' is not a device, it should
* be configured in cpu commandline. The address
* cannot be configured by the user */
if (!ARCH_IS_X86(def->os.arch)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("only i686 and x86_64 guests support "
"panic device of model 'hyperv'"));
return -1;
}
if (def->panics[i]->info.type !=
VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("setting the panic device address is not "
"supported for model 'hyperv'"));
return -1;
}
break;
case VIR_DOMAIN_PANIC_MODEL_PSERIES:
/* For pSeries guests, the firmware provides the same
* functionality as the pvpanic device. The address
* cannot be configured by the user */
if (!qemuDomainMachineIsPSeries(def)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("only pSeries guests support panic device "
"of model 'pseries'"));
return -1;
}
if (def->panics[i]->info.type !=
VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("setting the panic device address is not "
"supported for model 'pseries'"));
return -1;
}
break;
case VIR_DOMAIN_PANIC_MODEL_ISA:
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_PANIC)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("the QEMU binary does not support the "
"ISA panic device"));
return -1;
}
switch (def->panics[i]->info.type) {
case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_ISA:
virCommandAddArg(cmd, "-device");
virCommandAddArgFormat(cmd, "pvpanic,ioport=%d",
def->panics[i]->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"));
return -1;
}
/* default model value was changed before in post parse */
case VIR_DOMAIN_PANIC_MODEL_DEFAULT:
case VIR_DOMAIN_PANIC_MODEL_LAST:
break;
}
}
return 0;
}
/**
* qemuBuildCommandLineValidate:
*
* Prior to taking the plunge and building a long command line only
* to find some configuration option isn't valid, let's do a couple
* of checks and fail early.
*
* Returns 0 on success, returns -1 and messages what the issue is.
*/
static int
qemuBuildCommandLineValidate(virQEMUDriverPtr driver,
const virDomainDef *def)
{
size_t i;
int sdl = 0;
int vnc = 0;
int spice = 0;
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) ||
virMemoryLimitIsSet(def->mem.swap_hard_limit)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("Memory tuning is not available in session mode"));
return -1;
}
if (def->blkio.weight) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("Block I/O tuning is not available in session mode"));
return -1;
}
if (def->cputune.sharesSpecified || def->cputune.period ||
def->cputune.quota || def->cputune.global_period ||
def->cputune.global_quota || def->cputune.emulator_period ||
def->cputune.emulator_quota) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("CPU tuning is not available in session mode"));
return -1;
}
}
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;
case VIR_DOMAIN_GRAPHICS_TYPE_RDP:
case VIR_DOMAIN_GRAPHICS_TYPE_DESKTOP:
case VIR_DOMAIN_GRAPHICS_TYPE_LAST:
break;
}
}
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"));
return -1;
}
if (def->virtType == VIR_DOMAIN_VIRT_XEN ||
def->os.type == VIR_DOMAIN_OSTYPE_XEN ||
def->os.type == VIR_DOMAIN_OSTYPE_LINUX) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("qemu emulator '%s' does not support xen"),
def->emulator);
return -1;
}
for (i = 0; i < def->ndisks; i++) {
virDomainDiskDefPtr disk = def->disks[i];
if (disk->src->driverName != NULL &&
STRNEQ(disk->src->driverName, "qemu")) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("unsupported driver name '%s' for disk '%s'"),
disk->src->driverName, disk->src->path);
return -1;
}
}
return 0;
}
/*
* Constructs a argv suitable for launching qemu with config defined
* for a given virtual machine.
*/
virCommandPtr
qemuBuildCommandLine(virQEMUDriverPtr driver,
virLogManagerPtr logManager,
virDomainDefPtr def,
virDomainChrSourceDefPtr monitor_chr,
bool monitor_json,
virQEMUCapsPtr qemuCaps,
const char *migrateURI,
virDomainSnapshotObjPtr snapshot,
virNetDevVPortProfileOp vmop,
bool standalone,
bool enableFips,
virBitmapPtr nodeset,
size_t *nnicindexes,
int **nicindexes,
const char *domainLibDir)
{
size_t i;
char uuid[VIR_UUID_STRING_BUFLEN];
virCommandPtr cmd = NULL;
virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver);
unsigned int bootHostdevNet = 0;
VIR_DEBUG("driver=%p def=%p mon=%p json=%d "
"qemuCaps=%p migrateURI=%s snapshot=%p vmop=%d",
driver, def, monitor_chr, monitor_json,
qemuCaps, migrateURI, snapshot, vmop);
if (qemuBuildCommandLineValidate(driver, def) < 0)
goto error;
/*
* 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(def->emulator);
virCommandAddEnvPassCommon(cmd);
if (qemuBuildNameCommandLine(cmd, cfg, def, qemuCaps) < 0)
goto error;
if (!standalone)
virCommandAddArg(cmd, "-S"); /* freeze CPU */
if (qemuBuildMasterKeyCommandLine(cmd, qemuCaps, domainLibDir) < 0)
goto error;
if (enableFips)
virCommandAddArg(cmd, "-enable-fips");
if (qemuBuildMachineCommandLine(cmd, def, qemuCaps) < 0)
goto error;
if (qemuBuildCpuCommandLine(cmd, driver, def, qemuCaps, !!migrateURI) < 0)
goto error;
if (qemuBuildDomainLoaderCommandLine(cmd, def, qemuCaps) < 0)
goto error;
if (!migrateURI && !snapshot && qemuDomainAlignMemorySizes(def) < 0)
goto error;
if (qemuBuildMemCommandLine(cmd, cfg, def, qemuCaps) < 0)
goto error;
if (qemuBuildSmpCommandLine(cmd, def) < 0)
goto error;
if (qemuBuildIOThreadCommandLine(cmd, def, qemuCaps) < 0)
goto error;
if (virDomainNumaGetNodeCount(def->numa) &&
qemuBuildNumaArgStr(cfg, def, cmd, qemuCaps, nodeset) < 0)
goto error;
if (qemuBuildMemoryDeviceCommandLine(cmd, cfg, def, qemuCaps, nodeset) < 0)
goto error;
virUUIDFormat(def->uuid, uuid);
virCommandAddArgList(cmd, "-uuid", uuid, NULL);
if (qemuBuildSmbiosCommandLine(cmd, driver, def, qemuCaps) < 0)
goto error;
/*
* 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->ngraphics) {
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DISPLAY)) {
virCommandAddArg(cmd, "-display");
virCommandAddArg(cmd, "none");
} else {
virCommandAddArg(cmd, "-nographic");
}
if (cfg->nogfxAllowHostAudio)
virCommandAddEnvPassBlockSUID(cmd, "QEMU_AUDIO_DRV", NULL);
else
virCommandAddEnvString(cmd, "QEMU_AUDIO_DRV=none");
}
/* 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");
if (qemuBuildSgaCommandLine(cmd, def, qemuCaps) < 0)
goto error;
if (qemuBuildMonitorCommandLine(logManager, cmd, cfg, def,
qemuCaps, monitor_chr,
monitor_json) < 0)
goto error;
if (qemuBuildClockCommandLine(cmd, def, qemuCaps) < 0)
goto error;
if (qemuBuildPMCommandLine(cmd, def, qemuCaps, monitor_json) < 0)
goto error;
if (qemuBuildBootCommandLine(cmd, def, qemuCaps) < 0)
goto error;
if (qemuBuildIOMMUCommandLine(cmd, def, qemuCaps) < 0)
goto error;
if (qemuBuildGlobalControllerCommandLine(cmd, def, qemuCaps) < 0)
goto error;
if (qemuBuildControllerDevCommandLine(cmd, def, qemuCaps) < 0)
goto error;
if (qemuBuildHubCommandLine(cmd, def, qemuCaps) < 0)
goto error;
if (qemuBuildDiskDriveCommandLine(cmd, def, qemuCaps) < 0)
goto error;
if (qemuBuildFSDevCommandLine(cmd, def, qemuCaps) < 0)
goto error;
if (qemuBuildNetCommandLine(cmd, driver, def, qemuCaps, vmop, standalone,
nnicindexes, nicindexes, &bootHostdevNet) < 0)
goto error;
if (qemuBuildSmartcardCommandLine(logManager, cmd, cfg, def, qemuCaps) < 0)
goto error;
if (qemuBuildSerialCommandLine(logManager, cmd, cfg, def, qemuCaps) < 0)
goto error;
if (qemuBuildParallelsCommandLine(logManager, cmd, cfg, def, qemuCaps) < 0)
goto error;
if (qemuBuildChannelsCommandLine(logManager, cmd, cfg, def, qemuCaps) < 0)
goto error;
if (qemuBuildConsoleCommandLine(logManager, cmd, cfg, def, qemuCaps) < 0)
goto error;
if (qemuBuildTPMCommandLine(cmd, def, qemuCaps) < 0)
goto error;
if (qemuBuildInputCommandLine(cmd, def, qemuCaps) < 0)
goto error;
for (i = 0; i < def->ngraphics; ++i) {
if (qemuBuildGraphicsCommandLine(cfg, cmd, def, qemuCaps,
def->graphics[i]) < 0)
goto error;
}
if (qemuBuildVideoCommandLine(cmd, def, qemuCaps) < 0)
goto error;
if (qemuBuildSoundCommandLine(cmd, def, qemuCaps) < 0)
goto error;
if (qemuBuildWatchdogCommandLine(cmd, def, qemuCaps) < 0)
goto error;
if (qemuBuildRedirdevCommandLine(logManager, cmd, cfg, def, qemuCaps) < 0)
goto error;
if (qemuBuildHostdevCommandLine(cmd, def, qemuCaps, &bootHostdevNet) < 0)
goto error;
if (migrateURI)
virCommandAddArgList(cmd, "-incoming", migrateURI, NULL);
if (qemuBuildMemballoonCommandLine(cmd, def, qemuCaps) < 0)
goto error;
if (qemuBuildRNGCommandLine(logManager, cmd, cfg, def, qemuCaps) < 0)
goto error;
if (qemuBuildNVRAMCommandLine(cmd, def, qemuCaps) < 0)
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 (qemuBuildPanicCommandLine(cmd, def, qemuCaps) < 0)
goto error;
for (i = 0; i < def->nshmems; i++) {
if (qemuBuildShmemCommandLine(logManager, cmd, cfg,
def, def->shmems[i], qemuCaps))
goto error;
}
/* In some situations, eg. VFIO passthrough, QEMU might need to lock a
* significant amount of memory, so we need to set the limit accordingly */
if (qemuDomainRequiresMemLock(def))
virCommandSetMaxMemLock(cmd, qemuDomainGetMemLockLimitBytes(def));
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_MSG_TIMESTAMP) &&
cfg->logTimestamp)
virCommandAddArgList(cmd, "-msg", "timestamp=on", NULL);
virObjectUnref(cfg);
return cmd;
error:
virObjectUnref(cfg);
virCommandFree(cmd);
return NULL;
}
/* This function generates the correct '-device' string for character
* devices of each architecture.
*/
static int
qemuBuildSerialChrDeviceStr(char **deviceStr,
const virDomainDef *def,
virDomainChrDefPtr serial,
virQEMUCapsPtr qemuCaps)
{
virBuffer cmd = VIR_BUFFER_INITIALIZER;
if (qemuDomainMachineIsPSeries(def)) {
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,
const virDomainDef *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,
const virDomainDef *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,
const virDomainDef *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);
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;
}