mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2025-01-10 23:07:44 +00:00
96ecc3e208
All currently supported qemu versions support all throttling capabilities. It is unlikely that any of the fields will be removed in the future and if it will we will need to do specific probing which is possible via the 'throttle' object which is the replacement for the legacy way to configure throttling. Signed-off-by: Peter Krempa <pkrempa@redhat.com> Reviewed-by: Martin Kletzander <mkletzan@redhat.com> Reviewed-by: Ján Tomko <jtomko@redhat.com>
5111 lines
184 KiB
C
5111 lines
184 KiB
C
/*
|
|
* qemu_validate.c: QEMU general validation functions
|
|
*
|
|
* Copyright IBM Corp, 2020
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library. If not, see
|
|
* <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <config.h>
|
|
|
|
#include "qemu_validate.h"
|
|
#include "qemu_block.h"
|
|
#include "qemu_command.h"
|
|
#include "qemu_domain.h"
|
|
#include "domain_conf.h"
|
|
#include "virlog.h"
|
|
#include "virutil.h"
|
|
|
|
#define VIR_FROM_THIS VIR_FROM_QEMU
|
|
#define QEMU_MAX_VCPUS_WITHOUT_EIM 255
|
|
|
|
VIR_LOG_INIT("qemu.qemu_validate");
|
|
|
|
|
|
static int
|
|
qemuValidateDomainDefPSeriesFeature(const virDomainDef *def,
|
|
virQEMUCaps *qemuCaps,
|
|
int feature)
|
|
{
|
|
const char *str;
|
|
|
|
if (def->features[feature] != VIR_TRISTATE_SWITCH_ABSENT &&
|
|
!qemuDomainIsPSeries(def)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("The '%s' feature is not supported for "
|
|
"architecture '%s' or machine type '%s'"),
|
|
virDomainFeatureTypeToString(feature),
|
|
virArchToString(def->os.arch),
|
|
def->os.machine);
|
|
return -1;
|
|
}
|
|
|
|
if (def->features[feature] == VIR_TRISTATE_SWITCH_ABSENT)
|
|
return 0;
|
|
|
|
switch (feature) {
|
|
case VIR_DOMAIN_FEATURE_HPT:
|
|
if (def->features[feature] != VIR_TRISTATE_SWITCH_ON)
|
|
break;
|
|
|
|
if (def->hpt_resizing != VIR_DOMAIN_HPT_RESIZING_NONE) {
|
|
if (!virQEMUCapsGet(qemuCaps,
|
|
QEMU_CAPS_MACHINE_PSERIES_RESIZE_HPT)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("HTP resizing is not supported by this "
|
|
"QEMU binary"));
|
|
return -1;
|
|
}
|
|
|
|
str = virDomainHPTResizingTypeToString(def->hpt_resizing);
|
|
if (!str) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("Invalid setting for HPT resizing"));
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if (def->hpt_maxpagesize > 0 &&
|
|
!virQEMUCapsGet(qemuCaps,
|
|
QEMU_CAPS_MACHINE_PSERIES_CAP_HPT_MAX_PAGE_SIZE)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("Configuring the page size for HPT guests "
|
|
"is not supported by this QEMU binary"));
|
|
return -1;
|
|
}
|
|
break;
|
|
|
|
case VIR_DOMAIN_FEATURE_HTM:
|
|
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_MACHINE_PSERIES_CAP_HTM)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("HTM configuration is not supported by this "
|
|
"QEMU binary"));
|
|
return -1;
|
|
}
|
|
|
|
str = virTristateSwitchTypeToString(def->features[feature]);
|
|
if (!str) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("Invalid setting for HTM state"));
|
|
return -1;
|
|
}
|
|
|
|
break;
|
|
|
|
case VIR_DOMAIN_FEATURE_NESTED_HV:
|
|
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_MACHINE_PSERIES_CAP_NESTED_HV)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("Nested HV configuration is not supported by "
|
|
"this QEMU binary"));
|
|
return -1;
|
|
}
|
|
|
|
str = virTristateSwitchTypeToString(def->features[feature]);
|
|
if (!str) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("Invalid setting for nested HV state"));
|
|
return -1;
|
|
}
|
|
|
|
break;
|
|
|
|
case VIR_DOMAIN_FEATURE_CCF_ASSIST:
|
|
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_MACHINE_PSERIES_CAP_CCF_ASSIST)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("ccf-assist configuration is not supported by "
|
|
"this QEMU binary"));
|
|
return -1;
|
|
}
|
|
|
|
str = virTristateSwitchTypeToString(def->features[feature]);
|
|
if (!str) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("Invalid setting for ccf-assist state"));
|
|
return -1;
|
|
}
|
|
|
|
break;
|
|
|
|
case VIR_DOMAIN_FEATURE_CFPC:
|
|
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_MACHINE_PSERIES_CAP_CFPC)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("cfpc configuration is not supported by "
|
|
"this QEMU binary"));
|
|
return -1;
|
|
}
|
|
|
|
break;
|
|
|
|
case VIR_DOMAIN_FEATURE_SBBC:
|
|
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_MACHINE_PSERIES_CAP_SBBC)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("sbbc configuration is not supported by "
|
|
"this QEMU binary"));
|
|
return -1;
|
|
}
|
|
|
|
break;
|
|
|
|
case VIR_DOMAIN_FEATURE_IBS:
|
|
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_MACHINE_PSERIES_CAP_IBS)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("ibs configuration is not supported by "
|
|
"this QEMU binary"));
|
|
return -1;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
qemuValidateDomainDefFeatures(const virDomainDef *def,
|
|
virQEMUCaps *qemuCaps)
|
|
{
|
|
size_t i;
|
|
|
|
for (i = 0; i < VIR_DOMAIN_FEATURE_LAST; i++) {
|
|
const char *featureName = virDomainFeatureTypeToString(i);
|
|
|
|
switch ((virDomainFeature) i) {
|
|
case VIR_DOMAIN_FEATURE_IOAPIC:
|
|
if (def->features[i] != VIR_DOMAIN_IOAPIC_NONE) {
|
|
if (!ARCH_IS_X86(def->os.arch)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("The '%s' feature is not supported for "
|
|
"architecture '%s' or machine type '%s'"),
|
|
featureName,
|
|
virArchToString(def->os.arch),
|
|
def->os.machine);
|
|
return -1;
|
|
}
|
|
|
|
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_MACHINE_KERNEL_IRQCHIP)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("I/O APIC tuning is not supported by "
|
|
"this QEMU binary"));
|
|
return -1;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case VIR_DOMAIN_FEATURE_HPT:
|
|
case VIR_DOMAIN_FEATURE_HTM:
|
|
case VIR_DOMAIN_FEATURE_NESTED_HV:
|
|
case VIR_DOMAIN_FEATURE_CCF_ASSIST:
|
|
case VIR_DOMAIN_FEATURE_CFPC:
|
|
case VIR_DOMAIN_FEATURE_SBBC:
|
|
case VIR_DOMAIN_FEATURE_IBS:
|
|
if (qemuValidateDomainDefPSeriesFeature(def, qemuCaps, i) < 0)
|
|
return -1;
|
|
break;
|
|
|
|
case VIR_DOMAIN_FEATURE_GIC:
|
|
if (def->features[i] == VIR_TRISTATE_SWITCH_ON &&
|
|
!qemuDomainIsARMVirt(def)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("The '%s' feature is not supported for "
|
|
"architecture '%s' or machine type '%s'"),
|
|
featureName,
|
|
virArchToString(def->os.arch),
|
|
def->os.machine);
|
|
return -1;
|
|
}
|
|
break;
|
|
|
|
case VIR_DOMAIN_FEATURE_VMPORT:
|
|
if (def->features[i] != VIR_TRISTATE_SWITCH_ABSENT &&
|
|
!virQEMUCapsSupportsVmport(qemuCaps, def)) {
|
|
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("vmport is not available "
|
|
"with this QEMU binary"));
|
|
return -1;
|
|
}
|
|
break;
|
|
|
|
case VIR_DOMAIN_FEATURE_VMCOREINFO:
|
|
if (def->features[i] == VIR_TRISTATE_SWITCH_ON &&
|
|
!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_VMCOREINFO)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("vmcoreinfo is not available "
|
|
"with this QEMU binary"));
|
|
return -1;
|
|
}
|
|
break;
|
|
|
|
case VIR_DOMAIN_FEATURE_APIC:
|
|
/* The kvm_pv_eoi feature is x86-only. */
|
|
if (def->features[i] != VIR_TRISTATE_SWITCH_ABSENT &&
|
|
def->apic_eoi != VIR_TRISTATE_SWITCH_ABSENT &&
|
|
!ARCH_IS_X86(def->os.arch)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("The 'eoi' attribute of the '%s' feature "
|
|
"is not supported for architecture '%s' or "
|
|
"machine type '%s'"),
|
|
featureName,
|
|
virArchToString(def->os.arch),
|
|
def->os.machine);
|
|
return -1;
|
|
}
|
|
break;
|
|
|
|
case VIR_DOMAIN_FEATURE_PVSPINLOCK:
|
|
if (def->features[i] != VIR_TRISTATE_SWITCH_ABSENT &&
|
|
!ARCH_IS_X86(def->os.arch)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("The '%s' feature is not supported for "
|
|
"architecture '%s' or machine type '%s'"),
|
|
featureName,
|
|
virArchToString(def->os.arch),
|
|
def->os.machine);
|
|
return -1;
|
|
}
|
|
break;
|
|
|
|
case VIR_DOMAIN_FEATURE_HYPERV:
|
|
if (def->features[i] != VIR_TRISTATE_SWITCH_ABSENT &&
|
|
!ARCH_IS_X86(def->os.arch) && !qemuDomainIsARMVirt(def)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("Hyperv features are not supported for "
|
|
"architecture '%s' or machine type '%s'"),
|
|
virArchToString(def->os.arch),
|
|
def->os.machine);
|
|
return -1;
|
|
}
|
|
break;
|
|
|
|
case VIR_DOMAIN_FEATURE_PMU:
|
|
if (def->features[i] == VIR_TRISTATE_SWITCH_OFF &&
|
|
ARCH_IS_PPC64(def->os.arch)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("PMU is always enabled for architecture '%s'"),
|
|
virArchToString(def->os.arch));
|
|
return -1;
|
|
}
|
|
break;
|
|
|
|
case VIR_DOMAIN_FEATURE_SMM:
|
|
case VIR_DOMAIN_FEATURE_KVM:
|
|
case VIR_DOMAIN_FEATURE_XEN:
|
|
case VIR_DOMAIN_FEATURE_ACPI:
|
|
case VIR_DOMAIN_FEATURE_PAE:
|
|
case VIR_DOMAIN_FEATURE_HAP:
|
|
case VIR_DOMAIN_FEATURE_VIRIDIAN:
|
|
case VIR_DOMAIN_FEATURE_PRIVNET:
|
|
case VIR_DOMAIN_FEATURE_CAPABILITIES:
|
|
case VIR_DOMAIN_FEATURE_MSRS:
|
|
case VIR_DOMAIN_FEATURE_LAST:
|
|
break;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
qemuValidateDomainDefCpu(virQEMUDriver *driver,
|
|
const virDomainDef *def,
|
|
virQEMUCaps *qemuCaps)
|
|
{
|
|
virCPUDef *cpu = def->cpu;
|
|
|
|
if (!cpu)
|
|
return 0;
|
|
|
|
if (!cpu->model && cpu->mode == VIR_CPU_MODE_CUSTOM)
|
|
return 0;
|
|
|
|
switch ((virCPUMode) cpu->mode) {
|
|
case VIR_CPU_MODE_HOST_PASSTHROUGH:
|
|
if (def->os.arch == VIR_ARCH_ARMV7L &&
|
|
driver->hostarch == 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"));
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if (cpu->migratable &&
|
|
cpu->migratable != VIR_TRISTATE_SWITCH_OFF &&
|
|
!virQEMUCapsGet(qemuCaps, QEMU_CAPS_CPU_MIGRATABLE)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("Migratable attribute for host-passthrough "
|
|
"CPU is not supported by this QEMU binary"));
|
|
return -1;
|
|
}
|
|
break;
|
|
|
|
case VIR_CPU_MODE_HOST_MODEL:
|
|
/* qemu_command.c will error out if cpu->mode is HOST_MODEL for
|
|
* every arch but PPC64. However, we can't move this validation
|
|
* here because non-PPC64 archs will translate HOST_MODEL to
|
|
* something else during domain start, changing cpu->mode to
|
|
* CUSTOM.
|
|
*/
|
|
break;
|
|
|
|
case VIR_CPU_MODE_MAXIMUM:
|
|
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_CPU_MAX)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("maximum CPU is not supported by QEMU binary"));
|
|
return -1;
|
|
}
|
|
break;
|
|
|
|
case VIR_CPU_MODE_CUSTOM:
|
|
case VIR_CPU_MODE_LAST:
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
qemuValidateDomainDefClockTimers(const virDomainDef *def,
|
|
virQEMUCaps *qemuCaps)
|
|
{
|
|
size_t i;
|
|
|
|
for (i = 0; i < def->clock.ntimers; i++) {
|
|
virDomainTimerDef *timer = def->clock.timers[i];
|
|
|
|
switch ((virDomainTimerNameType)timer->name) {
|
|
case VIR_DOMAIN_TIMER_NAME_PLATFORM:
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("unsupported timer type (name) '%s'"),
|
|
virDomainTimerNameTypeToString(timer->name));
|
|
return -1;
|
|
|
|
case VIR_DOMAIN_TIMER_NAME_TSC:
|
|
case VIR_DOMAIN_TIMER_NAME_KVMCLOCK:
|
|
case VIR_DOMAIN_TIMER_NAME_HYPERVCLOCK:
|
|
if (!ARCH_IS_X86(def->os.arch) && timer->present == 1) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("Configuring the '%s' timer is not supported "
|
|
"for virtType=%s arch=%s machine=%s guests"),
|
|
virDomainTimerNameTypeToString(timer->name),
|
|
virDomainVirtTypeToString(def->virtType),
|
|
virArchToString(def->os.arch),
|
|
def->os.machine);
|
|
return -1;
|
|
}
|
|
break;
|
|
|
|
case VIR_DOMAIN_TIMER_NAME_LAST:
|
|
break;
|
|
|
|
case VIR_DOMAIN_TIMER_NAME_RTC:
|
|
switch (timer->track) {
|
|
case -1: /* unspecified - use hypervisor default */
|
|
case VIR_DOMAIN_TIMER_TRACK_GUEST:
|
|
case VIR_DOMAIN_TIMER_TRACK_WALL:
|
|
case VIR_DOMAIN_TIMER_TRACK_REALTIME:
|
|
break;
|
|
case VIR_DOMAIN_TIMER_TRACK_BOOT:
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("unsupported rtc timer track '%s'"),
|
|
virDomainTimerTrackTypeToString(timer->track));
|
|
return -1;
|
|
}
|
|
|
|
switch (timer->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 */
|
|
break;
|
|
case VIR_DOMAIN_TIMER_TICKPOLICY_MERGE:
|
|
case VIR_DOMAIN_TIMER_TICKPOLICY_DISCARD:
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("unsupported rtc timer tickpolicy '%s'"),
|
|
virDomainTimerTickpolicyTypeToString(
|
|
timer->tickpolicy));
|
|
return -1;
|
|
}
|
|
break;
|
|
|
|
case VIR_DOMAIN_TIMER_NAME_PIT:
|
|
switch (timer->tickpolicy) {
|
|
case -1:
|
|
case VIR_DOMAIN_TIMER_TICKPOLICY_DELAY:
|
|
case VIR_DOMAIN_TIMER_TICKPOLICY_DISCARD:
|
|
break;
|
|
case VIR_DOMAIN_TIMER_TICKPOLICY_CATCHUP:
|
|
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_KVM_PIT_TICK_POLICY)) {
|
|
/* can't catchup if we don't have kvm-pit */
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("unsupported pit tickpolicy '%s'"),
|
|
virDomainTimerTickpolicyTypeToString(
|
|
timer->tickpolicy));
|
|
return -1;
|
|
}
|
|
break;
|
|
case VIR_DOMAIN_TIMER_TICKPOLICY_MERGE:
|
|
/* no way to support this mode for pit in qemu */
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("unsupported pit tickpolicy '%s'"),
|
|
virDomainTimerTickpolicyTypeToString(
|
|
timer->tickpolicy));
|
|
return -1;
|
|
}
|
|
break;
|
|
|
|
case VIR_DOMAIN_TIMER_NAME_HPET:
|
|
/* no hpet timer available. The only possible action
|
|
is to raise an error if present="yes" */
|
|
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_NO_HPET) &&
|
|
timer->present == 1) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
"%s", _("hpet timer is not supported"));
|
|
return -1;
|
|
}
|
|
break;
|
|
|
|
case VIR_DOMAIN_TIMER_NAME_ARMVTIMER:
|
|
if (def->virtType != VIR_DOMAIN_VIRT_KVM ||
|
|
!qemuDomainIsARMVirt(def)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("Configuring the '%s' timer is not supported "
|
|
"for virtType=%s arch=%s machine=%s guests"),
|
|
virDomainTimerNameTypeToString(timer->name),
|
|
virDomainVirtTypeToString(def->virtType),
|
|
virArchToString(def->os.arch),
|
|
def->os.machine);
|
|
return -1;
|
|
}
|
|
if (timer->present == 0) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("The '%s' timer can't be disabled"),
|
|
virDomainTimerNameTypeToString(timer->name));
|
|
return -1;
|
|
}
|
|
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_CPU_KVM_NO_ADJVTIME)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("Configuring the '%s' timer is not supported "
|
|
"with this QEMU binary"),
|
|
virDomainTimerNameTypeToString(timer->name));
|
|
return -1;
|
|
}
|
|
|
|
switch (timer->tickpolicy) {
|
|
case -1:
|
|
case VIR_DOMAIN_TIMER_TICKPOLICY_DELAY:
|
|
case VIR_DOMAIN_TIMER_TICKPOLICY_DISCARD:
|
|
break;
|
|
case VIR_DOMAIN_TIMER_TICKPOLICY_CATCHUP:
|
|
case VIR_DOMAIN_TIMER_TICKPOLICY_MERGE:
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("The '%s' timer does not support tickpolicy '%s'"),
|
|
virDomainTimerNameTypeToString(timer->name),
|
|
virDomainTimerTickpolicyTypeToString(timer->tickpolicy));
|
|
return -1;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
qemuValidateDomainDefPM(const virDomainDef *def,
|
|
virQEMUCaps *qemuCaps)
|
|
{
|
|
bool q35Dom = qemuDomainIsQ35(def);
|
|
|
|
if (def->pm.s3) {
|
|
bool q35ICH9_S3 = q35Dom &&
|
|
virQEMUCapsGet(qemuCaps, QEMU_CAPS_ICH9_DISABLE_S3);
|
|
|
|
if (!q35ICH9_S3 && !virQEMUCapsGet(qemuCaps, QEMU_CAPS_PIIX_DISABLE_S3)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
"%s", _("setting ACPI S3 not supported"));
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if (def->pm.s4) {
|
|
bool q35ICH9_S4 = q35Dom &&
|
|
virQEMUCapsGet(qemuCaps, QEMU_CAPS_ICH9_DISABLE_S4);
|
|
|
|
if (!q35ICH9_S4 && !virQEMUCapsGet(qemuCaps, QEMU_CAPS_PIIX_DISABLE_S4)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
"%s", _("setting ACPI S4 not supported"));
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
qemuValidateDomainDefBoot(const virDomainDef *def,
|
|
virQEMUCaps *qemuCaps)
|
|
{
|
|
if (def->os.loader &&
|
|
def->os.loader->secure == VIR_TRISTATE_BOOL_YES) {
|
|
/* These are the QEMU implementation limitations. But we
|
|
* have to live with them for now. */
|
|
|
|
if (!qemuDomainIsQ35(def)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("Secure boot is supported with q35 machine types only"));
|
|
return -1;
|
|
}
|
|
|
|
/* Now, technically it is possible to have secure boot on
|
|
* 32bits too, but that would require some -cpu xxx magic
|
|
* too. Not worth it unless we are explicitly asked. */
|
|
if (def->os.arch != VIR_ARCH_X86_64) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("Secure boot is supported for x86_64 architecture only"));
|
|
return -1;
|
|
}
|
|
|
|
/* SMM will be enabled by qemuFirmwareFillDomain() if needed. */
|
|
if (def->os.firmware == VIR_DOMAIN_OS_DEF_FIRMWARE_NONE &&
|
|
def->features[VIR_DOMAIN_FEATURE_SMM] != VIR_TRISTATE_SWITCH_ON) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("Secure boot requires SMM feature enabled"));
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
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"));
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
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"));
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* qemuValidateDefGetVcpuHotplugGranularity:
|
|
* @def: domain definition
|
|
*
|
|
* With QEMU 2.7 and newer, vCPUs can only be hotplugged in groups that
|
|
* respect the guest's hotplug granularity; because of that, QEMU will
|
|
* not allow guests to start unless the initial number of vCPUs is a
|
|
* multiple of the hotplug granularity.
|
|
*
|
|
* Returns the vCPU hotplug granularity.
|
|
*/
|
|
static unsigned int
|
|
qemuValidateDefGetVcpuHotplugGranularity(const virDomainDef *def)
|
|
{
|
|
/* If the guest CPU topology has not been configured, assume we
|
|
* can hotplug vCPUs one at a time */
|
|
if (!def->cpu || def->cpu->sockets == 0)
|
|
return 1;
|
|
|
|
/* For pSeries guests, hotplug can only be performed one core
|
|
* at a time, so the vCPU hotplug granularity is the number
|
|
* of threads per core */
|
|
if (qemuDomainIsPSeries(def))
|
|
return def->cpu->threads;
|
|
|
|
/* In all other cases, we can hotplug vCPUs one at a time */
|
|
return 1;
|
|
}
|
|
|
|
|
|
static int
|
|
qemuValidateDomainVCpuTopology(const virDomainDef *def, virQEMUCaps *qemuCaps)
|
|
{
|
|
unsigned int maxCpus = virQEMUCapsGetMachineMaxCpus(qemuCaps, def->virtType,
|
|
def->os.machine);
|
|
|
|
if (virDomainDefGetVcpus(def) == 0) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("Domain requires at least 1 vCPU"));
|
|
return -1;
|
|
}
|
|
|
|
if (maxCpus > 0 && virDomainDefGetVcpusMax(def) > maxCpus) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("Maximum CPUs greater than specified machine "
|
|
"type limit %u"), maxCpus);
|
|
return -1;
|
|
}
|
|
|
|
/* QEMU 2.7 (detected via the availability of query-hotpluggable-cpus)
|
|
* enforces stricter rules than previous versions when it comes to guest
|
|
* CPU topology. Verify known constraints are respected */
|
|
if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_QUERY_HOTPLUGGABLE_CPUS)) {
|
|
unsigned int topologycpus;
|
|
unsigned int granularity;
|
|
unsigned int numacpus;
|
|
|
|
/* Starting from QEMU 2.5, max vCPU count and overall vCPU topology
|
|
* must agree. We only actually enforce this with QEMU 2.7+, due
|
|
* to the capability check above */
|
|
if (virDomainDefGetVcpusTopology(def, &topologycpus) == 0) {
|
|
if (topologycpus != virDomainDefGetVcpusMax(def)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("CPU topology doesn't match maximum vcpu count"));
|
|
return -1;
|
|
}
|
|
|
|
numacpus = virDomainNumaGetCPUCountTotal(def->numa);
|
|
if ((numacpus != 0) && (topologycpus != numacpus)) {
|
|
VIR_WARN("CPU topology doesn't match numa CPU count; "
|
|
"partial NUMA mapping is obsoleted and will "
|
|
"be removed in future");
|
|
}
|
|
}
|
|
|
|
/* vCPU hotplug granularity must be respected */
|
|
granularity = qemuValidateDefGetVcpuHotplugGranularity(def);
|
|
if ((virDomainDefGetVcpus(def) % granularity) != 0) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("vCPUs count must be a multiple of the vCPU "
|
|
"hotplug granularity (%u)"),
|
|
granularity);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if (ARCH_IS_X86(def->os.arch) &&
|
|
virDomainDefGetVcpusMax(def) > QEMU_MAX_VCPUS_WITHOUT_EIM) {
|
|
if (!qemuDomainIsQ35(def)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("more than %d vCPUs are only supported on "
|
|
"q35-based machine types"),
|
|
QEMU_MAX_VCPUS_WITHOUT_EIM);
|
|
return -1;
|
|
}
|
|
if (!def->iommu || def->iommu->eim != VIR_TRISTATE_SWITCH_ON) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("more than %d vCPUs require extended interrupt "
|
|
"mode enabled on the iommu device"),
|
|
QEMU_MAX_VCPUS_WITHOUT_EIM);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
qemuValidateDomainDefMemory(const virDomainDef *def,
|
|
virQEMUCaps *qemuCaps)
|
|
{
|
|
const char *defaultRAMid = virQEMUCapsGetMachineDefaultRAMid(qemuCaps,
|
|
def->virtType,
|
|
def->os.machine);
|
|
const long system_page_size = virGetSystemPageSizeKB();
|
|
const virDomainMemtune *mem = &def->mem;
|
|
|
|
if (mem->nhugepages == 0)
|
|
return 0;
|
|
|
|
if (mem->allocation == VIR_DOMAIN_MEMORY_ALLOCATION_ONDEMAND) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("hugepages are not allowed with memory "
|
|
"allocation ondemand"));
|
|
return -1;
|
|
}
|
|
|
|
if (mem->source == VIR_DOMAIN_MEMORY_SOURCE_ANONYMOUS) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("hugepages are not allowed with anonymous "
|
|
"memory source"));
|
|
return -1;
|
|
}
|
|
|
|
if (mem->source == VIR_DOMAIN_MEMORY_SOURCE_MEMFD &&
|
|
!virQEMUCapsGet(qemuCaps, QEMU_CAPS_OBJECT_MEMORY_MEMFD_HUGETLB)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("hugepages is not supported with memfd memory source"));
|
|
return -1;
|
|
}
|
|
|
|
/* We can't guarantee any other mem.access if no guest NUMA
|
|
* nodes are defined, unless defaultRAMid is provided. */
|
|
if (!defaultRAMid &&
|
|
mem->hugepages[0].size != system_page_size &&
|
|
virDomainNumaGetNodeCount(def->numa) == 0 &&
|
|
mem->access != VIR_DOMAIN_MEMORY_ACCESS_DEFAULT &&
|
|
mem->access != VIR_DOMAIN_MEMORY_ACCESS_PRIVATE) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("memory access mode '%s' not supported "
|
|
"without guest numa node"),
|
|
virDomainMemoryAccessTypeToString(mem->access));
|
|
return -1;
|
|
}
|
|
|
|
if (mem->nosharepages && !virQEMUCapsGet(qemuCaps, QEMU_CAPS_MEM_MERGE)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("disable shared memory is not available "
|
|
"with this QEMU binary"));
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
qemuValidateDomainDefNuma(const virDomainDef *def,
|
|
virQEMUCaps *qemuCaps)
|
|
{
|
|
const long system_page_size = virGetSystemPageSizeKB();
|
|
size_t ncells = virDomainNumaGetNodeCount(def->numa);
|
|
size_t i;
|
|
bool hasMemoryCap = virQEMUCapsGet(qemuCaps, QEMU_CAPS_OBJECT_MEMORY_RAM) ||
|
|
virQEMUCapsGet(qemuCaps, QEMU_CAPS_OBJECT_MEMORY_FILE) ||
|
|
virQEMUCapsGet(qemuCaps, QEMU_CAPS_OBJECT_MEMORY_MEMFD);
|
|
bool needBacking = false;
|
|
|
|
if (virDomainNumatuneHasPerNodeBinding(def->numa) && !hasMemoryCap) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("Per-node memory binding is not supported "
|
|
"with this QEMU"));
|
|
return -1;
|
|
}
|
|
|
|
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"));
|
|
return -1;
|
|
}
|
|
|
|
for (i = 0; i < ncells; i++) {
|
|
virBitmap *cpumask = virDomainNumaGetNodeCpumask(def->numa, i);
|
|
|
|
if (!hasMemoryCap &&
|
|
virDomainNumaGetNodeMemoryAccessMode(def->numa, i)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("Shared memory mapping is not supported "
|
|
"with this QEMU"));
|
|
return -1;
|
|
}
|
|
|
|
if (cpumask) {
|
|
g_autofree char * cpumaskStr = NULL;
|
|
if (!(cpumaskStr = virBitmapFormat(cpumask)))
|
|
return -1;
|
|
|
|
if (strchr(cpumaskStr, ',') &&
|
|
!virQEMUCapsGet(qemuCaps, QEMU_CAPS_NUMA)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("disjoint NUMA cpu ranges are not supported "
|
|
"with this QEMU"));
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!virQEMUCapsGetMachineNumaMemSupported(qemuCaps,
|
|
def->virtType,
|
|
def->os.machine)) {
|
|
needBacking = true;
|
|
}
|
|
|
|
if (virDomainNumaHasHMAT(def->numa)) {
|
|
needBacking = true;
|
|
|
|
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_NUMA_HMAT)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("HMAT is not supported with this QEMU"));
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if (needBacking && !hasMemoryCap) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("NUMA without specified memory backing is not "
|
|
"supported with this QEMU binary"));
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
qemuValidateDomainDefConsole(const virDomainDef *def,
|
|
virQEMUCaps *qemuCaps)
|
|
{
|
|
size_t i;
|
|
|
|
/* Explicit console devices */
|
|
for (i = 0; i < def->nconsoles; i++) {
|
|
virDomainChrDef *console = def->consoles[i];
|
|
|
|
switch (console->targetType) {
|
|
case VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_SCLP:
|
|
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_SCLPCONSOLE)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("sclpconsole is not supported in this QEMU binary"));
|
|
return -1;
|
|
}
|
|
break;
|
|
|
|
case VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_SCLPLM:
|
|
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_SCLPLMCONSOLE)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("sclplmconsole is not supported in this QEMU binary"));
|
|
return -1;
|
|
}
|
|
break;
|
|
|
|
case VIR_DOMAIN_CHR_CONSOLE_TARGET_TYPE_VIRTIO:
|
|
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;
|
|
}
|
|
|
|
|
|
static int
|
|
qemuValidateDomainDefSysinfo(const virSysinfoDef *def,
|
|
virQEMUCaps *qemuCaps)
|
|
{
|
|
size_t i;
|
|
|
|
if (def->type == VIR_SYSINFO_FWCFG &&
|
|
!virQEMUCapsGet(qemuCaps, QEMU_CAPS_FW_CFG)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("fw_cfg is not supported with this QEMU"));
|
|
return -1;
|
|
}
|
|
|
|
for (i = 0; i < def->nfw_cfgs; i++) {
|
|
const virSysinfoFWCfgDef *f = &def->fw_cfgs[i];
|
|
|
|
if (!STRPREFIX(f->name, "opt/")) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("Invalid firmware name"));
|
|
return -1;
|
|
}
|
|
|
|
if (STRPREFIX(f->name, "opt/ovmf/") ||
|
|
STRPREFIX(f->name, "opt/org.qemu/")) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("That firmware name is reserved"));
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
qemuValidateDomainDefPanic(const virDomainDef *def,
|
|
virQEMUCaps *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 (!qemuDomainIsPSeries(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;
|
|
}
|
|
|
|
if (def->panics[i]->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE &&
|
|
def->panics[i]->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_ISA) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("panic is supported only "
|
|
"with ISA address type"));
|
|
return -1;
|
|
}
|
|
break;
|
|
|
|
/* default model value was changed before in post parse */
|
|
case VIR_DOMAIN_PANIC_MODEL_DEFAULT:
|
|
case VIR_DOMAIN_PANIC_MODEL_LAST:
|
|
break;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
qemuValidateDomainDeviceInfo(virDomainDef *def G_GNUC_UNUSED,
|
|
virDomainDeviceDef *dev G_GNUC_UNUSED,
|
|
virDomainDeviceInfo *info,
|
|
void *opaque)
|
|
{
|
|
virQEMUCaps *qemuCaps = opaque;
|
|
|
|
if (info->acpiIndex) {
|
|
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_ACPI_INDEX)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("ACPI index is not supported with this QEMU"));
|
|
return -1;
|
|
}
|
|
|
|
if (info->type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE &&
|
|
info->type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("ACPI index is only supported for PCI devices"));
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
qemuValidateDomainDef(const virDomainDef *def,
|
|
void *opaque,
|
|
void *parseOpaque)
|
|
{
|
|
virQEMUDriver *driver = opaque;
|
|
g_autoptr(virQEMUDriverConfig) cfg = virQEMUDriverGetConfig(driver);
|
|
g_autoptr(virQEMUCaps) qemuCapsLocal = NULL;
|
|
virQEMUCaps *qemuCaps = parseOpaque;
|
|
size_t i;
|
|
|
|
if (!qemuCaps) {
|
|
if (!(qemuCapsLocal = virQEMUCapsCacheLookup(driver->qemuCapsCache,
|
|
def->emulator)))
|
|
return -1;
|
|
|
|
qemuCaps = qemuCapsLocal;
|
|
}
|
|
|
|
if (def->os.type != VIR_DOMAIN_OSTYPE_HVM) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("Emulator '%s' does not support os type '%s'"),
|
|
def->emulator, virDomainOSTypeToString(def->os.type));
|
|
return -1;
|
|
}
|
|
|
|
if (!virQEMUCapsIsArchSupported(qemuCaps, def->os.arch)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("Emulator '%s' does not support arch '%s'"),
|
|
def->emulator, virArchToString(def->os.arch));
|
|
return -1;
|
|
}
|
|
|
|
if (!virQEMUCapsIsVirtTypeSupported(qemuCaps, def->virtType)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("Emulator '%s' does not support virt type '%s'"),
|
|
def->emulator, virDomainVirtTypeToString(def->virtType));
|
|
return -1;
|
|
}
|
|
|
|
if (!virQEMUCapsIsMachineSupported(qemuCaps, def->virtType, def->os.machine)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("Emulator '%s' does not support machine type '%s'"),
|
|
def->emulator, def->os.machine);
|
|
return -1;
|
|
}
|
|
|
|
if (def->mem.min_guarantee) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("Parameter 'min_guarantee' not supported by QEMU."));
|
|
return -1;
|
|
}
|
|
|
|
/* On x86, UEFI requires ACPI */
|
|
if ((def->os.firmware == VIR_DOMAIN_OS_DEF_FIRMWARE_EFI ||
|
|
virDomainDefHasOldStyleUEFI(def)) &&
|
|
ARCH_IS_X86(def->os.arch) &&
|
|
def->features[VIR_DOMAIN_FEATURE_ACPI] != VIR_TRISTATE_SWITCH_ON) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("UEFI requires ACPI on this architecture"));
|
|
return -1;
|
|
}
|
|
|
|
/* On aarch64, ACPI requires UEFI */
|
|
if (def->features[VIR_DOMAIN_FEATURE_ACPI] == VIR_TRISTATE_SWITCH_ON &&
|
|
def->os.arch == VIR_ARCH_AARCH64 &&
|
|
(def->os.firmware != VIR_DOMAIN_OS_DEF_FIRMWARE_EFI &&
|
|
!virDomainDefHasOldStyleUEFI(def))) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("ACPI requires UEFI on this architecture"));
|
|
return -1;
|
|
}
|
|
|
|
if (def->genidRequested &&
|
|
!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_VMGENID)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("this QEMU does not support the 'genid' capability"));
|
|
return -1;
|
|
}
|
|
|
|
/* 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;
|
|
}
|
|
}
|
|
|
|
if (qemuValidateDomainDefCpu(driver, def, qemuCaps) < 0)
|
|
return -1;
|
|
|
|
if (qemuDomainDefValidateMemoryHotplug(def, NULL) < 0)
|
|
return -1;
|
|
|
|
if (qemuValidateDomainDefClockTimers(def, qemuCaps) < 0)
|
|
return -1;
|
|
|
|
if (qemuValidateDomainDefPM(def, qemuCaps) < 0)
|
|
return -1;
|
|
|
|
if (qemuValidateDomainDefBoot(def, qemuCaps) < 0)
|
|
return -1;
|
|
|
|
if (qemuValidateDomainVCpuTopology(def, qemuCaps) < 0)
|
|
return -1;
|
|
|
|
if (def->nresctrls &&
|
|
def->virtType != VIR_DOMAIN_VIRT_KVM) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("cachetune is only supported for KVM domains"));
|
|
return -1;
|
|
}
|
|
|
|
if (qemuValidateDomainDefFeatures(def, qemuCaps) < 0)
|
|
return -1;
|
|
|
|
if (qemuValidateDomainDefMemory(def, qemuCaps) < 0)
|
|
return -1;
|
|
|
|
if (qemuValidateDomainDefNuma(def, qemuCaps) < 0)
|
|
return -1;
|
|
|
|
if (qemuValidateDomainDefConsole(def, qemuCaps) < 0)
|
|
return -1;
|
|
|
|
if (cfg->vncTLS && cfg->vncTLSx509secretUUID &&
|
|
!virQEMUCapsGet(qemuCaps, QEMU_CAPS_OBJECT_TLS_CREDS_X509)) {
|
|
for (i = 0; i < def->ngraphics; i++) {
|
|
if (def->graphics[i]->type == VIR_DOMAIN_GRAPHICS_TYPE_VNC) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("encrypted VNC TLS keys are not supported with "
|
|
"this QEMU binary"));
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < def->nsysinfo; i++) {
|
|
if (qemuValidateDomainDefSysinfo(def->sysinfo[i], qemuCaps) < 0)
|
|
return -1;
|
|
}
|
|
|
|
if (qemuValidateDomainDefPanic(def, qemuCaps) < 0)
|
|
return -1;
|
|
|
|
if (def->sec) {
|
|
switch ((virDomainLaunchSecurity) def->sec->sectype) {
|
|
case VIR_DOMAIN_LAUNCH_SECURITY_SEV:
|
|
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_SEV_GUEST)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("SEV launch security is not supported with "
|
|
"this QEMU binary"));
|
|
return -1;
|
|
}
|
|
break;
|
|
case VIR_DOMAIN_LAUNCH_SECURITY_PV:
|
|
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_MACHINE_CONFIDENTAL_GUEST_SUPPORT) ||
|
|
!virQEMUCapsGet(qemuCaps, QEMU_CAPS_S390_PV_GUEST) ||
|
|
!virQEMUCapsGetKVMSupportsSecureGuest(qemuCaps)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("S390 PV launch security is not supported with "
|
|
"this QEMU binary"));
|
|
return -1;
|
|
}
|
|
break;
|
|
case VIR_DOMAIN_LAUNCH_SECURITY_NONE:
|
|
case VIR_DOMAIN_LAUNCH_SECURITY_LAST:
|
|
virReportEnumRangeError(virDomainLaunchSecurity, def->sec->sectype);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if (def->naudios > 1 &&
|
|
!virQEMUCapsGet(qemuCaps, QEMU_CAPS_AUDIODEV)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("only one audio backend is supported with this QEMU binary"));
|
|
return -1;
|
|
}
|
|
|
|
/* Explicitly discarding 'const' from 'def' is ok because
|
|
* we know our callback qemuValidateDomainDeviceInfo will
|
|
* not modify it
|
|
*/
|
|
if (virDomainDeviceInfoIterate((virDomainDef *)def,
|
|
qemuValidateDomainDeviceInfo,
|
|
qemuCaps) < 0)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
qemuValidateDomainDeviceDefZPCIAddress(virDomainDeviceInfo *info,
|
|
virQEMUCaps *qemuCaps)
|
|
{
|
|
virZPCIDeviceAddress *zpci = &info->addr.pci.zpci;
|
|
|
|
if (virZPCIDeviceAddressIsPresent(zpci) &&
|
|
!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_ZPCI)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
"%s",
|
|
_("This QEMU binary doesn't support zPCI"));
|
|
return -1;
|
|
}
|
|
|
|
/* We don't need to check fid because fid covers
|
|
* all range of uint32 type.
|
|
*/
|
|
if (zpci->uid.isSet &&
|
|
(zpci->uid.value > VIR_DOMAIN_DEVICE_ZPCI_MAX_UID ||
|
|
zpci->uid.value == 0)) {
|
|
virReportError(VIR_ERR_XML_ERROR,
|
|
_("Invalid PCI address uid='0x%.4x', "
|
|
"must be > 0x0000 and <= 0x%.4x"),
|
|
zpci->uid.value,
|
|
VIR_DOMAIN_DEVICE_ZPCI_MAX_UID);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
qemuValidateDomainDeviceDefAddress(const virDomainDeviceDef *dev,
|
|
const virDomainDef *def,
|
|
virQEMUCaps *qemuCaps)
|
|
{
|
|
virDomainDeviceInfo *info;
|
|
|
|
if (!(info = virDomainDeviceGetInfo((virDomainDeviceDef *)dev)))
|
|
return 0;
|
|
|
|
switch ((virDomainDeviceAddressType) info->type) {
|
|
case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI:
|
|
if (qemuValidateDomainDeviceDefZPCIAddress(info, qemuCaps) < 0)
|
|
return -1;
|
|
break;
|
|
|
|
case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE:
|
|
/* Address validation might happen before we have had a chance to
|
|
* automatically assign addresses to devices for which the user
|
|
* didn't specify one themselves */
|
|
break;
|
|
|
|
case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_SPAPRVIO: {
|
|
virDomainDeviceSpaprVioAddress *addr = &(info->addr.spaprvio);
|
|
|
|
if (addr->has_reg && addr->reg > 0xffffffff) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("spapr-vio reg='0x%llx' exceeds maximum "
|
|
"possible value (0xffffffff)"),
|
|
addr->reg);
|
|
return -1;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_S390:
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("'virtio-s390' addresses are no longer supported"));
|
|
return -1;
|
|
|
|
case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_CCW:
|
|
if (!qemuDomainIsS390CCW(def)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("cannot use CCW address type for device '%s' using machine type '%s'"),
|
|
NULLSTR(info->alias), def->os.machine);
|
|
return -1;
|
|
}
|
|
|
|
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_CCW)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("CCW address type is not supported by this QEMU"));
|
|
return -1;
|
|
}
|
|
|
|
break;
|
|
|
|
case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DRIVE:
|
|
case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_SERIAL:
|
|
case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_CCID:
|
|
case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_USB:
|
|
case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_VIRTIO_MMIO:
|
|
case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_ISA:
|
|
case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DIMM:
|
|
case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_UNASSIGNED:
|
|
/* No validation for these address types yet */
|
|
break;
|
|
|
|
case VIR_DOMAIN_DEVICE_ADDRESS_TYPE_LAST:
|
|
default:
|
|
virReportEnumRangeError(virDomainDeviceAddressType, info->type);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static bool
|
|
qemuValidateNetSupportsCoalesce(virDomainNetType type)
|
|
{
|
|
switch (type) {
|
|
case VIR_DOMAIN_NET_TYPE_NETWORK:
|
|
case VIR_DOMAIN_NET_TYPE_BRIDGE:
|
|
return true;
|
|
case VIR_DOMAIN_NET_TYPE_VHOSTUSER:
|
|
case VIR_DOMAIN_NET_TYPE_ETHERNET:
|
|
case VIR_DOMAIN_NET_TYPE_DIRECT:
|
|
case VIR_DOMAIN_NET_TYPE_HOSTDEV:
|
|
case VIR_DOMAIN_NET_TYPE_USER:
|
|
case VIR_DOMAIN_NET_TYPE_SERVER:
|
|
case VIR_DOMAIN_NET_TYPE_CLIENT:
|
|
case VIR_DOMAIN_NET_TYPE_MCAST:
|
|
case VIR_DOMAIN_NET_TYPE_INTERNAL:
|
|
case VIR_DOMAIN_NET_TYPE_UDP:
|
|
case VIR_DOMAIN_NET_TYPE_VDPA:
|
|
case VIR_DOMAIN_NET_TYPE_LAST:
|
|
break;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
static int
|
|
qemuValidateDomainVirtioOptions(const virDomainVirtioOptions *virtio,
|
|
virQEMUCaps *qemuCaps)
|
|
{
|
|
if (!virtio)
|
|
return 0;
|
|
|
|
if (virtio->iommu != VIR_TRISTATE_SWITCH_ABSENT &&
|
|
!virQEMUCapsGet(qemuCaps, QEMU_CAPS_VIRTIO_PCI_IOMMU_PLATFORM)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("the iommu setting is not supported "
|
|
"with this QEMU binary"));
|
|
return -1;
|
|
}
|
|
|
|
if (virtio->ats != VIR_TRISTATE_SWITCH_ABSENT &&
|
|
!virQEMUCapsGet(qemuCaps, QEMU_CAPS_VIRTIO_PCI_ATS)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("the ats setting is not supported with this "
|
|
"QEMU binary"));
|
|
return -1;
|
|
}
|
|
|
|
if (virtio->packed != VIR_TRISTATE_SWITCH_ABSENT &&
|
|
!virQEMUCapsGet(qemuCaps, QEMU_CAPS_VIRTIO_PACKED_QUEUES)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("the packed setting is not supported with this "
|
|
"QEMU binary"));
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* qemuValidateDomainDefVhostUserRequireSharedMemory:
|
|
* @def: VM definition
|
|
* @name: name of the attribute/element
|
|
* @qemuCaps: capabilities of QEMU binary
|
|
*
|
|
* Check if the VM definition contains any form of shared memory
|
|
* which is required by vhost-user devices to operate properly.
|
|
*
|
|
* On success returns 0, on error returns -1 and reports proper error
|
|
* message.
|
|
*/
|
|
static int
|
|
qemuValidateDomainDefVhostUserRequireSharedMemory(const virDomainDef *def,
|
|
const char *name,
|
|
virQEMUCaps *qemuCaps)
|
|
{
|
|
const char *defaultRAMId = virQEMUCapsGetMachineDefaultRAMid(qemuCaps,
|
|
def->virtType,
|
|
def->os.machine);
|
|
size_t numa_nodes = virDomainNumaGetNodeCount(def->numa);
|
|
size_t i;
|
|
|
|
if (numa_nodes == 0 &&
|
|
!(defaultRAMId && def->mem.access == VIR_DOMAIN_MEMORY_ACCESS_SHARED)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("'%s' requires shared memory"), name);
|
|
return -1;
|
|
}
|
|
|
|
for (i = 0; i < numa_nodes; i++) {
|
|
virDomainMemoryAccess node_access =
|
|
virDomainNumaGetNodeMemoryAccessMode(def->numa, i);
|
|
|
|
switch (node_access) {
|
|
case VIR_DOMAIN_MEMORY_ACCESS_DEFAULT:
|
|
if (def->mem.access != VIR_DOMAIN_MEMORY_ACCESS_SHARED) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("'%s' requires shared memory"), name);
|
|
return -1;
|
|
}
|
|
break;
|
|
case VIR_DOMAIN_MEMORY_ACCESS_SHARED:
|
|
break;
|
|
case VIR_DOMAIN_MEMORY_ACCESS_PRIVATE:
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("'%s' requires shared memory"), name);
|
|
return -1;
|
|
|
|
case VIR_DOMAIN_MEMORY_ACCESS_LAST:
|
|
default:
|
|
virReportEnumRangeError(virDomainMemoryAccess, node_access);
|
|
return -1;
|
|
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
qemuValidateDomainDeviceDefNetwork(const virDomainNetDef *net,
|
|
virQEMUCaps *qemuCaps)
|
|
{
|
|
bool hasIPv4 = false;
|
|
bool hasIPv6 = false;
|
|
size_t i;
|
|
|
|
if (net->type == VIR_DOMAIN_NET_TYPE_USER) {
|
|
if (net->guestIP.nroutes) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("Invalid attempt to set network interface "
|
|
"guest-side IP route, not supported by QEMU"));
|
|
return -1;
|
|
}
|
|
|
|
for (i = 0; i < net->guestIP.nips; i++) {
|
|
const virNetDevIPAddr *ip = net->guestIP.ips[i];
|
|
|
|
if (VIR_SOCKET_ADDR_VALID(&net->guestIP.ips[i]->peer)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("Invalid attempt to set peer IP for guest"));
|
|
return -1;
|
|
}
|
|
|
|
if (VIR_SOCKET_ADDR_IS_FAMILY(&ip->address, AF_INET)) {
|
|
if (hasIPv4) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("Only one IPv4 address per "
|
|
"interface is allowed"));
|
|
return -1;
|
|
}
|
|
hasIPv4 = true;
|
|
|
|
if (ip->prefix > 0 &&
|
|
(ip->prefix < 4 || ip->prefix > 27)) {
|
|
virReportError(VIR_ERR_XML_ERROR, "%s",
|
|
_("invalid prefix, must be in range of 4-27"));
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if (VIR_SOCKET_ADDR_IS_FAMILY(&ip->address, AF_INET6)) {
|
|
if (hasIPv6) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("Only one IPv6 address per "
|
|
"interface is allowed"));
|
|
return -1;
|
|
}
|
|
hasIPv6 = true;
|
|
|
|
if (ip->prefix > 120) {
|
|
virReportError(VIR_ERR_XML_ERROR, "%s",
|
|
_("prefix too long"));
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
} else if (net->type == VIR_DOMAIN_NET_TYPE_VDPA) {
|
|
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_NETDEV_VHOST_VDPA)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("vDPA devices are not supported with this QEMU binary"));
|
|
return -1;
|
|
}
|
|
|
|
if (net->model != VIR_DOMAIN_NET_MODEL_VIRTIO) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("invalid model for interface of type '%s': '%s'"),
|
|
virDomainNetTypeToString(net->type),
|
|
virDomainNetModelTypeToString(net->model));
|
|
return -1;
|
|
}
|
|
} else if (net->guestIP.nroutes || net->guestIP.nips) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("Invalid attempt to set network interface "
|
|
"guest-side IP route and/or address info, "
|
|
"not supported by QEMU"));
|
|
return -1;
|
|
}
|
|
|
|
if (virDomainNetIsVirtioModel(net)) {
|
|
if (net->driver.virtio.txmode &&
|
|
!virQEMUCapsGet(qemuCaps, QEMU_CAPS_VIRTIO_TX_ALG)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("virtio-net-pci 'tx' option not supported in "
|
|
"this QEMU binary"));
|
|
return -1;
|
|
}
|
|
|
|
if (net->driver.virtio.rx_queue_size) {
|
|
if (!VIR_IS_POW2(net->driver.virtio.rx_queue_size)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("rx_queue_size has to be a power of two"));
|
|
return -1;
|
|
}
|
|
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_VIRTIO_NET_RX_QUEUE_SIZE)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("virtio rx_queue_size option is not supported "
|
|
"with this QEMU binary"));
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if (net->driver.virtio.tx_queue_size) {
|
|
if (!VIR_IS_POW2(net->driver.virtio.tx_queue_size)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("tx_queue_size has to be a power of two"));
|
|
return -1;
|
|
}
|
|
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_VIRTIO_NET_TX_QUEUE_SIZE)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("virtio tx_queue_size option is not supported "
|
|
"with this QEMU binary"));
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if (net->mtu &&
|
|
!virQEMUCapsGet(qemuCaps, QEMU_CAPS_VIRTIO_NET_HOST_MTU)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("setting MTU is not supported with this "
|
|
"QEMU binary"));
|
|
return -1;
|
|
}
|
|
|
|
if (qemuValidateDomainVirtioOptions(net->virtio, qemuCaps) < 0)
|
|
return -1;
|
|
}
|
|
|
|
if (net->mtu &&
|
|
!qemuDomainNetSupportsMTU(net->type)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("setting MTU on interface type %s is not supported yet"),
|
|
virDomainNetTypeToString(net->type));
|
|
return -1;
|
|
}
|
|
|
|
if (net->teaming) {
|
|
if (net->teaming->type != VIR_DOMAIN_NET_TEAMING_TYPE_NONE &&
|
|
!virQEMUCapsGet(qemuCaps, QEMU_CAPS_VIRTIO_NET_FAILOVER)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("virtio-net failover (teaming) is not supported with this QEMU binary"));
|
|
return -1;
|
|
}
|
|
if (net->teaming->type == VIR_DOMAIN_NET_TEAMING_TYPE_PERSISTENT
|
|
&& !virDomainNetIsVirtioModel(net)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("virtio-net teaming persistent interface must be <model type='virtio'/>, not '%s'"),
|
|
virDomainNetGetModelString(net));
|
|
return -1;
|
|
}
|
|
if (net->teaming->type == VIR_DOMAIN_NET_TEAMING_TYPE_TRANSIENT &&
|
|
net->type != VIR_DOMAIN_NET_TYPE_HOSTDEV &&
|
|
net->type != VIR_DOMAIN_NET_TYPE_NETWORK) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("virtio-net teaming transient interface must be type='hostdev', not '%s'"),
|
|
virDomainNetTypeToString(net->type));
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if (net->coalesce && !qemuValidateNetSupportsCoalesce(net->type)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("coalesce settings on interface type %s are not supported"),
|
|
virDomainNetTypeToString(net->type));
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
qemuValidateDomainChrSourceReconnectDef(const virDomainChrSourceReconnectDef *def)
|
|
{
|
|
if (def->enabled == VIR_TRISTATE_BOOL_YES &&
|
|
def->timeout == 0) {
|
|
virReportError(VIR_ERR_INVALID_ARG, "%s",
|
|
_("chardev reconnect source timeout cannot be '0'"));
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
qemuValidateChrSerialTargetTypeToAddressType(int targetType)
|
|
{
|
|
switch ((virDomainChrSerialTargetType)targetType) {
|
|
case VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_ISA:
|
|
return VIR_DOMAIN_DEVICE_ADDRESS_TYPE_ISA;
|
|
case VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_USB:
|
|
return VIR_DOMAIN_DEVICE_ADDRESS_TYPE_USB;
|
|
case VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_PCI:
|
|
return VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI;
|
|
case VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_SPAPR_VIO:
|
|
return VIR_DOMAIN_DEVICE_ADDRESS_TYPE_SPAPRVIO;
|
|
case VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_SYSTEM:
|
|
case VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_SCLP:
|
|
case VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_LAST:
|
|
case VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_NONE:
|
|
break;
|
|
}
|
|
|
|
return VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE;
|
|
}
|
|
|
|
|
|
static int
|
|
qemuValidateChrSerialTargetModelToTargetType(int targetModel)
|
|
{
|
|
switch ((virDomainChrSerialTargetModel) targetModel) {
|
|
case VIR_DOMAIN_CHR_SERIAL_TARGET_MODEL_ISA_SERIAL:
|
|
return VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_ISA;
|
|
case VIR_DOMAIN_CHR_SERIAL_TARGET_MODEL_USB_SERIAL:
|
|
return VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_USB;
|
|
case VIR_DOMAIN_CHR_SERIAL_TARGET_MODEL_PCI_SERIAL:
|
|
return VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_PCI;
|
|
case VIR_DOMAIN_CHR_SERIAL_TARGET_MODEL_SPAPR_VTY:
|
|
return VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_SPAPR_VIO;
|
|
case VIR_DOMAIN_CHR_SERIAL_TARGET_MODEL_PL011:
|
|
case VIR_DOMAIN_CHR_SERIAL_TARGET_MODEL_16550A:
|
|
return VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_SYSTEM;
|
|
case VIR_DOMAIN_CHR_SERIAL_TARGET_MODEL_SCLPCONSOLE:
|
|
case VIR_DOMAIN_CHR_SERIAL_TARGET_MODEL_SCLPLMCONSOLE:
|
|
return VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_SCLP;
|
|
case VIR_DOMAIN_CHR_SERIAL_TARGET_MODEL_NONE:
|
|
case VIR_DOMAIN_CHR_SERIAL_TARGET_MODEL_LAST:
|
|
break;
|
|
}
|
|
|
|
return VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_NONE;
|
|
}
|
|
|
|
|
|
static int
|
|
qemuValidateDomainChrTargetDef(const virDomainChrDef *chr)
|
|
{
|
|
int expected;
|
|
|
|
switch ((virDomainChrDeviceType)chr->deviceType) {
|
|
case VIR_DOMAIN_CHR_DEVICE_TYPE_SERIAL:
|
|
|
|
/* Validate target type */
|
|
switch ((virDomainChrSerialTargetType)chr->targetType) {
|
|
case VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_ISA:
|
|
case VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_USB:
|
|
case VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_PCI:
|
|
case VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_SPAPR_VIO:
|
|
|
|
expected = qemuValidateChrSerialTargetTypeToAddressType(chr->targetType);
|
|
|
|
if (chr->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE &&
|
|
chr->info.type != expected) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("Target type '%s' requires address type '%s'"),
|
|
virDomainChrSerialTargetTypeToString(chr->targetType),
|
|
virDomainDeviceAddressTypeToString(expected));
|
|
return -1;
|
|
}
|
|
break;
|
|
|
|
case VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_SYSTEM:
|
|
case VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_SCLP:
|
|
if (chr->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("Target type '%s' cannot have an "
|
|
"associated address"),
|
|
virDomainChrSerialTargetTypeToString(chr->targetType));
|
|
return -1;
|
|
}
|
|
break;
|
|
|
|
case VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_NONE:
|
|
case VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_LAST:
|
|
break;
|
|
}
|
|
|
|
/* Validate target model */
|
|
switch ((virDomainChrSerialTargetModel) chr->targetModel) {
|
|
case VIR_DOMAIN_CHR_SERIAL_TARGET_MODEL_ISA_SERIAL:
|
|
case VIR_DOMAIN_CHR_SERIAL_TARGET_MODEL_USB_SERIAL:
|
|
case VIR_DOMAIN_CHR_SERIAL_TARGET_MODEL_PCI_SERIAL:
|
|
case VIR_DOMAIN_CHR_SERIAL_TARGET_MODEL_SPAPR_VTY:
|
|
case VIR_DOMAIN_CHR_SERIAL_TARGET_MODEL_PL011:
|
|
case VIR_DOMAIN_CHR_SERIAL_TARGET_MODEL_SCLPCONSOLE:
|
|
case VIR_DOMAIN_CHR_SERIAL_TARGET_MODEL_SCLPLMCONSOLE:
|
|
case VIR_DOMAIN_CHR_SERIAL_TARGET_MODEL_16550A:
|
|
|
|
expected = qemuValidateChrSerialTargetModelToTargetType(chr->targetModel);
|
|
|
|
if (chr->targetType != expected) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("Target model '%s' requires target type '%s'"),
|
|
virDomainChrSerialTargetModelTypeToString(chr->targetModel),
|
|
virDomainChrSerialTargetTypeToString(expected));
|
|
return -1;
|
|
}
|
|
break;
|
|
|
|
case VIR_DOMAIN_CHR_SERIAL_TARGET_MODEL_NONE:
|
|
case VIR_DOMAIN_CHR_SERIAL_TARGET_MODEL_LAST:
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case VIR_DOMAIN_CHR_DEVICE_TYPE_CONSOLE:
|
|
case VIR_DOMAIN_CHR_DEVICE_TYPE_PARALLEL:
|
|
case VIR_DOMAIN_CHR_DEVICE_TYPE_CHANNEL:
|
|
case VIR_DOMAIN_CHR_DEVICE_TYPE_LAST:
|
|
/* Nothing to do */
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
qemuValidateDomainChrSourceDef(const virDomainChrSourceDef *def,
|
|
virQEMUCaps *qemuCaps)
|
|
{
|
|
switch ((virDomainChrType)def->type) {
|
|
case VIR_DOMAIN_CHR_TYPE_TCP:
|
|
if (qemuValidateDomainChrSourceReconnectDef(&def->data.tcp.reconnect) < 0)
|
|
return -1;
|
|
break;
|
|
|
|
case VIR_DOMAIN_CHR_TYPE_UNIX:
|
|
if (qemuValidateDomainChrSourceReconnectDef(&def->data.nix.reconnect) < 0)
|
|
return -1;
|
|
break;
|
|
|
|
case VIR_DOMAIN_CHR_TYPE_FILE:
|
|
if (def->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"));
|
|
return -1;
|
|
}
|
|
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_PIPE:
|
|
case VIR_DOMAIN_CHR_TYPE_STDIO:
|
|
case VIR_DOMAIN_CHR_TYPE_UDP:
|
|
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 (def->logfile) {
|
|
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_CHARDEV_LOGFILE)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("logfile not supported in this QEMU binary"));
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
qemuValidateDomainChrDef(const virDomainChrDef *dev,
|
|
const virDomainDef *def,
|
|
virQEMUCaps *qemuCaps)
|
|
{
|
|
if (qemuValidateDomainChrSourceDef(dev->source, qemuCaps) < 0)
|
|
return -1;
|
|
|
|
if (qemuValidateDomainChrTargetDef(dev) < 0)
|
|
return -1;
|
|
|
|
if (dev->deviceType == VIR_DOMAIN_CHR_DEVICE_TYPE_PARALLEL &&
|
|
(ARCH_IS_S390(def->os.arch) || qemuDomainIsPSeries(def))) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("parallel ports are not supported"));
|
|
return -1;
|
|
}
|
|
|
|
if (dev->deviceType == VIR_DOMAIN_CHR_DEVICE_TYPE_SERIAL) {
|
|
bool isCompatible = true;
|
|
|
|
if (dev->targetType == VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_SYSTEM) {
|
|
if (dev->targetModel == VIR_DOMAIN_CHR_SERIAL_TARGET_MODEL_PL011 &&
|
|
!qemuDomainIsARMVirt(def)) {
|
|
isCompatible = false;
|
|
}
|
|
if (dev->targetModel == VIR_DOMAIN_CHR_SERIAL_TARGET_MODEL_16550A &&
|
|
!qemuDomainIsRISCVVirt(def)) {
|
|
isCompatible = false;
|
|
}
|
|
}
|
|
|
|
if (!qemuDomainIsPSeries(def) &&
|
|
(dev->targetType == VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_SPAPR_VIO ||
|
|
dev->targetModel == VIR_DOMAIN_CHR_SERIAL_TARGET_MODEL_SPAPR_VTY)) {
|
|
isCompatible = false;
|
|
}
|
|
|
|
if (!ARCH_IS_S390(def->os.arch) &&
|
|
(dev->targetType == VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_SCLP ||
|
|
dev->targetModel == VIR_DOMAIN_CHR_SERIAL_TARGET_MODEL_SCLPCONSOLE ||
|
|
dev->targetModel == VIR_DOMAIN_CHR_SERIAL_TARGET_MODEL_SCLPLMCONSOLE)) {
|
|
isCompatible = false;
|
|
}
|
|
|
|
if (!isCompatible) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("Serial device with target type '%s' and "
|
|
"target model '%s' not compatible with guest "
|
|
"architecture or machine type"),
|
|
virDomainChrSerialTargetTypeToString(dev->targetType),
|
|
virDomainChrSerialTargetModelTypeToString(dev->targetModel));
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
qemuValidateDomainSmartcardDef(const virDomainSmartcardDef *def,
|
|
virQEMUCaps *qemuCaps)
|
|
{
|
|
switch (def->type) {
|
|
case VIR_DOMAIN_SMARTCARD_TYPE_HOST:
|
|
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_CCID_EMULATED)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("this QEMU binary lacks smartcard host "
|
|
"mode support"));
|
|
return -1;
|
|
}
|
|
break;
|
|
|
|
case VIR_DOMAIN_SMARTCARD_TYPE_HOST_CERTIFICATES:
|
|
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_CCID_EMULATED)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("this QEMU binary lacks smartcard host "
|
|
"mode support"));
|
|
return -1;
|
|
}
|
|
break;
|
|
|
|
case VIR_DOMAIN_SMARTCARD_TYPE_PASSTHROUGH:
|
|
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_CCID_PASSTHRU)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("this QEMU binary lacks smartcard "
|
|
"passthrough mode support"));
|
|
return -1;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
virReportEnumRangeError(virDomainSmartcardType, def->type);
|
|
return -1;
|
|
}
|
|
|
|
if (def->type == VIR_DOMAIN_SMARTCARD_TYPE_PASSTHROUGH &&
|
|
qemuValidateDomainChrSourceDef(def->data.passthru, qemuCaps) < 0)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
qemuValidateDomainRNGDef(const virDomainRNGDef *def,
|
|
virQEMUCaps *qemuCaps)
|
|
{
|
|
virDomainCapsDeviceRNG rngCaps = { 0 };
|
|
|
|
switch ((virDomainRNGBackend) def->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"));
|
|
return -1;
|
|
}
|
|
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"));
|
|
return -1;
|
|
}
|
|
|
|
if (qemuValidateDomainChrSourceDef(def->source.chardev, qemuCaps) < 0)
|
|
return -1;
|
|
|
|
break;
|
|
|
|
case VIR_DOMAIN_RNG_BACKEND_BUILTIN:
|
|
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_OBJECT_RNG_BUILTIN)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("this qemu doesn't support the rng-builtin "
|
|
"backend"));
|
|
return -1;
|
|
}
|
|
break;
|
|
|
|
case VIR_DOMAIN_RNG_BACKEND_LAST:
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("unknown rng-random backend"));
|
|
return -1;
|
|
}
|
|
|
|
virQEMUCapsFillDomainDeviceRNGCaps(qemuCaps, &rngCaps);
|
|
|
|
if (!VIR_DOMAIN_CAPS_ENUM_IS_SET(rngCaps.model, def->model)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("domain configuration does not support rng model '%s'"),
|
|
virDomainRNGModelTypeToString(def->model));
|
|
return -1;
|
|
}
|
|
|
|
if (qemuValidateDomainVirtioOptions(def->virtio, qemuCaps) < 0)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
qemuValidateDomainRedirdevDef(const virDomainRedirdevDef *dev,
|
|
const virDomainDef *def,
|
|
virQEMUCaps *qemuCaps)
|
|
{
|
|
if (qemuValidateDomainChrSourceDef(dev->source, qemuCaps) < 0)
|
|
return -1;
|
|
|
|
if (dev->bus != VIR_DOMAIN_REDIRDEV_BUS_USB) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("Redirection bus %s is not supported by QEMU"),
|
|
virDomainRedirdevBusTypeToString(dev->bus));
|
|
return -1;
|
|
}
|
|
|
|
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_USB_REDIR)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("USB redirection is not supported "
|
|
"by this version of QEMU"));
|
|
return -1;
|
|
}
|
|
|
|
if (def->redirfilter && def->redirfilter->nusbdevs &&
|
|
!virQEMUCapsGet(qemuCaps, QEMU_CAPS_USB_REDIR_FILTER)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("USB redirection filter is not "
|
|
"supported by this version of QEMU"));
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
qemuValidateDomainWatchdogDef(const virDomainWatchdogDef *dev,
|
|
const virDomainDef *def)
|
|
{
|
|
switch ((virDomainWatchdogModel) dev->model) {
|
|
case VIR_DOMAIN_WATCHDOG_MODEL_I6300ESB:
|
|
if (dev->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE &&
|
|
dev->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("%s model of watchdog can go only on PCI bus"),
|
|
virDomainWatchdogModelTypeToString(dev->model));
|
|
return -1;
|
|
}
|
|
break;
|
|
|
|
case VIR_DOMAIN_WATCHDOG_MODEL_IB700:
|
|
if (dev->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("%s model of watchdog does not support configuring the address"),
|
|
virDomainWatchdogModelTypeToString(dev->model));
|
|
return -1;
|
|
}
|
|
break;
|
|
|
|
case VIR_DOMAIN_WATCHDOG_MODEL_DIAG288:
|
|
if (dev->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("%s model of watchdog is virtual and cannot go on any bus."),
|
|
virDomainWatchdogModelTypeToString(dev->model));
|
|
return -1;
|
|
}
|
|
if (!(ARCH_IS_S390(def->os.arch))) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("%s model of watchdog is allowed for s390 and s390x only"),
|
|
virDomainWatchdogModelTypeToString(dev->model));
|
|
return -1;
|
|
}
|
|
break;
|
|
|
|
case VIR_DOMAIN_WATCHDOG_MODEL_LAST:
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
qemuValidateDomainMdevDefVFIOPCI(const virDomainHostdevDef *hostdev,
|
|
const virDomainDef *def,
|
|
virQEMUCaps *qemuCaps)
|
|
{
|
|
const virDomainHostdevSubsysMediatedDev *dev;
|
|
|
|
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;
|
|
}
|
|
|
|
/* VFIO-PCI does not support boot */
|
|
if (hostdev->info->bootIndex) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("booting from assigned devices is not "
|
|
"supported by mediated devices of "
|
|
"model vfio-pci"));
|
|
return -1;
|
|
}
|
|
|
|
dev = &hostdev->source.subsys.u.mdev;
|
|
if (dev->display == VIR_TRISTATE_SWITCH_ABSENT)
|
|
return 0;
|
|
|
|
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_VFIO_PCI_DISPLAY)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("display property of device vfio-pci is "
|
|
"not supported by this version of QEMU"));
|
|
return -1;
|
|
}
|
|
|
|
if (dev->model != VIR_MDEV_MODEL_TYPE_VFIO_PCI) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("<hostdev> attribute 'display' is only supported"
|
|
" with model='vfio-pci'"));
|
|
|
|
return -1;
|
|
}
|
|
|
|
if (dev->display == VIR_TRISTATE_SWITCH_ON) {
|
|
if (def->ngraphics == 0) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("graphics device is needed for attribute value "
|
|
"'display=on' in <hostdev>"));
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
qemuValidateDomainMdevDefVFIOAP(const virDomainHostdevDef *hostdev,
|
|
const virDomainDef *def,
|
|
virQEMUCaps *qemuCaps)
|
|
{
|
|
size_t i;
|
|
bool vfioap_found = false;
|
|
|
|
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_VFIO_AP)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("VFIO AP device assignment is not "
|
|
"supported by this version of QEMU"));
|
|
return -1;
|
|
}
|
|
|
|
/* VFIO-AP does not support boot */
|
|
if (hostdev->info->bootIndex) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("booting from assigned devices is not "
|
|
"supported by mediated devices of "
|
|
"model vfio-ap"));
|
|
return -1;
|
|
}
|
|
|
|
/* VFIO-AP is restricted to a single mediated device only */
|
|
for (i = 0; i < def->nhostdevs; i++) {
|
|
virDomainHostdevDef *hdev = def->hostdevs[i];
|
|
|
|
if (virHostdevIsMdevDevice(hdev) &&
|
|
hdev->source.subsys.u.mdev.model == VIR_MDEV_MODEL_TYPE_VFIO_AP) {
|
|
if (vfioap_found) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("Only one hostdev of model vfio-ap is "
|
|
"supported"));
|
|
return -1;
|
|
}
|
|
vfioap_found = true;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
qemuValidateDomainMdevDef(const virDomainHostdevDef *hostdev,
|
|
const virDomainDef *def,
|
|
virQEMUCaps *qemuCaps)
|
|
{
|
|
const virDomainHostdevSubsysMediatedDev *mdevsrc;
|
|
|
|
mdevsrc = &hostdev->source.subsys.u.mdev;
|
|
switch ((virMediatedDeviceModelType) mdevsrc->model) {
|
|
case VIR_MDEV_MODEL_TYPE_VFIO_PCI:
|
|
return qemuValidateDomainMdevDefVFIOPCI(hostdev, def, qemuCaps);
|
|
case VIR_MDEV_MODEL_TYPE_VFIO_AP:
|
|
return qemuValidateDomainMdevDefVFIOAP(hostdev, def, qemuCaps);
|
|
case VIR_MDEV_MODEL_TYPE_VFIO_CCW:
|
|
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_VFIO_CCW)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("VFIO CCW device assignment is not "
|
|
"supported by this version of QEMU"));
|
|
return -1;
|
|
}
|
|
break;
|
|
case VIR_MDEV_MODEL_TYPE_LAST:
|
|
default:
|
|
virReportEnumRangeError(virMediatedDeviceModelType,
|
|
mdevsrc->model);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
qemuValidateDomainDeviceDefHostdev(const virDomainHostdevDef *hostdev,
|
|
const virDomainDef *def,
|
|
virQEMUCaps *qemuCaps)
|
|
{
|
|
int backend;
|
|
|
|
/* forbid capabilities mode hostdev in this kind of hypervisor */
|
|
if (hostdev->mode == VIR_DOMAIN_HOSTDEV_MODE_CAPABILITIES) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("hostdev mode 'capabilities' is not "
|
|
"supported in %s"),
|
|
virDomainVirtTypeToString(def->virtType));
|
|
return -1;
|
|
}
|
|
|
|
if (hostdev->mode == VIR_DOMAIN_HOSTDEV_MODE_SUBSYS) {
|
|
switch ((virDomainHostdevSubsysType) hostdev->source.subsys.type) {
|
|
case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_USB:
|
|
case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI:
|
|
break;
|
|
|
|
case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_PCI:
|
|
backend = hostdev->source.subsys.u.pci.backend;
|
|
|
|
if (backend == VIR_DOMAIN_HOSTDEV_PCI_BACKEND_VFIO) {
|
|
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_VFIO_PCI)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("VFIO PCI device assignment is not "
|
|
"supported by this version of qemu"));
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if (hostdev->writeFiltering != VIR_TRISTATE_BOOL_ABSENT) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("Write filtering of PCI device configuration "
|
|
"space is not supported by qemu"));
|
|
return -1;
|
|
}
|
|
break;
|
|
|
|
case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI_HOST:
|
|
if (hostdev->info->bootIndex) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("booting from assigned devices is not "
|
|
"supported by vhost SCSI devices"));
|
|
return -1;
|
|
}
|
|
|
|
if (hostdev->source.subsys.u.scsi_host.protocol ==
|
|
VIR_DOMAIN_HOSTDEV_SUBSYS_SCSI_HOST_PROTOCOL_TYPE_VHOST &&
|
|
!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_VHOST_SCSI)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("This QEMU doesn't support vhost-scsi devices"));
|
|
return -1;
|
|
}
|
|
break;
|
|
case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_MDEV:
|
|
return qemuValidateDomainMdevDef(hostdev, def, qemuCaps);
|
|
case VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_LAST:
|
|
default:
|
|
virReportEnumRangeError(virDomainHostdevSubsysType,
|
|
hostdev->source.subsys.type);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
qemuValidateDomainDeviceDefVideo(const virDomainVideoDef *video,
|
|
virQEMUCaps *qemuCaps)
|
|
{
|
|
virDomainCapsDeviceVideo videoCaps = { 0 };
|
|
|
|
/* there's no properties to validate for NONE video devices */
|
|
if (video->type == VIR_DOMAIN_VIDEO_TYPE_NONE)
|
|
return 0;
|
|
|
|
virQEMUCapsFillDomainDeviceVideoCaps(qemuCaps, &videoCaps);
|
|
|
|
if (!VIR_DOMAIN_CAPS_ENUM_IS_SET(videoCaps.modelType, video->type)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("domain configuration does not support video model '%s'"),
|
|
virDomainVideoTypeToString(video->type));
|
|
return -1;
|
|
}
|
|
|
|
if (!video->primary &&
|
|
video->type != VIR_DOMAIN_VIDEO_TYPE_QXL &&
|
|
video->type != VIR_DOMAIN_VIDEO_TYPE_VIRTIO) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("video type '%s' is only valid as primary "
|
|
"video device"),
|
|
virDomainVideoTypeToString(video->type));
|
|
return -1;
|
|
}
|
|
|
|
if (video->accel && video->accel->accel2d == VIR_TRISTATE_SWITCH_ON) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("qemu does not support the accel2d setting"));
|
|
return -1;
|
|
}
|
|
|
|
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);
|
|
return -1;
|
|
}
|
|
if (video->ram > (UINT_MAX / 1024)) {
|
|
virReportError(VIR_ERR_OVERFLOW,
|
|
_("value for 'ram' must be less than '%u'"),
|
|
UINT_MAX / 1024);
|
|
return -1;
|
|
}
|
|
if (video->vgamem) {
|
|
if (video->vgamem < 1024) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("value for 'vgamem' must be at least 1 MiB "
|
|
"(1024 KiB)"));
|
|
return -1;
|
|
}
|
|
|
|
if (video->vgamem != VIR_ROUND_UP_POWER_OF_TWO(video->vgamem)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("value for 'vgamem' must be power of two"));
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (video->type != VIR_DOMAIN_VIDEO_TYPE_VGA &&
|
|
video->type != VIR_DOMAIN_VIDEO_TYPE_QXL &&
|
|
video->type != VIR_DOMAIN_VIDEO_TYPE_VIRTIO &&
|
|
video->type != VIR_DOMAIN_VIDEO_TYPE_BOCHS) {
|
|
if (video->res) {
|
|
virReportError(VIR_ERR_XML_ERROR, "%s",
|
|
_("model resolution is not supported"));
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if (video->type == VIR_DOMAIN_VIDEO_TYPE_VGA ||
|
|
video->type == VIR_DOMAIN_VIDEO_TYPE_VMVGA) {
|
|
if (video->vram && video->vram < 1024) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
"%s", _("value for 'vram' must be at least "
|
|
"1 MiB (1024 KiB)"));
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if (video->backend == VIR_DOMAIN_VIDEO_BACKEND_TYPE_VHOSTUSER) {
|
|
if (video->type == VIR_DOMAIN_VIDEO_TYPE_VIRTIO &&
|
|
!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_VHOST_USER_GPU)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("this QEMU does not support 'vhost-user' video device"));
|
|
return -1;
|
|
}
|
|
} else if (video->accel) {
|
|
if (video->accel->accel3d == VIR_TRISTATE_SWITCH_ON &&
|
|
(video->type != VIR_DOMAIN_VIDEO_TYPE_VIRTIO ||
|
|
!(virQEMUCapsGet(qemuCaps, QEMU_CAPS_VIRTIO_GPU_VIRGL) ||
|
|
virQEMUCapsGet(qemuCaps, QEMU_CAPS_VIRTIO_GPU_GL_PCI) ||
|
|
virQEMUCapsGet(qemuCaps, QEMU_CAPS_VIRTIO_VGA_GL)))) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("%s 3d acceleration is not supported"),
|
|
virDomainVideoTypeToString(video->type));
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if (qemuValidateDomainVirtioOptions(video->virtio, qemuCaps) < 0)
|
|
return -1;
|
|
|
|
if (video->type == VIR_DOMAIN_VIDEO_TYPE_RAMFB &&
|
|
video->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("'address' is not supported for 'ramfb' video devices"));
|
|
return -1;
|
|
}
|
|
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
#define QEMU_SERIAL_PARAM_ACCEPTED_CHARS \
|
|
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_ .+"
|
|
|
|
static int
|
|
qemuValidateDomainDeviceDefDiskSerial(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 bool
|
|
qemuValidateDomainDeviceDefDiskIOThreads(const virDomainDef *def,
|
|
const virDomainDiskDef *disk)
|
|
{
|
|
switch ((virDomainDiskBus)disk->bus) {
|
|
case VIR_DOMAIN_DISK_BUS_VIRTIO:
|
|
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_NONE:
|
|
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;
|
|
}
|
|
|
|
|
|
static int
|
|
qemuValidateDomainDeviceDefDiskFrontend(const virDomainDiskDef *disk,
|
|
const virDomainDef *def,
|
|
virQEMUCaps *qemuCaps)
|
|
{
|
|
virDomainDeviceInfo *diskInfo;
|
|
int cModel;
|
|
|
|
if (disk->geometry.cylinders > 0 &&
|
|
disk->geometry.heads > 0 &&
|
|
disk->geometry.sectors > 0) {
|
|
if (disk->bus == VIR_DOMAIN_DISK_BUS_USB ||
|
|
disk->bus == VIR_DOMAIN_DISK_BUS_SD) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("CHS geometry can not be set for '%s' bus"),
|
|
virDomainDiskBusTypeToString(disk->bus));
|
|
return -1;
|
|
}
|
|
|
|
if (disk->geometry.trans != VIR_DOMAIN_DISK_TRANS_DEFAULT &&
|
|
disk->bus != VIR_DOMAIN_DISK_BUS_IDE) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("CHS translation mode can only be set for 'ide' bus not '%s'"),
|
|
virDomainDiskBusTypeToString(disk->bus));
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if (disk->serial && disk->bus == VIR_DOMAIN_DISK_BUS_SD) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Serial property not supported for drive bus '%s'"),
|
|
virDomainDiskBusTypeToString(disk->bus));
|
|
return -1;
|
|
}
|
|
|
|
if (disk->device == VIR_DOMAIN_DISK_DEVICE_CDROM &&
|
|
(disk->bus == VIR_DOMAIN_DISK_BUS_VIRTIO ||
|
|
disk->bus == VIR_DOMAIN_DISK_BUS_SD)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("disk type of '%s' does not support ejectable media"),
|
|
disk->dst);
|
|
return -1;
|
|
}
|
|
|
|
if (disk->copy_on_read == VIR_TRISTATE_SWITCH_ON) {
|
|
if (disk->src->readonly) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("copy_on_read is not compatible with read-only disk '%s'"),
|
|
disk->dst);
|
|
return -1;
|
|
}
|
|
|
|
if (disk->device == VIR_DOMAIN_DISK_DEVICE_CDROM ||
|
|
disk->device == VIR_DOMAIN_DISK_DEVICE_FLOPPY) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("copy_on_read is not supported with removable disk '%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->bus == VIR_DOMAIN_DISK_BUS_IDE &&
|
|
!virQEMUCapsGet(qemuCaps, QEMU_CAPS_IDE_DRIVE_WWN)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("Setting wwn for ide disk is not supported "
|
|
"by this QEMU"));
|
|
return -1;
|
|
}
|
|
|
|
if (disk->bus != VIR_DOMAIN_DISK_BUS_SCSI &&
|
|
!virQEMUCapsGet(qemuCaps, QEMU_CAPS_SCSI_DISK_WWN)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("Setting wwn for scsi disk is not supported "
|
|
"by this QEMU"));
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if (disk->vendor || disk->product) {
|
|
if (disk->bus != VIR_DOMAIN_DISK_BUS_SCSI) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("Only scsi disk supports vendor and product"));
|
|
return -1;
|
|
}
|
|
|
|
/* Properties wwn, vendor and product were introduced in the
|
|
* same QEMU release (1.2.0).
|
|
*/
|
|
if (!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"));
|
|
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'"),
|
|
virDomainDiskBusTypeToString(disk->bus));
|
|
return -1;
|
|
}
|
|
|
|
if (disk->bus == VIR_DOMAIN_DISK_BUS_SCSI &&
|
|
!virQEMUCapsGet(qemuCaps, QEMU_CAPS_SCSI_BLOCK)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("This QEMU doesn't support scsi-block for "
|
|
"lun passthrough"));
|
|
return -1;
|
|
}
|
|
|
|
|
|
if (disk->copy_on_read == VIR_TRISTATE_SWITCH_ON) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("copy_on_read is not compatible with 'lun' disk '%s'"),
|
|
disk->dst);
|
|
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;
|
|
}
|
|
}
|
|
|
|
if (disk->rotation_rate) {
|
|
if (disk->bus != VIR_DOMAIN_DISK_BUS_SCSI &&
|
|
disk->bus != VIR_DOMAIN_DISK_BUS_IDE &&
|
|
disk->bus != VIR_DOMAIN_DISK_BUS_SATA) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("rotation rate is only valid for SCSI/IDE/SATA bus"));
|
|
return -1;
|
|
}
|
|
|
|
if (disk->device != VIR_DOMAIN_DISK_DEVICE_DISK) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("rotation rate is only valid for disk device"));
|
|
return -1;
|
|
}
|
|
|
|
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_ROTATION_RATE)) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("rotation rate is not supported with this QEMU"));
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
switch (disk->bus) {
|
|
case VIR_DOMAIN_DISK_BUS_SCSI:
|
|
diskInfo = (virDomainDeviceInfo *)&disk->info;
|
|
cModel = qemuDomainFindSCSIControllerModel(def, diskInfo);
|
|
|
|
if (disk->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DRIVE) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("unexpected address type for scsi disk"));
|
|
return -1;
|
|
}
|
|
|
|
/* 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"));
|
|
return -1;
|
|
}
|
|
|
|
/* We allow hotplug/hotunplug disks without a controller,
|
|
* hence we don't error out if cModel is < 0. These
|
|
* validations were originally made under the assumption of
|
|
* a controller being found though. */
|
|
if (cModel > 0) {
|
|
if (cModel == 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'"));
|
|
return -1;
|
|
}
|
|
} 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"));
|
|
return -1;
|
|
}
|
|
|
|
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"));
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
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"));
|
|
return -1;
|
|
}
|
|
/* We can only have 1 IDE controller (currently) */
|
|
if (disk->info.addr.drive.controller != 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("Only 1 IDE controller is supported"));
|
|
return -1;
|
|
}
|
|
if (disk->info.addr.drive.target != 0) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("target must be 0 for ide controller"));
|
|
return -1;
|
|
}
|
|
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"));
|
|
return -1;
|
|
}
|
|
/* We can only have 1 FDC controller (currently) */
|
|
if (disk->info.addr.drive.controller != 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("Only 1 fdc controller is supported"));
|
|
return -1;
|
|
}
|
|
/* We can only have 1 FDC bus (currently) */
|
|
if (disk->info.addr.drive.bus != 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("Only 1 fdc bus is supported"));
|
|
return -1;
|
|
}
|
|
if (disk->info.addr.drive.target != 0) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("target must be 0 for controller fdc"));
|
|
return -1;
|
|
}
|
|
break;
|
|
|
|
case VIR_DOMAIN_DISK_BUS_SATA:
|
|
if (disk->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_DRIVE) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("unexpected address type for sata disk"));
|
|
return -1;
|
|
}
|
|
|
|
if (disk->info.addr.drive.bus != 0) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("bus must be 0 for sata controller"));
|
|
return -1;
|
|
}
|
|
if (disk->info.addr.drive.target != 0) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("target must be 0 for sata controller"));
|
|
return -1;
|
|
}
|
|
break;
|
|
|
|
case VIR_DOMAIN_DISK_BUS_VIRTIO:
|
|
if (disk->queues &&
|
|
!virQEMUCapsGet(qemuCaps, QEMU_CAPS_VIRTIO_BLK_NUM_QUEUES)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("num-queues property isn't supported by this "
|
|
"QEMU binary"));
|
|
return -1;
|
|
}
|
|
break;
|
|
|
|
case VIR_DOMAIN_DISK_BUS_USB:
|
|
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_USB_STORAGE)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("This QEMU doesn't support '-device "
|
|
"usb-storage'"));
|
|
return -1;
|
|
}
|
|
|
|
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"));
|
|
return -1;
|
|
}
|
|
|
|
if (disk->removable != VIR_TRISTATE_SWITCH_ABSENT &&
|
|
!virQEMUCapsGet(qemuCaps, QEMU_CAPS_USB_STORAGE_REMOVABLE)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("This QEMU doesn't support setting the "
|
|
"removable flag of USB storage devices"));
|
|
return -1;
|
|
}
|
|
|
|
break;
|
|
|
|
case VIR_DOMAIN_DISK_BUS_XEN:
|
|
case VIR_DOMAIN_DISK_BUS_SD:
|
|
case VIR_DOMAIN_DISK_BUS_NONE:
|
|
case VIR_DOMAIN_DISK_BUS_UML:
|
|
case VIR_DOMAIN_DISK_BUS_LAST:
|
|
break;
|
|
}
|
|
|
|
if (disk->src->readonly &&
|
|
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"));
|
|
return -1;
|
|
}
|
|
|
|
if (disk->bus == VIR_DOMAIN_DISK_BUS_SATA) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("readonly sata disks are not supported"));
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
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"));
|
|
return -1;
|
|
}
|
|
|
|
if (disk->serial &&
|
|
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"));
|
|
return -1;
|
|
}
|
|
|
|
if (disk->discard &&
|
|
!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DRIVE_DISCARD)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("discard is not supported by this QEMU binary"));
|
|
return -1;
|
|
}
|
|
|
|
if (disk->detect_zeroes &&
|
|
!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DRIVE_DETECT_ZEROES)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("detect_zeroes is not supported by this QEMU binary"));
|
|
return -1;
|
|
}
|
|
|
|
if (disk->iomode == VIR_DOMAIN_DISK_IO_URING) {
|
|
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_AIO_IO_URING)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("io uring is not supported by this QEMU binary"));
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if (disk->serial &&
|
|
qemuValidateDomainDeviceDefDiskSerial(disk->serial) < 0)
|
|
return -1;
|
|
|
|
if (disk->iothread && !qemuValidateDomainDeviceDefDiskIOThreads(def, disk))
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* qemuValidateDomainDeviceDefDiskBlkdeviotune:
|
|
* @disk: disk configuration
|
|
*
|
|
* Checks whether block io tuning settings make sense. Returns -1 on error and
|
|
* reports a proper libvirt error.
|
|
*/
|
|
static int
|
|
qemuValidateDomainDeviceDefDiskBlkdeviotune(const virDomainDiskDef *disk,
|
|
const virDomainDef *def)
|
|
{
|
|
/* group_name by itself is ignored by qemu */
|
|
if (disk->blkdeviotune.group_name &&
|
|
!virDomainBlockIoTuneInfoHasAny(&disk->blkdeviotune)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("group_name can be configured only together with "
|
|
"settings"));
|
|
return -1;
|
|
}
|
|
|
|
/* checking def here is only for calling from tests */
|
|
if (disk->blkdeviotune.group_name) {
|
|
size_t i;
|
|
|
|
for (i = 0; i < def->ndisks; i++) {
|
|
virDomainDiskDef *d = def->disks[i];
|
|
|
|
if (STREQ(d->dst, disk->dst) ||
|
|
STRNEQ_NULLABLE(d->blkdeviotune.group_name,
|
|
disk->blkdeviotune.group_name))
|
|
continue;
|
|
|
|
if (!virDomainBlockIoTuneInfoEqual(&d->blkdeviotune,
|
|
&disk->blkdeviotune)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("different iotunes for disks %s and %s"),
|
|
disk->dst, d->dst);
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
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);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
qemuValidateDomainDeviceDefDiskTransient(const virDomainDiskDef *disk,
|
|
virQEMUCaps *qemuCaps)
|
|
|
|
{
|
|
virStorageType actualType = virStorageSourceGetActualType(disk->src);
|
|
|
|
if (!disk->transient)
|
|
return 0;
|
|
|
|
if (virStorageSourceIsEmpty(disk->src)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("transient disk '%s' must not be empty"), disk->dst);
|
|
return -1;
|
|
}
|
|
|
|
if (disk->src->readonly) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("transient disk '%s' must not be read-only"), disk->dst);
|
|
return -1;
|
|
}
|
|
|
|
if (actualType != VIR_STORAGE_TYPE_FILE) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("transient disk supported only with 'file' type (%s)"),
|
|
disk->dst);
|
|
return -1;
|
|
}
|
|
|
|
if (disk->device != VIR_DOMAIN_DISK_DEVICE_DISK) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("transient disk supported only with 'disk' device (%s)"),
|
|
disk->dst);
|
|
return -1;
|
|
}
|
|
|
|
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_BLOCKDEV)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("transient disk not supported by this QEMU binary (%s)"),
|
|
disk->dst);
|
|
return -1;
|
|
}
|
|
|
|
if (disk->transientShareBacking == VIR_TRISTATE_BOOL_YES) {
|
|
/* sharing the backing file requires hotplug of the disk in the qemu driver */
|
|
switch (disk->bus) {
|
|
case VIR_DOMAIN_DISK_BUS_USB:
|
|
case VIR_DOMAIN_DISK_BUS_VIRTIO:
|
|
case VIR_DOMAIN_DISK_BUS_SCSI:
|
|
break;
|
|
|
|
case VIR_DOMAIN_DISK_BUS_IDE:
|
|
case VIR_DOMAIN_DISK_BUS_FDC:
|
|
case VIR_DOMAIN_DISK_BUS_XEN:
|
|
case VIR_DOMAIN_DISK_BUS_UML:
|
|
case VIR_DOMAIN_DISK_BUS_SATA:
|
|
case VIR_DOMAIN_DISK_BUS_SD:
|
|
case VIR_DOMAIN_DISK_BUS_NONE:
|
|
case VIR_DOMAIN_DISK_BUS_LAST:
|
|
default:
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("disk bus '%s' doesn't support transiend disk backing image sharing"),
|
|
virDomainDiskBusTypeToString(disk->bus));
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int
|
|
qemuValidateDomainDeviceDefDisk(const virDomainDiskDef *disk,
|
|
const virDomainDef *def,
|
|
virQEMUCaps *qemuCaps)
|
|
{
|
|
const char *driverName = virDomainDiskGetDriver(disk);
|
|
bool isSD = qemuDiskBusIsSD(disk->bus);
|
|
virStorageSource *n;
|
|
int idx;
|
|
int partition;
|
|
|
|
if (qemuValidateDomainDeviceDefDiskFrontend(disk, def, qemuCaps) < 0)
|
|
return -1;
|
|
|
|
if (qemuValidateDomainDeviceDefDiskBlkdeviotune(disk, def) < 0)
|
|
return -1;
|
|
|
|
if (qemuValidateDomainDeviceDefDiskTransient(disk, qemuCaps) < 0)
|
|
return -1;
|
|
|
|
if (disk->src->shared && !disk->src->readonly &&
|
|
!qemuBlockStorageSourceSupportsConcurrentAccess(disk->src)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("shared access for disk '%s' requires use of "
|
|
"supported storage format"), disk->dst);
|
|
return -1;
|
|
}
|
|
|
|
if (driverName && STRNEQ(driverName, "qemu")) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("unsupported driver name '%s' for disk '%s'"),
|
|
driverName, disk->dst);
|
|
return -1;
|
|
}
|
|
|
|
if (virDiskNameParse(disk->dst, &idx, &partition) < 0) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("invalid disk target '%s'"), disk->dst);
|
|
return -1;
|
|
}
|
|
|
|
if (partition != 0) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("invalid disk target '%s', partitions can't appear in disk targets"),
|
|
disk->dst);
|
|
return -1;
|
|
}
|
|
|
|
for (n = disk->src; virStorageSourceIsBacking(n); n = n->backingStore) {
|
|
/* blockdev support is masked out for 'sd' disks */
|
|
if (qemuDomainValidateStorageSource(n, qemuCaps, isSD) < 0)
|
|
return -1;
|
|
}
|
|
|
|
if (disk->bus == VIR_DOMAIN_DISK_BUS_VIRTIO &&
|
|
qemuValidateDomainVirtioOptions(disk->virtio, qemuCaps) < 0) {
|
|
return -1;
|
|
}
|
|
|
|
if (disk->src->type == VIR_STORAGE_TYPE_VHOST_USER) {
|
|
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_VHOST_USER_BLK)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("vhostuser disk is not supported with this QEMU binary"));
|
|
return -1;
|
|
}
|
|
|
|
if (qemuValidateDomainDefVhostUserRequireSharedMemory(def, "vhostuser",
|
|
qemuCaps) < 0) {
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* @qemuCaps: QEMU capabilities
|
|
* @model: SCSI model to check
|
|
*
|
|
* Using the @qemuCaps, let's ensure the provided @model can be supported
|
|
*
|
|
* Returns true if acceptable, false otherwise with error message set.
|
|
*/
|
|
static bool
|
|
qemuValidateCheckSCSIControllerModel(virQEMUCaps *qemuCaps,
|
|
int model)
|
|
{
|
|
switch ((virDomainControllerModelSCSI) model) {
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_SCSI_LSILOGIC:
|
|
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_SCSI_LSI)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("This QEMU doesn't support "
|
|
"the LSI 53C895A SCSI controller"));
|
|
return false;
|
|
}
|
|
break;
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_SCSI_VIRTIO_SCSI:
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_SCSI_VIRTIO_TRANSITIONAL:
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_SCSI_VIRTIO_NON_TRANSITIONAL:
|
|
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_VIRTIO_SCSI)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("This QEMU doesn't support "
|
|
"virtio scsi controller"));
|
|
return false;
|
|
}
|
|
break;
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_SCSI_IBMVSCSI:
|
|
/*TODO: need checking work here if necessary */
|
|
break;
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_SCSI_LSISAS1068:
|
|
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_SCSI_MPTSAS1068)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("This QEMU doesn't support "
|
|
"the LSI SAS1068 (MPT Fusion) controller"));
|
|
return false;
|
|
}
|
|
break;
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_SCSI_LSISAS1078:
|
|
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_SCSI_MEGASAS)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("This QEMU doesn't support "
|
|
"the LSI SAS1078 (MegaRAID) controller"));
|
|
return false;
|
|
}
|
|
break;
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_SCSI_VMPVSCSI:
|
|
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_SCSI_PVSCSI)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("This QEMU doesn't support "
|
|
"the pvscsi (VMware paravirtual SCSI) controller"));
|
|
return false;
|
|
}
|
|
break;
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_SCSI_AUTO:
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_SCSI_BUSLOGIC:
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("Unsupported controller model: %s"),
|
|
virDomainControllerModelSCSITypeToString(model));
|
|
return false;
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_SCSI_NCR53C90:
|
|
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_SCSI_NCR53C90)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("This QEMU doesn't support "
|
|
"the NCR53C90 (ESP) controller"));
|
|
}
|
|
return true;
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_SCSI_DC390:
|
|
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_SCSI_DC390)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("This QEMU doesn't support "
|
|
"the DC390 (ESP) controller"));
|
|
}
|
|
return true;
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_SCSI_AM53C974:
|
|
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_SCSI_AM53C974)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("This QEMU doesn't support "
|
|
"the AM53C974 (ESP) controller"));
|
|
}
|
|
return true;
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_SCSI_DEFAULT:
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_SCSI_LAST:
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Unexpected SCSI controller model %d"),
|
|
model);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
qemuValidateDomainDeviceDefControllerSATA(const virDomainControllerDef *controller,
|
|
const virDomainDef *def,
|
|
virQEMUCaps *qemuCaps)
|
|
{
|
|
/* first SATA controller on Q35 machines is implicit */
|
|
if (controller->idx == 0 && qemuDomainIsQ35(def))
|
|
return 0;
|
|
|
|
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_ICH9_AHCI)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("SATA is not supported with this QEMU binary"));
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
qemuValidateDomainDeviceDefControllerIDE(const virDomainControllerDef *controller,
|
|
const virDomainDef *def)
|
|
{
|
|
/* first IDE controller is implicit on various machines */
|
|
if (controller->idx == 0 && qemuDomainHasBuiltinIDE(def))
|
|
return 0;
|
|
|
|
/* 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 controller had multiple
|
|
* IDE controllers specified.
|
|
*/
|
|
if (qemuDomainHasBuiltinIDE(def))
|
|
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"));
|
|
return -1;
|
|
}
|
|
|
|
|
|
/* qemuValidateCheckSCSIControllerIOThreads:
|
|
* @controller: Pointer to controller def
|
|
* @def: Pointer to domain def
|
|
*
|
|
* 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
|
|
qemuValidateCheckSCSIControllerIOThreads(const virDomainControllerDef *controller,
|
|
const virDomainDef *def)
|
|
{
|
|
if (!controller->iothread)
|
|
return true;
|
|
|
|
if (controller->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE &&
|
|
controller->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_PCI &&
|
|
controller->info.type != VIR_DOMAIN_DEVICE_ADDRESS_TYPE_CCW) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("virtio-scsi IOThreads only available for virtio "
|
|
"pci and virtio ccw controllers"));
|
|
return false;
|
|
}
|
|
|
|
/* Can we find the controller iothread in the iothreadid list? */
|
|
if (!virDomainIOThreadIDFind(def, controller->iothread)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("controller iothread '%u' not defined in iothreadid"),
|
|
controller->iothread);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
static int
|
|
qemuValidateDomainDeviceDefControllerSCSI(const virDomainControllerDef *controller,
|
|
const virDomainDef *def)
|
|
{
|
|
switch ((virDomainControllerModelSCSI) controller->model) {
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_SCSI_VIRTIO_SCSI:
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_SCSI_VIRTIO_TRANSITIONAL:
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_SCSI_VIRTIO_NON_TRANSITIONAL:
|
|
if (!qemuValidateCheckSCSIControllerIOThreads(controller, def))
|
|
return -1;
|
|
break;
|
|
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_SCSI_NCR53C90:
|
|
if (controller->idx != 0) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("ncr53c90 can only be used as first SCSI controller"));
|
|
return -1;
|
|
}
|
|
if (!qemuDomainHasBuiltinESP(def)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("ncr53c90 SCSI controller is not a built-in for this machine"));
|
|
return -1;
|
|
}
|
|
break;
|
|
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_SCSI_AUTO:
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_SCSI_BUSLOGIC:
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_SCSI_LSILOGIC:
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_SCSI_LSISAS1068:
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_SCSI_VMPVSCSI:
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_SCSI_IBMVSCSI:
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_SCSI_LSISAS1078:
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_SCSI_DC390:
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_SCSI_AM53C974:
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_SCSI_DEFAULT:
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_SCSI_LAST:
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* virValidateControllerPCIModelNameToQEMUCaps:
|
|
* @modelName: model name
|
|
*
|
|
* Maps model names for PCI controllers (virDomainControllerPCIModelName)
|
|
* to the QEMU capabilities required to use them (virQEMUCapsFlags).
|
|
*
|
|
* Returns: the QEMU capability itself (>0) on success; 0 if no QEMU
|
|
* capability is needed; <0 on error.
|
|
*/
|
|
static int
|
|
virValidateControllerPCIModelNameToQEMUCaps(int modelName)
|
|
{
|
|
switch ((virDomainControllerPCIModelName) modelName) {
|
|
case VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_PCI_BRIDGE:
|
|
return QEMU_CAPS_DEVICE_PCI_BRIDGE;
|
|
case VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_I82801B11_BRIDGE:
|
|
return QEMU_CAPS_DEVICE_DMI_TO_PCI_BRIDGE;
|
|
case VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_IOH3420:
|
|
return QEMU_CAPS_DEVICE_IOH3420;
|
|
case VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_X3130_UPSTREAM:
|
|
return QEMU_CAPS_DEVICE_X3130_UPSTREAM;
|
|
case VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_XIO3130_DOWNSTREAM:
|
|
return QEMU_CAPS_DEVICE_XIO3130_DOWNSTREAM;
|
|
case VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_PXB:
|
|
return QEMU_CAPS_DEVICE_PXB;
|
|
case VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_PXB_PCIE:
|
|
return QEMU_CAPS_DEVICE_PXB_PCIE;
|
|
case VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_PCIE_ROOT_PORT:
|
|
return QEMU_CAPS_DEVICE_PCIE_ROOT_PORT;
|
|
case VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_SPAPR_PCI_HOST_BRIDGE:
|
|
return QEMU_CAPS_DEVICE_SPAPR_PCI_HOST_BRIDGE;
|
|
case VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_PCIE_PCI_BRIDGE:
|
|
return QEMU_CAPS_DEVICE_PCIE_PCI_BRIDGE;
|
|
case VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_NONE:
|
|
return 0;
|
|
case VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_LAST:
|
|
default:
|
|
return -1;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
|
|
static int
|
|
qemuValidateDomainDeviceDefControllerAttributes(const virDomainControllerDef *controller,
|
|
virQEMUCaps *qemuCaps)
|
|
{
|
|
if (!(controller->type == VIR_DOMAIN_CONTROLLER_TYPE_SCSI &&
|
|
(controller->model == VIR_DOMAIN_CONTROLLER_MODEL_SCSI_VIRTIO_SCSI ||
|
|
controller->model == VIR_DOMAIN_CONTROLLER_MODEL_SCSI_VIRTIO_TRANSITIONAL ||
|
|
controller->model == VIR_DOMAIN_CONTROLLER_MODEL_SCSI_VIRTIO_NON_TRANSITIONAL))) {
|
|
if (controller->queues) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("'queues' is only supported by virtio-scsi controller"));
|
|
return -1;
|
|
}
|
|
if (controller->cmd_per_lun) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("'cmd_per_lun' is only supported by virtio-scsi controller"));
|
|
return -1;
|
|
}
|
|
if (controller->max_sectors) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("'max_sectors' is only supported by virtio-scsi controller"));
|
|
return -1;
|
|
}
|
|
if (controller->ioeventfd) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("'ioeventfd' is only supported by virtio-scsi controller"));
|
|
return -1;
|
|
}
|
|
if (controller->iothread) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("'iothread' is only supported for virtio-scsi controller"));
|
|
return -1;
|
|
}
|
|
if (qemuValidateDomainVirtioOptions(controller->virtio, qemuCaps) < 0)
|
|
return -1;
|
|
}
|
|
|
|
if (controller->type == VIR_DOMAIN_CONTROLLER_TYPE_VIRTIO_SERIAL &&
|
|
qemuValidateDomainVirtioOptions(controller->virtio, qemuCaps) < 0) {
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
#define virReportControllerMissingOption(cont, model, modelName, option) \
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, \
|
|
_("Required option '%s' is not set for PCI controller " \
|
|
"with index '%d', model '%s' and modelName '%s'"), \
|
|
(option), (cont->idx), (model), (modelName));
|
|
#define virReportControllerInvalidOption(cont, model, modelName, option) \
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, \
|
|
_("Option '%s' is not valid for PCI controller " \
|
|
"with index '%d', model '%s' and modelName '%s'"), \
|
|
(option), (cont->idx), (model), (modelName));
|
|
#define virReportControllerInvalidValue(cont, model, modelName, option) \
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, \
|
|
_("Option '%s' has invalid value for PCI controller " \
|
|
"with index '%d', model '%s' and modelName '%s'"), \
|
|
(option), (cont->idx), (model), (modelName));
|
|
|
|
|
|
static int
|
|
qemuValidateDomainDeviceDefControllerPCI(const virDomainControllerDef *cont,
|
|
const virDomainDef *def,
|
|
virQEMUCaps *qemuCaps)
|
|
|
|
{
|
|
const virDomainPCIControllerOpts *pciopts = &cont->opts.pciopts;
|
|
const char *model = virDomainControllerModelPCITypeToString(cont->model);
|
|
const char *modelName = virDomainControllerPCIModelNameTypeToString(pciopts->modelName);
|
|
int cap = virValidateControllerPCIModelNameToQEMUCaps(pciopts->modelName);
|
|
|
|
if (!model) {
|
|
virReportEnumRangeError(virDomainControllerModelPCI, cont->model);
|
|
return -1;
|
|
}
|
|
if (!modelName) {
|
|
virReportEnumRangeError(virDomainControllerPCIModelName, pciopts->modelName);
|
|
return -1;
|
|
}
|
|
|
|
/* modelName */
|
|
switch ((virDomainControllerModelPCI) cont->model) {
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCI_BRIDGE:
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_DMI_TO_PCI_BRIDGE:
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_ROOT_PORT:
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_SWITCH_UPSTREAM_PORT:
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_SWITCH_DOWNSTREAM_PORT:
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCI_EXPANDER_BUS:
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_EXPANDER_BUS:
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_TO_PCI_BRIDGE:
|
|
/* modelName should have been set automatically */
|
|
if (pciopts->modelName == VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_NONE) {
|
|
virReportControllerMissingOption(cont, model, modelName, "modelName");
|
|
return -1;
|
|
}
|
|
break;
|
|
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCI_ROOT:
|
|
/* modelName must be set for pSeries guests, but it's an error
|
|
* for it to be set for any other guest */
|
|
if (qemuDomainIsPSeries(def)) {
|
|
if (pciopts->modelName == VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_NONE) {
|
|
virReportControllerMissingOption(cont, model, modelName, "modelName");
|
|
return -1;
|
|
}
|
|
} else {
|
|
if (pciopts->modelName != VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_NONE) {
|
|
virReportControllerInvalidOption(cont, model, modelName, "modelName");
|
|
return -1;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_ROOT:
|
|
if (pciopts->modelName != VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_NONE) {
|
|
virReportControllerInvalidOption(cont, model, modelName, "modelName");
|
|
return -1;
|
|
}
|
|
break;
|
|
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCI_DEFAULT:
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCI_LAST:
|
|
default:
|
|
virReportEnumRangeError(virDomainControllerModelPCI, cont->model);
|
|
return -1;
|
|
}
|
|
|
|
/* modelName (cont'd) */
|
|
switch ((virDomainControllerModelPCI) cont->model) {
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCI_ROOT:
|
|
if (pciopts->modelName != VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_NONE &&
|
|
pciopts->modelName != VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_SPAPR_PCI_HOST_BRIDGE) {
|
|
virReportControllerInvalidValue(cont, model, modelName, "modelName");
|
|
return -1;
|
|
}
|
|
break;
|
|
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCI_BRIDGE:
|
|
if (pciopts->modelName != VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_PCI_BRIDGE) {
|
|
virReportControllerInvalidValue(cont, model, modelName, "modelName");
|
|
return -1;
|
|
}
|
|
break;
|
|
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_DMI_TO_PCI_BRIDGE:
|
|
if (pciopts->modelName != VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_I82801B11_BRIDGE) {
|
|
virReportControllerInvalidValue(cont, model, modelName, "modelName");
|
|
return -1;
|
|
}
|
|
break;
|
|
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_ROOT_PORT:
|
|
if (pciopts->modelName != VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_IOH3420 &&
|
|
pciopts->modelName != VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_PCIE_ROOT_PORT) {
|
|
virReportControllerInvalidValue(cont, model, modelName, "modelName");
|
|
return -1;
|
|
}
|
|
break;
|
|
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_SWITCH_UPSTREAM_PORT:
|
|
if (pciopts->modelName != VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_X3130_UPSTREAM) {
|
|
virReportControllerInvalidValue(cont, model, modelName, "modelName");
|
|
return -1;
|
|
}
|
|
break;
|
|
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_SWITCH_DOWNSTREAM_PORT:
|
|
if (pciopts->modelName != VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_XIO3130_DOWNSTREAM) {
|
|
virReportControllerInvalidValue(cont, model, modelName, "modelName");
|
|
return -1;
|
|
}
|
|
break;
|
|
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCI_EXPANDER_BUS:
|
|
if (pciopts->modelName != VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_PXB) {
|
|
virReportControllerInvalidValue(cont, model, modelName, "modelName");
|
|
return -1;
|
|
}
|
|
break;
|
|
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_EXPANDER_BUS:
|
|
if (pciopts->modelName != VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_PXB_PCIE) {
|
|
virReportControllerInvalidValue(cont, model, modelName, "modelName");
|
|
return -1;
|
|
}
|
|
break;
|
|
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_ROOT:
|
|
if (pciopts->modelName != VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_NONE) {
|
|
virReportControllerInvalidValue(cont, model, modelName, "modelName");
|
|
return -1;
|
|
}
|
|
break;
|
|
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_TO_PCI_BRIDGE:
|
|
if (pciopts->modelName != VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_PCIE_PCI_BRIDGE) {
|
|
virReportControllerInvalidValue(cont, model, modelName, "modelName");
|
|
return -1;
|
|
}
|
|
break;
|
|
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCI_DEFAULT:
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCI_LAST:
|
|
default:
|
|
virReportEnumRangeError(virDomainControllerModelPCI, cont->model);
|
|
return -1;
|
|
}
|
|
|
|
/* index */
|
|
switch ((virDomainControllerModelPCI) cont->model) {
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCI_BRIDGE:
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_DMI_TO_PCI_BRIDGE:
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_ROOT_PORT:
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_SWITCH_UPSTREAM_PORT:
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_SWITCH_DOWNSTREAM_PORT:
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCI_EXPANDER_BUS:
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_EXPANDER_BUS:
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_TO_PCI_BRIDGE:
|
|
if (cont->idx == 0) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("Index for '%s' controllers must be > 0"),
|
|
model);
|
|
return -1;
|
|
}
|
|
break;
|
|
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCI_ROOT:
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_ROOT:
|
|
/* pSeries guests can have multiple PHBs, so it's expected that
|
|
* the index will not be zero for some of them */
|
|
if (cont->model == VIR_DOMAIN_CONTROLLER_MODEL_PCI_ROOT &&
|
|
pciopts->modelName == VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_SPAPR_PCI_HOST_BRIDGE) {
|
|
break;
|
|
}
|
|
|
|
/* For all other pci-root and pcie-root controllers, though,
|
|
* the index must be zero */
|
|
if (cont->idx != 0) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("Index for '%s' controllers must be 0"),
|
|
model);
|
|
return -1;
|
|
}
|
|
break;
|
|
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCI_DEFAULT:
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCI_LAST:
|
|
default:
|
|
virReportEnumRangeError(virDomainControllerModelPCI, cont->model);
|
|
return -1;
|
|
}
|
|
|
|
/* targetIndex */
|
|
switch ((virDomainControllerModelPCI) cont->model) {
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCI_ROOT:
|
|
/* PHBs for pSeries guests must have been assigned a targetIndex */
|
|
if (pciopts->targetIndex == -1 &&
|
|
pciopts->modelName == VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_SPAPR_PCI_HOST_BRIDGE) {
|
|
virReportControllerMissingOption(cont, model, modelName, "targetIndex");
|
|
return -1;
|
|
}
|
|
|
|
/* targetIndex only applies to PHBs, so for any other pci-root
|
|
* controller it being present is an error */
|
|
if (pciopts->targetIndex != -1 &&
|
|
pciopts->modelName != VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_SPAPR_PCI_HOST_BRIDGE) {
|
|
virReportControllerInvalidOption(cont, model, modelName, "targetIndex");
|
|
return -1;
|
|
}
|
|
break;
|
|
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCI_BRIDGE:
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_DMI_TO_PCI_BRIDGE:
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_ROOT_PORT:
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_SWITCH_UPSTREAM_PORT:
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_SWITCH_DOWNSTREAM_PORT:
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCI_EXPANDER_BUS:
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_EXPANDER_BUS:
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_ROOT:
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_TO_PCI_BRIDGE:
|
|
if (pciopts->targetIndex != -1) {
|
|
virReportControllerInvalidOption(cont, model, modelName, "targetIndex");
|
|
return -1;
|
|
}
|
|
break;
|
|
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCI_DEFAULT:
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCI_LAST:
|
|
default:
|
|
virReportEnumRangeError(virDomainControllerModelPCI, cont->model);
|
|
return -1;
|
|
}
|
|
|
|
/* pcihole64 */
|
|
switch ((virDomainControllerModelPCI) cont->model) {
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCI_ROOT:
|
|
if (pciopts->pcihole64 || pciopts->pcihole64size != 0) {
|
|
if (!qemuDomainIsI440FX(def)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("Setting the 64-bit PCI hole size is not "
|
|
"supported for machine '%s'"), def->os.machine);
|
|
return -1;
|
|
}
|
|
|
|
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_I440FX_PCI_HOLE64_SIZE)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("64-bit PCI hole size setting is not supported "
|
|
"with this QEMU binary"));
|
|
return -1;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_ROOT:
|
|
if (pciopts->pcihole64 || pciopts->pcihole64size != 0) {
|
|
if (!qemuDomainIsQ35(def)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("Setting the 64-bit PCI hole size is not "
|
|
"supported for machine '%s'"), def->os.machine);
|
|
return -1;
|
|
}
|
|
|
|
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_Q35_PCI_HOLE64_SIZE)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("64-bit PCI hole size setting is not supported "
|
|
"with this QEMU binary"));
|
|
return -1;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCI_BRIDGE:
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_DMI_TO_PCI_BRIDGE:
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_ROOT_PORT:
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_SWITCH_UPSTREAM_PORT:
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_SWITCH_DOWNSTREAM_PORT:
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCI_EXPANDER_BUS:
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_EXPANDER_BUS:
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_TO_PCI_BRIDGE:
|
|
if (pciopts->pcihole64 ||
|
|
pciopts->pcihole64size != 0) {
|
|
virReportControllerInvalidOption(cont, model, modelName, "pcihole64");
|
|
return -1;
|
|
}
|
|
break;
|
|
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCI_DEFAULT:
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCI_LAST:
|
|
default:
|
|
virReportEnumRangeError(virDomainControllerModelPCI, cont->model);
|
|
return -1;
|
|
}
|
|
|
|
/* busNr */
|
|
switch ((virDomainControllerModelPCI) cont->model) {
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCI_EXPANDER_BUS:
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_EXPANDER_BUS:
|
|
if (pciopts->busNr == -1) {
|
|
virReportControllerMissingOption(cont, model, modelName, "busNr");
|
|
return -1;
|
|
}
|
|
break;
|
|
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCI_ROOT:
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCI_BRIDGE:
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_DMI_TO_PCI_BRIDGE:
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_ROOT_PORT:
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_SWITCH_UPSTREAM_PORT:
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_SWITCH_DOWNSTREAM_PORT:
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_ROOT:
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_TO_PCI_BRIDGE:
|
|
if (pciopts->busNr != -1) {
|
|
virReportControllerInvalidOption(cont, model, modelName, "busNr");
|
|
return -1;
|
|
}
|
|
break;
|
|
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCI_DEFAULT:
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCI_LAST:
|
|
default:
|
|
virReportEnumRangeError(virDomainControllerModelPCI, cont->model);
|
|
return -1;
|
|
}
|
|
|
|
/* numaNode */
|
|
switch ((virDomainControllerModelPCI) cont->model) {
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCI_EXPANDER_BUS:
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_EXPANDER_BUS:
|
|
/* numaNode can be used for these controllers, but it's not set
|
|
* automatically so it can be missing */
|
|
break;
|
|
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCI_ROOT:
|
|
/* Only PHBs support numaNode */
|
|
if (pciopts->numaNode != -1 &&
|
|
pciopts->modelName != VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_SPAPR_PCI_HOST_BRIDGE) {
|
|
virReportControllerInvalidOption(cont, model, modelName, "numaNode");
|
|
return -1;
|
|
}
|
|
|
|
/* However, the default PHB doesn't support numaNode */
|
|
if (pciopts->numaNode != -1 &&
|
|
pciopts->modelName == VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_SPAPR_PCI_HOST_BRIDGE &&
|
|
pciopts->targetIndex == 0) {
|
|
virReportControllerInvalidOption(cont, model, modelName, "numaNode");
|
|
return -1;
|
|
}
|
|
break;
|
|
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCI_BRIDGE:
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_DMI_TO_PCI_BRIDGE:
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_ROOT_PORT:
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_SWITCH_UPSTREAM_PORT:
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_SWITCH_DOWNSTREAM_PORT:
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_ROOT:
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_TO_PCI_BRIDGE:
|
|
if (pciopts->numaNode != -1) {
|
|
virReportControllerInvalidOption(cont, model, modelName, "numaNode");
|
|
return -1;
|
|
}
|
|
break;
|
|
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCI_DEFAULT:
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCI_LAST:
|
|
default:
|
|
virReportEnumRangeError(virDomainControllerModelPCI, cont->model);
|
|
return -1;
|
|
}
|
|
|
|
/* chassisNr */
|
|
switch ((virDomainControllerModelPCI) cont->model) {
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCI_BRIDGE:
|
|
if (pciopts->chassisNr == -1) {
|
|
virReportControllerMissingOption(cont, model, modelName, "chassisNr");
|
|
return -1;
|
|
}
|
|
break;
|
|
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCI_ROOT:
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_DMI_TO_PCI_BRIDGE:
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_ROOT_PORT:
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_SWITCH_UPSTREAM_PORT:
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_SWITCH_DOWNSTREAM_PORT:
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCI_EXPANDER_BUS:
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_EXPANDER_BUS:
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_ROOT:
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_TO_PCI_BRIDGE:
|
|
if (pciopts->chassisNr != -1) {
|
|
virReportControllerInvalidOption(cont, model, modelName, "chassisNr");
|
|
return -1;
|
|
}
|
|
break;
|
|
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCI_DEFAULT:
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCI_LAST:
|
|
default:
|
|
virReportEnumRangeError(virDomainControllerModelPCI, cont->model);
|
|
return -1;
|
|
}
|
|
|
|
/* chassis and port */
|
|
switch ((virDomainControllerModelPCI) cont->model) {
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_ROOT_PORT:
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_SWITCH_DOWNSTREAM_PORT:
|
|
if (pciopts->chassis == -1) {
|
|
virReportControllerMissingOption(cont, model, modelName, "chassis");
|
|
return -1;
|
|
}
|
|
if (pciopts->port == -1) {
|
|
virReportControllerMissingOption(cont, model, modelName, "port");
|
|
return -1;
|
|
}
|
|
break;
|
|
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCI_ROOT:
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCI_BRIDGE:
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_DMI_TO_PCI_BRIDGE:
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_SWITCH_UPSTREAM_PORT:
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCI_EXPANDER_BUS:
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_EXPANDER_BUS:
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_ROOT:
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_TO_PCI_BRIDGE:
|
|
if (pciopts->chassis != -1) {
|
|
virReportControllerInvalidOption(cont, model, modelName, "chassis");
|
|
return -1;
|
|
}
|
|
if (pciopts->port != -1) {
|
|
virReportControllerInvalidOption(cont, model, modelName, "port");
|
|
return -1;
|
|
}
|
|
break;
|
|
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCI_DEFAULT:
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCI_LAST:
|
|
default:
|
|
virReportEnumRangeError(virDomainControllerModelPCI, cont->model);
|
|
}
|
|
|
|
/* hotplug */
|
|
if (pciopts->hotplug != VIR_TRISTATE_SWITCH_ABSENT) {
|
|
switch ((virDomainControllerModelPCI) cont->model) {
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_ROOT_PORT:
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_SWITCH_DOWNSTREAM_PORT:
|
|
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_PCIE_ROOT_PORT_HOTPLUG)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("setting the hotplug property on a '%s' device is not supported by this QEMU binary"),
|
|
modelName);
|
|
return -1;
|
|
}
|
|
break;
|
|
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCI_ROOT:
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCI_BRIDGE:
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_DMI_TO_PCI_BRIDGE:
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_SWITCH_UPSTREAM_PORT:
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCI_EXPANDER_BUS:
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_EXPANDER_BUS:
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_ROOT:
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCIE_TO_PCI_BRIDGE:
|
|
virReportControllerInvalidOption(cont, model, modelName, "hotplug");
|
|
return -1;
|
|
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCI_DEFAULT:
|
|
case VIR_DOMAIN_CONTROLLER_MODEL_PCI_LAST:
|
|
default:
|
|
virReportEnumRangeError(virDomainControllerModelPCI, cont->model);
|
|
}
|
|
}
|
|
|
|
/* QEMU device availability */
|
|
if (cap < 0) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("Unknown QEMU device for '%s' controller"),
|
|
modelName);
|
|
return -1;
|
|
}
|
|
if (cap > 0 && !virQEMUCapsGet(qemuCaps, cap)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("The '%s' device is not supported by this QEMU binary"),
|
|
modelName);
|
|
return -1;
|
|
}
|
|
|
|
/* PHBs didn't support numaNode from the very beginning, so an extra
|
|
* capability check is required */
|
|
if (cont->model == VIR_DOMAIN_CONTROLLER_MODEL_PCI_ROOT &&
|
|
pciopts->modelName == VIR_DOMAIN_CONTROLLER_PCI_MODEL_NAME_SPAPR_PCI_HOST_BRIDGE &&
|
|
pciopts->numaNode != -1 &&
|
|
!virQEMUCapsGet(qemuCaps, QEMU_CAPS_SPAPR_PCI_HOST_BRIDGE_NUMA_NODE)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("Option '%s' is not supported by '%s' device with this QEMU binary"),
|
|
"numaNode", modelName);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
#undef virReportControllerInvalidValue
|
|
#undef virReportControllerInvalidOption
|
|
#undef virReportControllerMissingOption
|
|
|
|
|
|
static int
|
|
qemuValidateDomainDeviceDefController(const virDomainControllerDef *controller,
|
|
const virDomainDef *def,
|
|
virQEMUCaps *qemuCaps)
|
|
{
|
|
int ret = 0;
|
|
|
|
if (controller->type == VIR_DOMAIN_CONTROLLER_TYPE_SCSI &&
|
|
!qemuValidateCheckSCSIControllerModel(qemuCaps, controller->model))
|
|
return -1;
|
|
|
|
if (qemuValidateDomainDeviceDefControllerAttributes(controller, qemuCaps) < 0)
|
|
return -1;
|
|
|
|
switch ((virDomainControllerType)controller->type) {
|
|
case VIR_DOMAIN_CONTROLLER_TYPE_IDE:
|
|
ret = qemuValidateDomainDeviceDefControllerIDE(controller, def);
|
|
break;
|
|
|
|
case VIR_DOMAIN_CONTROLLER_TYPE_SCSI:
|
|
ret = qemuValidateDomainDeviceDefControllerSCSI(controller, def);
|
|
break;
|
|
|
|
case VIR_DOMAIN_CONTROLLER_TYPE_PCI:
|
|
ret = qemuValidateDomainDeviceDefControllerPCI(controller, def,
|
|
qemuCaps);
|
|
break;
|
|
|
|
case VIR_DOMAIN_CONTROLLER_TYPE_SATA:
|
|
ret = qemuValidateDomainDeviceDefControllerSATA(controller, def,
|
|
qemuCaps);
|
|
break;
|
|
|
|
case VIR_DOMAIN_CONTROLLER_TYPE_FDC:
|
|
case VIR_DOMAIN_CONTROLLER_TYPE_VIRTIO_SERIAL:
|
|
case VIR_DOMAIN_CONTROLLER_TYPE_CCID:
|
|
case VIR_DOMAIN_CONTROLLER_TYPE_USB:
|
|
case VIR_DOMAIN_CONTROLLER_TYPE_XENBUS:
|
|
case VIR_DOMAIN_CONTROLLER_TYPE_ISA:
|
|
case VIR_DOMAIN_CONTROLLER_TYPE_LAST:
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
static int
|
|
qemuValidateDomainDeviceDefSPICEGraphics(const virDomainGraphicsDef *graphics,
|
|
virQEMUDriver *driver,
|
|
virQEMUCaps *qemuCaps)
|
|
{
|
|
g_autoptr(virQEMUDriverConfig) cfg = virQEMUDriverGetConfig(driver);
|
|
virDomainGraphicsListenDef *glisten = NULL;
|
|
int tlsPort = graphics->data.spice.tlsPort;
|
|
|
|
glisten = virDomainGraphicsGetListen((virDomainGraphicsDef *)graphics, 0);
|
|
if (!glisten) {
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
_("missing listen element"));
|
|
return -1;
|
|
}
|
|
|
|
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"));
|
|
return -1;
|
|
}
|
|
break;
|
|
|
|
case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_ADDRESS:
|
|
case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_NETWORK:
|
|
if (tlsPort > 0 && !cfg->spiceTLS) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("spice TLS port set in XML configuration, "
|
|
"but TLS is disabled in qemu.conf"));
|
|
return -1;
|
|
}
|
|
break;
|
|
|
|
case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_NONE:
|
|
break;
|
|
case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_LAST:
|
|
break;
|
|
}
|
|
|
|
if (graphics->data.spice.filetransfer == VIR_TRISTATE_BOOL_NO &&
|
|
!virQEMUCapsGet(qemuCaps, QEMU_CAPS_SPICE_FILE_XFER_DISABLE)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("This QEMU can't disable file transfers through spice"));
|
|
return -1;
|
|
}
|
|
|
|
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"));
|
|
return -1;
|
|
}
|
|
|
|
if (graphics->data.spice.rendernode &&
|
|
!virQEMUCapsGet(qemuCaps, QEMU_CAPS_SPICE_RENDERNODE)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("This QEMU doesn't support spice OpenGL rendernode"));
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
qemuValidateDomainDeviceDefVNCGraphics(const virDomainGraphicsDef *graphics,
|
|
virQEMUCaps *qemuCaps)
|
|
{
|
|
if (graphics->data.vnc.powerControl != VIR_TRISTATE_BOOL_ABSENT &&
|
|
!virQEMUCapsGet(qemuCaps, QEMU_CAPS_VNC_POWER_CONTROL)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("VNC power control is not available"));
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
qemuValidateDomainDeviceDefGraphics(const virDomainGraphicsDef *graphics,
|
|
const virDomainDef *def,
|
|
virQEMUDriver *driver,
|
|
virQEMUCaps *qemuCaps)
|
|
{
|
|
virDomainCapsDeviceGraphics graphicsCaps = { 0 };
|
|
bool have_egl_headless = false;
|
|
size_t i;
|
|
|
|
virQEMUCapsFillDomainDeviceGraphicsCaps(qemuCaps, &graphicsCaps);
|
|
|
|
if (!VIR_DOMAIN_CAPS_ENUM_IS_SET(graphicsCaps.type, graphics->type)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("%s graphics are not supported with this QEMU"),
|
|
virDomainGraphicsTypeToString(graphics->type));
|
|
return -1;
|
|
}
|
|
|
|
for (i = 0; i < def->ngraphics; i++) {
|
|
if (def->graphics[i]->type == VIR_DOMAIN_GRAPHICS_TYPE_EGL_HEADLESS) {
|
|
have_egl_headless = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Only VNC and SPICE can be paired with egl-headless, the other types
|
|
* either don't make sense to pair with egl-headless or aren't even
|
|
* supported by QEMU.
|
|
*/
|
|
if (have_egl_headless) {
|
|
if (graphics->type != VIR_DOMAIN_GRAPHICS_TYPE_EGL_HEADLESS &&
|
|
graphics->type != VIR_DOMAIN_GRAPHICS_TYPE_VNC &&
|
|
graphics->type != VIR_DOMAIN_GRAPHICS_TYPE_SPICE) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("graphics type 'egl-headless' is only supported "
|
|
"with one of: 'vnc', 'spice' graphics types"));
|
|
return -1;
|
|
}
|
|
|
|
/* '-spice gl=on' and '-display egl-headless' are mutually
|
|
* exclusive
|
|
*/
|
|
if (graphics->type == VIR_DOMAIN_GRAPHICS_TYPE_SPICE &&
|
|
graphics->data.spice.gl == VIR_TRISTATE_BOOL_YES) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("multiple OpenGL displays are not supported "
|
|
"by QEMU"));
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
switch (graphics->type) {
|
|
case VIR_DOMAIN_GRAPHICS_TYPE_SPICE:
|
|
if (qemuValidateDomainDeviceDefSPICEGraphics(graphics, driver,
|
|
qemuCaps) < 0)
|
|
return -1;
|
|
|
|
break;
|
|
|
|
case VIR_DOMAIN_GRAPHICS_TYPE_EGL_HEADLESS:
|
|
if (graphics->data.egl_headless.rendernode &&
|
|
!virQEMUCapsGet(qemuCaps, QEMU_CAPS_EGL_HEADLESS_RENDERNODE)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("This QEMU doesn't support OpenGL rendernode "
|
|
"with egl-headless graphics type"));
|
|
return -1;
|
|
}
|
|
|
|
break;
|
|
|
|
case VIR_DOMAIN_GRAPHICS_TYPE_VNC:
|
|
if (qemuValidateDomainDeviceDefVNCGraphics(graphics, qemuCaps) < 0)
|
|
return -1;
|
|
|
|
break;
|
|
|
|
case VIR_DOMAIN_GRAPHICS_TYPE_SDL:
|
|
case VIR_DOMAIN_GRAPHICS_TYPE_RDP:
|
|
case VIR_DOMAIN_GRAPHICS_TYPE_DESKTOP:
|
|
case VIR_DOMAIN_GRAPHICS_TYPE_LAST:
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
qemuValidateDomainDeviceDefFS(virDomainFSDef *fs,
|
|
const virDomainDef *def,
|
|
virQEMUDriver *driver,
|
|
virQEMUCaps *qemuCaps)
|
|
{
|
|
if (fs->type != VIR_DOMAIN_FS_TYPE_MOUNT) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("only supports mount filesystem type"));
|
|
return -1;
|
|
}
|
|
if (fs->multidevs != VIR_DOMAIN_FS_MODEL_DEFAULT &&
|
|
!virQEMUCapsGet(qemuCaps, QEMU_CAPS_FSDEV_MULTIDEVS)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("multidevs is not supported with this QEMU binary"));
|
|
return -1;
|
|
}
|
|
|
|
if ((fs->fmode != 0) || (fs->dmode != 0)) {
|
|
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_FSDEV_CREATEMODE)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("fmode and dmode are not supported with this QEMU binary"));
|
|
return -1;
|
|
}
|
|
if (fs->accessmode != VIR_DOMAIN_FS_ACCESSMODE_MAPPED) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("fmode and dmode must be used with accessmode=mapped"));
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if (fs->fsdriver != VIR_DOMAIN_FS_DRIVER_TYPE_VIRTIOFS &&
|
|
fs->sandbox != VIR_DOMAIN_FS_SANDBOX_MODE_DEFAULT) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("sandbox can only be used with driver=virtiofs"));
|
|
return -1;
|
|
}
|
|
|
|
switch ((virDomainFSDriverType) fs->fsdriver) {
|
|
case VIR_DOMAIN_FS_DRIVER_TYPE_DEFAULT:
|
|
case VIR_DOMAIN_FS_DRIVER_TYPE_PATH:
|
|
break;
|
|
|
|
case VIR_DOMAIN_FS_DRIVER_TYPE_HANDLE:
|
|
if (fs->accessmode != VIR_DOMAIN_FS_ACCESSMODE_PASSTHROUGH) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("only supports passthrough accessmode"));
|
|
return -1;
|
|
}
|
|
break;
|
|
|
|
case VIR_DOMAIN_FS_DRIVER_TYPE_LOOP:
|
|
case VIR_DOMAIN_FS_DRIVER_TYPE_NBD:
|
|
case VIR_DOMAIN_FS_DRIVER_TYPE_PLOOP:
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("Filesystem driver type not supported"));
|
|
return -1;
|
|
|
|
case VIR_DOMAIN_FS_DRIVER_TYPE_VIRTIOFS:
|
|
if (!fs->sock) {
|
|
if (fs->readonly) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("virtiofs does not yet support read-only mode"));
|
|
return -1;
|
|
}
|
|
if (!driver->privileged) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("virtiofs is not yet supported in session mode"));
|
|
return -1;
|
|
}
|
|
if (fs->accessmode != VIR_DOMAIN_FS_ACCESSMODE_PASSTHROUGH) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("virtiofs only supports passthrough accessmode"));
|
|
return -1;
|
|
}
|
|
if (fs->wrpolicy != VIR_DOMAIN_FS_WRPOLICY_DEFAULT) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("virtiofs does not support wrpolicy"));
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if (fs->model != VIR_DOMAIN_FS_MODEL_DEFAULT) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("virtiofs does not support model"));
|
|
return -1;
|
|
}
|
|
if (fs->format != VIR_STORAGE_FILE_NONE) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("virtiofs does not support format"));
|
|
return -1;
|
|
}
|
|
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_VHOST_USER_FS)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("virtiofs is not supported with this QEMU binary"));
|
|
return -1;
|
|
}
|
|
if (fs->multidevs != VIR_DOMAIN_FS_MULTIDEVS_DEFAULT) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("virtiofs does not support multidevs"));
|
|
return -1;
|
|
}
|
|
if ((fs->fmode != 0) || (fs->dmode != 0)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("virtiofs does not support fmode and dmode"));
|
|
return -1;
|
|
}
|
|
if (qemuValidateDomainDefVhostUserRequireSharedMemory(def, "virtiofs",
|
|
qemuCaps) < 0) {
|
|
return -1;
|
|
}
|
|
if (fs->info.bootIndex &&
|
|
!virQEMUCapsGet(qemuCaps, QEMU_CAPS_VHOST_USER_FS_BOOTINDEX)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("setting virtiofs boot order is not supported with this QEMU binary"));
|
|
return -1;
|
|
}
|
|
break;
|
|
|
|
case VIR_DOMAIN_FS_DRIVER_TYPE_LAST:
|
|
default:
|
|
virReportEnumRangeError(virDomainFSDriverType, fs->fsdriver);
|
|
return -1;
|
|
}
|
|
|
|
if (qemuValidateDomainVirtioOptions(fs->virtio, qemuCaps) < 0)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
qemuValidateDomainDeviceDefAudio(virDomainAudioDef *audio,
|
|
virQEMUCaps *qemuCaps G_GNUC_UNUSED)
|
|
{
|
|
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_AUDIODEV)) {
|
|
if (audio->input.mixingEngine == VIR_TRISTATE_BOOL_NO ||
|
|
audio->output.mixingEngine == VIR_TRISTATE_BOOL_NO) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("disabling audio mixing engine is not supported with this QEMU"));
|
|
return -1;
|
|
}
|
|
|
|
if ((audio->input.bufferLength ||
|
|
audio->output.bufferLength) &&
|
|
(audio->type != VIR_DOMAIN_AUDIO_TYPE_PULSEAUDIO &&
|
|
audio->type != VIR_DOMAIN_AUDIO_TYPE_COREAUDIO &&
|
|
audio->type != VIR_DOMAIN_AUDIO_TYPE_SDL)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("setting audio buffer length is not supported with this QEMU"));
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
switch (audio->type) {
|
|
case VIR_DOMAIN_AUDIO_TYPE_NONE:
|
|
break;
|
|
|
|
case VIR_DOMAIN_AUDIO_TYPE_ALSA:
|
|
break;
|
|
|
|
case VIR_DOMAIN_AUDIO_TYPE_COREAUDIO:
|
|
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_AUDIODEV)) {
|
|
if (audio->input.bufferLength) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("setting audio buffer length is not supported with this QEMU"));
|
|
return -1;
|
|
}
|
|
if (audio->backend.coreaudio.input.bufferCount) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("setting audio buffer count is not supported with this QEMU"));
|
|
return -1;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case VIR_DOMAIN_AUDIO_TYPE_JACK:
|
|
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_AUDIODEV)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("'jack' audio backend is not supported with this QEMU"));
|
|
return -1;
|
|
}
|
|
break;
|
|
|
|
case VIR_DOMAIN_AUDIO_TYPE_OSS:
|
|
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_AUDIODEV)) {
|
|
if (audio->backend.oss.input.bufferCount !=
|
|
audio->backend.oss.output.bufferCount) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("setting audio buffer count is not supported with this QEMU"));
|
|
return -1;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case VIR_DOMAIN_AUDIO_TYPE_PULSEAUDIO:
|
|
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_AUDIODEV)) {
|
|
if (audio->backend.pulseaudio.input.streamName ||
|
|
audio->backend.pulseaudio.output.streamName) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("setting audio stream name is not supported with this QEMU"));
|
|
return -1;
|
|
}
|
|
|
|
if (audio->backend.pulseaudio.input.latency ||
|
|
audio->backend.pulseaudio.output.latency) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("setting audio latency is not supported with this QEMU"));
|
|
return -1;
|
|
}
|
|
|
|
if (audio->input.bufferLength != audio->output.bufferLength) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("setting audio buffer length is not supported with this QEMU"));
|
|
return -1;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case VIR_DOMAIN_AUDIO_TYPE_SDL:
|
|
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_AUDIODEV)) {
|
|
if (audio->input.bufferLength) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("setting audio buffer length is not supported with this QEMU"));
|
|
return -1;
|
|
}
|
|
|
|
if (audio->backend.sdl.input.bufferCount ||
|
|
audio->backend.sdl.output.bufferCount) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("setting audio buffer count is not supported with this QEMU"));
|
|
return -1;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case VIR_DOMAIN_AUDIO_TYPE_SPICE:
|
|
break;
|
|
|
|
case VIR_DOMAIN_AUDIO_TYPE_FILE:
|
|
break;
|
|
|
|
case VIR_DOMAIN_AUDIO_TYPE_LAST:
|
|
default:
|
|
virReportEnumRangeError(virDomainAudioType, audio->type);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
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;
|
|
case VIR_DOMAIN_SOUND_CODEC_TYPE_OUTPUT:
|
|
return QEMU_CAPS_HDA_OUTPUT;
|
|
default:
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
|
|
static int
|
|
qemuValidateDomainDeviceDefSound(virDomainSoundDef *sound,
|
|
virQEMUCaps *qemuCaps)
|
|
{
|
|
size_t i;
|
|
|
|
switch (sound->model) {
|
|
case VIR_DOMAIN_SOUND_MODEL_USB:
|
|
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_OBJECT_USB_AUDIO)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("usb-audio controller is not supported "
|
|
"by this QEMU binary"));
|
|
return -1;
|
|
}
|
|
break;
|
|
case VIR_DOMAIN_SOUND_MODEL_ICH9:
|
|
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"));
|
|
return -1;
|
|
}
|
|
break;
|
|
|
|
case VIR_DOMAIN_SOUND_MODEL_ES1370:
|
|
case VIR_DOMAIN_SOUND_MODEL_AC97:
|
|
case VIR_DOMAIN_SOUND_MODEL_ICH6:
|
|
case VIR_DOMAIN_SOUND_MODEL_SB16:
|
|
case VIR_DOMAIN_SOUND_MODEL_PCSPK:
|
|
break;
|
|
case VIR_DOMAIN_SOUND_MODEL_ICH7:
|
|
case VIR_DOMAIN_SOUND_MODEL_LAST:
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
_("sound card model '%s' is not supported by qemu"),
|
|
virDomainSoundModelTypeToString(sound->model));
|
|
return -1;
|
|
}
|
|
|
|
if (virDomainSoundModelSupportsCodecs(sound)) {
|
|
for (i = 0; i < sound->ncodecs; i++) {
|
|
const char *stype;
|
|
int type, flags;
|
|
|
|
type = sound->codecs[i]->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);
|
|
return -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
qemuValidateDomainDeviceDefVsock(const virDomainVsockDef *vsock,
|
|
virQEMUCaps *qemuCaps)
|
|
{
|
|
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_VHOST_VSOCK)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("vsock device is not supported "
|
|
"with this QEMU binary"));
|
|
return -1;
|
|
}
|
|
|
|
if (qemuValidateDomainVirtioOptions(vsock->virtio, qemuCaps) < 0)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
qemuValidateDomainDeviceDefTPM(virDomainTPMDef *tpm,
|
|
const virDomainDef *def,
|
|
virQEMUCaps *qemuCaps)
|
|
{
|
|
virQEMUCapsFlags flag;
|
|
|
|
switch (tpm->version) {
|
|
case VIR_DOMAIN_TPM_VERSION_1_2:
|
|
/* TPM 1.2 + CRB do not work */
|
|
if (tpm->type == VIR_DOMAIN_TPM_TYPE_EMULATOR &&
|
|
tpm->model == VIR_DOMAIN_TPM_MODEL_CRB) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("Unsupported interface %s for TPM 1.2"),
|
|
virDomainTPMModelTypeToString(tpm->model));
|
|
return -1;
|
|
}
|
|
/* TPM 1.2 + SPAPR do not work with any 'type' (backend) */
|
|
if (tpm->model == VIR_DOMAIN_TPM_MODEL_SPAPR) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("TPM 1.2 is not supported with the SPAPR device model"));
|
|
return -1;
|
|
}
|
|
/* TPM 1.2 + ARM does not work */
|
|
if (qemuDomainIsARMVirt(def)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("TPM 1.2 is not supported on ARM"));
|
|
return -1;
|
|
}
|
|
break;
|
|
case VIR_DOMAIN_TPM_VERSION_2_0:
|
|
case VIR_DOMAIN_TPM_VERSION_DEFAULT:
|
|
case VIR_DOMAIN_TPM_VERSION_LAST:
|
|
break;
|
|
}
|
|
|
|
switch (tpm->type) {
|
|
case VIR_DOMAIN_TPM_TYPE_PASSTHROUGH:
|
|
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_TPM_PASSTHROUGH))
|
|
goto no_support;
|
|
break;
|
|
|
|
case VIR_DOMAIN_TPM_TYPE_EMULATOR:
|
|
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_TPM_EMULATOR))
|
|
goto no_support;
|
|
|
|
break;
|
|
case VIR_DOMAIN_TPM_TYPE_LAST:
|
|
break;
|
|
}
|
|
|
|
switch (tpm->model) {
|
|
case VIR_DOMAIN_TPM_MODEL_TIS:
|
|
if (!ARCH_IS_X86(def->os.arch) && (def->os.arch != VIR_ARCH_AARCH64)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("TPM model '%s' is only available for x86 and aarch64 guests"),
|
|
virDomainTPMModelTypeToString(tpm->model));
|
|
return -1;
|
|
}
|
|
flag = QEMU_CAPS_DEVICE_TPM_TIS;
|
|
break;
|
|
case VIR_DOMAIN_TPM_MODEL_CRB:
|
|
flag = QEMU_CAPS_DEVICE_TPM_CRB;
|
|
break;
|
|
case VIR_DOMAIN_TPM_MODEL_SPAPR:
|
|
flag = QEMU_CAPS_DEVICE_TPM_SPAPR;
|
|
break;
|
|
case VIR_DOMAIN_TPM_MODEL_SPAPR_PROXY:
|
|
if (!ARCH_IS_PPC64(def->os.arch)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("TPM Proxy model %s is only available for "
|
|
"PPC64 guests"),
|
|
virDomainTPMModelTypeToString(tpm->model));
|
|
return -1;
|
|
}
|
|
|
|
/* TPM Proxy devices have 'passthrough' backend */
|
|
if (tpm->type != VIR_DOMAIN_TPM_TYPE_PASSTHROUGH) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("TPM Proxy model %s requires "
|
|
"'Passthrough' backend"),
|
|
virDomainTPMModelTypeToString(tpm->model));
|
|
}
|
|
|
|
flag = QEMU_CAPS_DEVICE_SPAPR_TPM_PROXY;
|
|
break;
|
|
case VIR_DOMAIN_TPM_MODEL_LAST:
|
|
default:
|
|
virReportEnumRangeError(virDomainTPMModel, tpm->model);
|
|
return -1;
|
|
}
|
|
|
|
if (!virQEMUCapsGet(qemuCaps, flag)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("The QEMU executable %s does not support TPM "
|
|
"model %s"),
|
|
def->emulator,
|
|
virDomainTPMModelTypeToString(tpm->model));
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
|
|
no_support:
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("The QEMU executable %s does not support TPM "
|
|
"backend type %s"),
|
|
def->emulator,
|
|
virDomainTPMBackendTypeToString(tpm->type));
|
|
return -1;
|
|
}
|
|
|
|
|
|
static int
|
|
qemuValidateDomainDeviceDefInput(const virDomainInputDef *input,
|
|
const virDomainDef *def,
|
|
virQEMUCaps *qemuCaps)
|
|
{
|
|
const char *baseName;
|
|
int cap;
|
|
int ccwCap;
|
|
|
|
if (input->bus == VIR_DOMAIN_INPUT_BUS_PS2 && !ARCH_IS_X86(def->os.arch) &&
|
|
!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_I8042)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("%s is not supported by this QEMU binary"),
|
|
virDomainInputBusTypeToString(input->bus));
|
|
return -1;
|
|
}
|
|
|
|
if (input->bus == VIR_DOMAIN_INPUT_BUS_USB &&
|
|
input->type == VIR_DOMAIN_INPUT_TYPE_KBD &&
|
|
!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_USB_KBD)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("usb keyboard is not supported by this "
|
|
"QEMU binary"));
|
|
return -1;
|
|
}
|
|
|
|
if (input->bus != VIR_DOMAIN_INPUT_BUS_VIRTIO)
|
|
return 0;
|
|
|
|
/* Only type=passthrough supports model=virtio-(non-)transitional */
|
|
switch ((virDomainInputModel)input->model) {
|
|
case VIR_DOMAIN_INPUT_MODEL_VIRTIO_TRANSITIONAL:
|
|
case VIR_DOMAIN_INPUT_MODEL_VIRTIO_NON_TRANSITIONAL:
|
|
switch ((virDomainInputType)input->type) {
|
|
case VIR_DOMAIN_INPUT_TYPE_MOUSE:
|
|
case VIR_DOMAIN_INPUT_TYPE_TABLET:
|
|
case VIR_DOMAIN_INPUT_TYPE_KBD:
|
|
case VIR_DOMAIN_INPUT_TYPE_EVDEV:
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("virtio (non-)transitional models are not "
|
|
"supported for input type=%s"),
|
|
virDomainInputTypeToString(input->type));
|
|
return -1;
|
|
case VIR_DOMAIN_INPUT_TYPE_PASSTHROUGH:
|
|
break;
|
|
case VIR_DOMAIN_INPUT_TYPE_LAST:
|
|
default:
|
|
virReportEnumRangeError(virDomainInputType,
|
|
input->type);
|
|
return -1;
|
|
}
|
|
break;
|
|
case VIR_DOMAIN_INPUT_MODEL_VIRTIO:
|
|
case VIR_DOMAIN_INPUT_MODEL_DEFAULT:
|
|
break;
|
|
case VIR_DOMAIN_INPUT_MODEL_LAST:
|
|
default:
|
|
virReportEnumRangeError(virDomainInputModel,
|
|
input->model);
|
|
return -1;
|
|
}
|
|
|
|
switch ((virDomainInputType)input->type) {
|
|
case VIR_DOMAIN_INPUT_TYPE_MOUSE:
|
|
baseName = "virtio-mouse";
|
|
cap = QEMU_CAPS_VIRTIO_MOUSE;
|
|
ccwCap = QEMU_CAPS_DEVICE_VIRTIO_MOUSE_CCW;
|
|
break;
|
|
case VIR_DOMAIN_INPUT_TYPE_TABLET:
|
|
baseName = "virtio-tablet";
|
|
cap = QEMU_CAPS_VIRTIO_TABLET;
|
|
ccwCap = QEMU_CAPS_DEVICE_VIRTIO_TABLET_CCW;
|
|
break;
|
|
case VIR_DOMAIN_INPUT_TYPE_KBD:
|
|
baseName = "virtio-keyboard";
|
|
cap = QEMU_CAPS_VIRTIO_KEYBOARD;
|
|
ccwCap = QEMU_CAPS_DEVICE_VIRTIO_KEYBOARD_CCW;
|
|
break;
|
|
case VIR_DOMAIN_INPUT_TYPE_PASSTHROUGH:
|
|
baseName = "virtio-input-host";
|
|
cap = QEMU_CAPS_VIRTIO_INPUT_HOST;
|
|
ccwCap = QEMU_CAPS_LAST;
|
|
break;
|
|
case VIR_DOMAIN_INPUT_TYPE_EVDEV:
|
|
baseName = "input-linux";
|
|
cap = QEMU_CAPS_INPUT_LINUX;
|
|
ccwCap = QEMU_CAPS_LAST;
|
|
break;
|
|
case VIR_DOMAIN_INPUT_TYPE_LAST:
|
|
default:
|
|
virReportEnumRangeError(virDomainInputType,
|
|
input->type);
|
|
return -1;
|
|
}
|
|
|
|
if (!virQEMUCapsGet(qemuCaps, cap) ||
|
|
(input->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_CCW &&
|
|
!virQEMUCapsGet(qemuCaps, ccwCap))) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("%s is not supported by this QEMU binary"),
|
|
baseName);
|
|
return -1;
|
|
}
|
|
|
|
if (qemuValidateDomainVirtioOptions(input->virtio, qemuCaps) < 0)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
qemuValidateDomainDeviceDefMemballoon(const virDomainMemballoonDef *memballoon,
|
|
virQEMUCaps *qemuCaps)
|
|
{
|
|
if (!memballoon ||
|
|
memballoon->model == VIR_DOMAIN_MEMBALLOON_MODEL_NONE) {
|
|
return 0;
|
|
}
|
|
|
|
if (memballoon->model != VIR_DOMAIN_MEMBALLOON_MODEL_VIRTIO &&
|
|
memballoon->model != VIR_DOMAIN_MEMBALLOON_MODEL_VIRTIO_TRANSITIONAL &&
|
|
memballoon->model != VIR_DOMAIN_MEMBALLOON_MODEL_VIRTIO_NON_TRANSITIONAL) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("Memory balloon device type '%s' is not supported by this version of qemu"),
|
|
virDomainMemballoonModelTypeToString(memballoon->model));
|
|
return -1;
|
|
}
|
|
|
|
if (memballoon->autodeflate != VIR_TRISTATE_SWITCH_ABSENT &&
|
|
!virQEMUCapsGet(qemuCaps, QEMU_CAPS_VIRTIO_BALLOON_AUTODEFLATE)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("deflate-on-oom is not supported by this QEMU binary"));
|
|
return -1;
|
|
}
|
|
|
|
if (memballoon->free_page_reporting != VIR_TRISTATE_SWITCH_ABSENT &&
|
|
!virQEMUCapsGet(qemuCaps, QEMU_CAPS_VIRTIO_BALLOON_FREE_PAGE_REPORTING)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("freePageReporting is not supported by this QEMU binary"));
|
|
return -1;
|
|
}
|
|
|
|
if (qemuValidateDomainVirtioOptions(memballoon->virtio, qemuCaps) < 0)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
qemuValidateDomainDeviceDefIOMMU(const virDomainIOMMUDef *iommu,
|
|
const virDomainDef *def,
|
|
virQEMUCaps *qemuCaps)
|
|
{
|
|
switch (iommu->model) {
|
|
case VIR_DOMAIN_IOMMU_MODEL_INTEL:
|
|
if (!qemuDomainIsQ35(def)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("IOMMU device: '%s' is only supported with "
|
|
"Q35 machines"),
|
|
virDomainIOMMUModelTypeToString(iommu->model));
|
|
return -1;
|
|
}
|
|
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_INTEL_IOMMU)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("IOMMU device: '%s' is not supported with "
|
|
"this QEMU binary"),
|
|
virDomainIOMMUModelTypeToString(iommu->model));
|
|
return -1;
|
|
}
|
|
break;
|
|
|
|
case VIR_DOMAIN_IOMMU_MODEL_SMMUV3:
|
|
if (!qemuDomainIsARMVirt(def)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("IOMMU device: '%s' is only supported with "
|
|
"ARM Virt machines"),
|
|
virDomainIOMMUModelTypeToString(iommu->model));
|
|
return -1;
|
|
}
|
|
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_MACHINE_VIRT_IOMMU)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("IOMMU device: '%s' is not supported with "
|
|
"this QEMU binary"),
|
|
virDomainIOMMUModelTypeToString(iommu->model));
|
|
return -1;
|
|
}
|
|
break;
|
|
|
|
case VIR_DOMAIN_IOMMU_MODEL_LAST:
|
|
default:
|
|
virReportEnumRangeError(virDomainIOMMUModel, iommu->model);
|
|
return -1;
|
|
}
|
|
|
|
/* These capability checks ensure we're not trying to use features
|
|
* of Intel IOMMU that the QEMU binary does not support, but they
|
|
* also make sure we report an error when trying to use features
|
|
* that are not implemented by SMMUv3 */
|
|
|
|
if (iommu->intremap != VIR_TRISTATE_SWITCH_ABSENT &&
|
|
!virQEMUCapsGet(qemuCaps, QEMU_CAPS_INTEL_IOMMU_INTREMAP)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("iommu: interrupt remapping is not supported "
|
|
"with this QEMU binary"));
|
|
return -1;
|
|
}
|
|
if (iommu->caching_mode != VIR_TRISTATE_SWITCH_ABSENT &&
|
|
!virQEMUCapsGet(qemuCaps, QEMU_CAPS_INTEL_IOMMU_CACHING_MODE)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("iommu: caching mode is not supported "
|
|
"with this QEMU binary"));
|
|
return -1;
|
|
}
|
|
if (iommu->eim != VIR_TRISTATE_SWITCH_ABSENT &&
|
|
!virQEMUCapsGet(qemuCaps, QEMU_CAPS_INTEL_IOMMU_EIM)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("iommu: eim is not supported "
|
|
"with this QEMU binary"));
|
|
return -1;
|
|
}
|
|
if (iommu->iotlb != VIR_TRISTATE_SWITCH_ABSENT &&
|
|
!virQEMUCapsGet(qemuCaps, QEMU_CAPS_INTEL_IOMMU_DEVICE_IOTLB)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("iommu: device IOTLB is not supported "
|
|
"with this QEMU binary"));
|
|
return -1;
|
|
}
|
|
if (iommu->aw_bits > 0 &&
|
|
!virQEMUCapsGet(qemuCaps, QEMU_CAPS_INTEL_IOMMU_AW_BITS)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("iommu: aw_bits is not supported "
|
|
"with this QEMU binary"));
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
qemuValidateDomainDeviceDefNVRAM(virDomainNVRAMDef *nvram,
|
|
const virDomainDef *def,
|
|
virQEMUCaps *qemuCaps)
|
|
{
|
|
if (!nvram)
|
|
return 0;
|
|
|
|
if (qemuDomainIsPSeries(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;
|
|
}
|
|
} else {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("nvram device is only supported for PPC64"));
|
|
return -1;
|
|
}
|
|
|
|
if (!(nvram->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_SPAPRVIO &&
|
|
nvram->info.addr.spaprvio.has_reg)) {
|
|
|
|
virReportError(VIR_ERR_XML_ERROR, "%s",
|
|
_("nvram address type must be spaprvio"));
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
qemuValidateDomainDeviceDefHub(virDomainHubDef *hub,
|
|
virQEMUCaps *qemuCaps)
|
|
{
|
|
if (hub->type != VIR_DOMAIN_HUB_TYPE_USB) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("hub type %s not supported"),
|
|
virDomainHubTypeToString(hub->type));
|
|
return -1;
|
|
}
|
|
|
|
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_USB_HUB)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("usb-hub not supported by QEMU binary"));
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
qemuValidateDomainDeviceDefMemory(virDomainMemoryDef *mem,
|
|
virQEMUCaps *qemuCaps)
|
|
{
|
|
switch (mem->model) {
|
|
case VIR_DOMAIN_MEMORY_MODEL_DIMM:
|
|
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_PC_DIMM)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("memory hotplug isn't supported by this QEMU binary"));
|
|
return -1;
|
|
}
|
|
break;
|
|
|
|
case VIR_DOMAIN_MEMORY_MODEL_NVDIMM:
|
|
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_NVDIMM)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("nvdimm isn't supported by this QEMU binary"));
|
|
return -1;
|
|
}
|
|
|
|
if (mem->readonly &&
|
|
!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_NVDIMM_UNARMED)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("nvdimm readonly property is not available "
|
|
"with this QEMU binary"));
|
|
return -1;
|
|
}
|
|
break;
|
|
|
|
case VIR_DOMAIN_MEMORY_MODEL_VIRTIO_PMEM:
|
|
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_VIRTIO_PMEM_PCI)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("virtio-pmem isn't supported by this QEMU binary"));
|
|
return -1;
|
|
}
|
|
break;
|
|
|
|
case VIR_DOMAIN_MEMORY_MODEL_NONE:
|
|
case VIR_DOMAIN_MEMORY_MODEL_LAST:
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
qemuValidateDomainDeviceDefShmem(virDomainShmemDef *shmem,
|
|
virQEMUCaps *qemuCaps)
|
|
{
|
|
switch (shmem->model) {
|
|
case VIR_DOMAIN_SHMEM_MODEL_IVSHMEM:
|
|
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_IVSHMEM)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
|
|
_("ivshmem device is not supported "
|
|
"with this QEMU binary"));
|
|
return -1;
|
|
}
|
|
break;
|
|
|
|
case VIR_DOMAIN_SHMEM_MODEL_IVSHMEM_PLAIN:
|
|
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_IVSHMEM_PLAIN)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("shmem model '%s' is not supported "
|
|
"by this QEMU binary"),
|
|
virDomainShmemModelTypeToString(shmem->model));
|
|
return -1;
|
|
}
|
|
break;
|
|
|
|
case VIR_DOMAIN_SHMEM_MODEL_IVSHMEM_DOORBELL:
|
|
if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_IVSHMEM_DOORBELL)) {
|
|
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
|
|
_("shmem model '%s' is not supported "
|
|
"by this QEMU binary"),
|
|
virDomainShmemModelTypeToString(shmem->model));
|
|
return -1;
|
|
}
|
|
break;
|
|
|
|
case VIR_DOMAIN_SHMEM_MODEL_LAST:
|
|
virReportEnumRangeError(virDomainShmemModel, shmem->model);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int
|
|
qemuValidateDomainDeviceDef(const virDomainDeviceDef *dev,
|
|
const virDomainDef *def,
|
|
void *opaque,
|
|
void *parseOpaque)
|
|
{
|
|
int ret = 0;
|
|
virQEMUDriver *driver = opaque;
|
|
g_autoptr(virQEMUCaps) qemuCapsLocal = NULL;
|
|
virQEMUCaps *qemuCaps = parseOpaque;
|
|
|
|
if (!qemuCaps) {
|
|
if (!(qemuCapsLocal = virQEMUCapsCacheLookup(driver->qemuCapsCache,
|
|
def->emulator)))
|
|
return -1;
|
|
|
|
qemuCaps = qemuCapsLocal;
|
|
}
|
|
|
|
if ((ret = qemuValidateDomainDeviceDefAddress(dev, def, qemuCaps)) < 0)
|
|
return ret;
|
|
|
|
switch ((virDomainDeviceType)dev->type) {
|
|
case VIR_DOMAIN_DEVICE_NET:
|
|
ret = qemuValidateDomainDeviceDefNetwork(dev->data.net, qemuCaps);
|
|
break;
|
|
|
|
case VIR_DOMAIN_DEVICE_CHR:
|
|
ret = qemuValidateDomainChrDef(dev->data.chr, def, qemuCaps);
|
|
break;
|
|
|
|
case VIR_DOMAIN_DEVICE_SMARTCARD:
|
|
ret = qemuValidateDomainSmartcardDef(dev->data.smartcard, qemuCaps);
|
|
break;
|
|
|
|
case VIR_DOMAIN_DEVICE_RNG:
|
|
ret = qemuValidateDomainRNGDef(dev->data.rng, qemuCaps);
|
|
break;
|
|
|
|
case VIR_DOMAIN_DEVICE_REDIRDEV:
|
|
ret = qemuValidateDomainRedirdevDef(dev->data.redirdev, def, qemuCaps);
|
|
break;
|
|
|
|
case VIR_DOMAIN_DEVICE_WATCHDOG:
|
|
ret = qemuValidateDomainWatchdogDef(dev->data.watchdog, def);
|
|
break;
|
|
|
|
case VIR_DOMAIN_DEVICE_HOSTDEV:
|
|
ret = qemuValidateDomainDeviceDefHostdev(dev->data.hostdev, def,
|
|
qemuCaps);
|
|
break;
|
|
|
|
case VIR_DOMAIN_DEVICE_VIDEO:
|
|
ret = qemuValidateDomainDeviceDefVideo(dev->data.video, qemuCaps);
|
|
break;
|
|
|
|
case VIR_DOMAIN_DEVICE_DISK:
|
|
ret = qemuValidateDomainDeviceDefDisk(dev->data.disk, def, qemuCaps);
|
|
break;
|
|
|
|
case VIR_DOMAIN_DEVICE_CONTROLLER:
|
|
ret = qemuValidateDomainDeviceDefController(dev->data.controller, def,
|
|
qemuCaps);
|
|
break;
|
|
|
|
case VIR_DOMAIN_DEVICE_VSOCK:
|
|
ret = qemuValidateDomainDeviceDefVsock(dev->data.vsock, qemuCaps);
|
|
break;
|
|
|
|
case VIR_DOMAIN_DEVICE_TPM:
|
|
ret = qemuValidateDomainDeviceDefTPM(dev->data.tpm, def, qemuCaps);
|
|
break;
|
|
|
|
case VIR_DOMAIN_DEVICE_GRAPHICS:
|
|
ret = qemuValidateDomainDeviceDefGraphics(dev->data.graphics, def,
|
|
driver, qemuCaps);
|
|
break;
|
|
|
|
case VIR_DOMAIN_DEVICE_INPUT:
|
|
ret = qemuValidateDomainDeviceDefInput(dev->data.input, def, qemuCaps);
|
|
break;
|
|
|
|
case VIR_DOMAIN_DEVICE_MEMBALLOON:
|
|
ret = qemuValidateDomainDeviceDefMemballoon(dev->data.memballoon, qemuCaps);
|
|
break;
|
|
|
|
case VIR_DOMAIN_DEVICE_IOMMU:
|
|
ret = qemuValidateDomainDeviceDefIOMMU(dev->data.iommu, def, qemuCaps);
|
|
break;
|
|
|
|
case VIR_DOMAIN_DEVICE_FS:
|
|
ret = qemuValidateDomainDeviceDefFS(dev->data.fs, def, driver, qemuCaps);
|
|
break;
|
|
|
|
case VIR_DOMAIN_DEVICE_NVRAM:
|
|
ret = qemuValidateDomainDeviceDefNVRAM(dev->data.nvram, def, qemuCaps);
|
|
break;
|
|
|
|
case VIR_DOMAIN_DEVICE_HUB:
|
|
ret = qemuValidateDomainDeviceDefHub(dev->data.hub, qemuCaps);
|
|
break;
|
|
|
|
case VIR_DOMAIN_DEVICE_SOUND:
|
|
ret = qemuValidateDomainDeviceDefSound(dev->data.sound, qemuCaps);
|
|
break;
|
|
|
|
case VIR_DOMAIN_DEVICE_MEMORY:
|
|
ret = qemuValidateDomainDeviceDefMemory(dev->data.memory, qemuCaps);
|
|
break;
|
|
|
|
case VIR_DOMAIN_DEVICE_SHMEM:
|
|
ret = qemuValidateDomainDeviceDefShmem(dev->data.shmem, qemuCaps);
|
|
break;
|
|
|
|
case VIR_DOMAIN_DEVICE_AUDIO:
|
|
ret = qemuValidateDomainDeviceDefAudio(dev->data.audio, qemuCaps);
|
|
break;
|
|
|
|
case VIR_DOMAIN_DEVICE_LEASE:
|
|
case VIR_DOMAIN_DEVICE_PANIC:
|
|
case VIR_DOMAIN_DEVICE_NONE:
|
|
case VIR_DOMAIN_DEVICE_LAST:
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|