From nobody Thu Dec 26 21:46:34 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of lists.libvirt.org designates 8.43.85.245 as permitted sender) client-ip=8.43.85.245; envelope-from=devel-bounces@lists.libvirt.org; helo=lists.libvirt.org; Authentication-Results: mx.zohomail.com; dkim=fail; spf=pass (zohomail.com: domain of lists.libvirt.org designates 8.43.85.245 as permitted sender) smtp.mailfrom=devel-bounces@lists.libvirt.org; dmarc=fail(p=none dis=none) header.from=redhat.com Return-Path: Received: from lists.libvirt.org (lists.libvirt.org [8.43.85.245]) by mx.zohomail.com with SMTPS id 1731494237108544.1284647054687; Wed, 13 Nov 2024 02:37:17 -0800 (PST) Received: by lists.libvirt.org (Postfix, from userid 996) id 888F9180B; Wed, 13 Nov 2024 05:37:15 -0500 (EST) Received: from lists.libvirt.org (localhost [IPv6:::1]) by lists.libvirt.org (Postfix) with ESMTP id AF40A16B1; Wed, 13 Nov 2024 05:35:30 -0500 (EST) Received: by lists.libvirt.org (Postfix, from userid 996) id C9B601650; Wed, 13 Nov 2024 05:35:27 -0500 (EST) Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by lists.libvirt.org (Postfix) with ESMTPS id 88943E75 for ; Wed, 13 Nov 2024 05:35:25 -0500 (EST) Received: from mx-prod-mc-02.mail-002.prod.us-west-2.aws.redhat.com (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-216-OOUKxuvaPOeGYNwkIc0NCQ-1; Wed, 13 Nov 2024 05:35:23 -0500 Received: from mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com (mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.17]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mx-prod-mc-02.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS id 5E64419560B0 for ; Wed, 13 Nov 2024 10:35:22 +0000 (UTC) Received: from moe.brq.redhat.com (unknown [10.43.3.236]) by mx-prod-int-05.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP id 1D89E19560A3 for ; Wed, 13 Nov 2024 10:35:20 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on lists.libvirt.org X-Spam-Level: X-Spam-Status: No, score=-0.8 required=5.0 tests=DKIM_INVALID,DKIM_SIGNED, HEADER_FROM_DIFFERENT_DOMAINS,MAILING_LIST_MULTI,RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H3,RCVD_IN_MSPIKE_WL,RCVD_IN_VALIDITY_RPBL_BLOCKED, RCVD_IN_VALIDITY_SAFE_BLOCKED,SPF_HELO_NONE autolearn=unavailable autolearn_force=no version=3.4.4 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1731494125; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=OF1rPMqc1xhwp273vFOONjQVv8rQ25UVtKDNOeeeNFA=; b=Kf79hmYURsodpFDjGsR+XVEuUyT151vAhDa6qvILsd0PCvKmnoAbcfmm4Oqc4qrhemkXTr c5fsNxvLraunOiCJoq+W8BEvDpe154jA6Ut7qzI6tFHFg6ZO2QobdaMoLogBOA/3DgWh6O Y3LWfRz89+xs0FbdJ/C6TgzVqm0lKRE= X-MC-Unique: OOUKxuvaPOeGYNwkIc0NCQ-1 X-Mimecast-MFC-AGG-ID: OOUKxuvaPOeGYNwkIc0NCQ From: Michal Privoznik To: devel@lists.libvirt.org Subject: [PATCH] qemu: Move PostParse functions out of qemu_domain.c Date: Wed, 13 Nov 2024 11:35:19 +0100 Message-ID: <4a5393e2640fe796d7aefa220b2a5670d3df5474.1731494068.git.mprivozn@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.0 on 10.30.177.17 X-Mimecast-Spam-Score: 0 X-Mimecast-MFC-PROC-ID: RdKLuGjoAzf-JsEs_eYCuqxF3csFMaSgK8ScmT6W8E4_1731494122 X-Mimecast-Originator: redhat.com Content-Transfer-Encoding: quoted-printable Message-ID-Hash: RBZRFQEVYDSRKZ6JKQNTCFIUGQBMI63A X-Message-ID-Hash: RBZRFQEVYDSRKZ6JKQNTCFIUGQBMI63A X-MailFrom: mprivozn@redhat.com X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; emergency; loop; banned-address; member-moderation; header-match-config-1; header-match-config-2; header-match-config-3; header-match-devel.lists.libvirt.org-0; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; suspicious-header X-Mailman-Version: 3.2.2 Precedence: list List-Id: Development discussions about the libvirt library & tools Archived-At: List-Archive: List-Help: List-Post: List-Subscribe: List-Unsubscribe: X-ZohoMail-DKIM: fail (Header signature does not verify) X-ZM-MESSAGEID: 1731494238840116600 Content-Type: text/plain; charset="utf-8"; x-default="true" Problem with qemu_domain.c is that it's constantly growing. But there are few options for improvement. For instance, validation functions were moved out and now live in qemu_validate.c. We can do the same for PostParse functions, though since PostParse may modify domain definition, some functions need to be exported from qemu_domain.c. Signed-off-by: Michal Privoznik Reviewed-by: Martin Kletzander --- Best viewed via 'git show --color-moved'. po/POTFILES | 1 + src/qemu/meson.build | 1 + src/qemu/qemu_domain.c | 1898 +----------------------------------- src/qemu/qemu_domain.h | 16 +- src/qemu/qemu_postparse.c | 1925 +++++++++++++++++++++++++++++++++++++ src/qemu/qemu_postparse.h | 54 ++ tests/qemublocktest.c | 1 + 7 files changed, 2001 insertions(+), 1895 deletions(-) create mode 100644 src/qemu/qemu_postparse.c create mode 100644 src/qemu/qemu_postparse.h diff --git a/po/POTFILES b/po/POTFILES index 1ed4086d2c..c20781e1a8 100644 --- a/po/POTFILES +++ b/po/POTFILES @@ -189,6 +189,7 @@ src/qemu/qemu_monitor_text.c src/qemu/qemu_namespace.c src/qemu/qemu_nbdkit.c src/qemu/qemu_passt.c +src/qemu/qemu_postparse.c src/qemu/qemu_process.c src/qemu/qemu_qapi.c src/qemu/qemu_saveimage.c diff --git a/src/qemu/meson.build b/src/qemu/meson.build index 1d904bbc68..2a85e2e604 100644 --- a/src/qemu/meson.build +++ b/src/qemu/meson.build @@ -32,6 +32,7 @@ qemu_driver_sources =3D [ 'qemu_namespace.c', 'qemu_nbdkit.c', 'qemu_passt.c', + 'qemu_postparse.c', 'qemu_process.c', 'qemu_qapi.c', 'qemu_saveimage.c', diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index 562fa76a78..c798ef37fd 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -27,7 +27,6 @@ #include "qemu_cgroup.h" #include "qemu_command.h" #include "qemu_capabilities.h" -#include "qemu_firmware.h" #include "qemu_hostdev.h" #include "qemu_migration_params.h" #include "qemu_security.h" @@ -38,6 +37,7 @@ #include "qemu_checkpoint.h" #include "qemu_validate.h" #include "qemu_namespace.h" +#include "qemu_postparse.h" #include "viralloc.h" #include "virlog.h" #include "virerror.h" @@ -71,8 +71,6 @@ #include #include =20 -#define QEMU_QXL_VGAMEM_DEFAULT 16 * 1024 - #define VIR_FROM_THIS VIR_FROM_QEMU =20 VIR_LOG_INIT("qemu.qemu_domain"); @@ -2020,7 +2018,7 @@ qemuDomainObjPrivateAlloc(void *opaque) } =20 =20 -static int +int qemuStorageSourcePrivateDataAssignSecinfo(qemuDomainSecretInfo **secinfo, char **alias) { @@ -4001,26 +3999,6 @@ virXMLNamespace virQEMUDriverDomainXMLNamespace =3D { }; =20 =20 -static int -qemuDomainDefAddImplicitInputDevice(virDomainDef *def, - virQEMUCaps *qemuCaps) -{ - if (virQEMUCapsSupportsI8042(qemuCaps, def) && - def->features[VIR_DOMAIN_FEATURE_PS2] !=3D VIR_TRISTATE_SWITCH_OFF= ) { - if (virDomainDefMaybeAddInput(def, - VIR_DOMAIN_INPUT_TYPE_MOUSE, - VIR_DOMAIN_INPUT_BUS_PS2) < 0) - return -1; - - if (virDomainDefMaybeAddInput(def, - VIR_DOMAIN_INPUT_TYPE_KBD, - VIR_DOMAIN_INPUT_BUS_PS2) < 0) - return -1; - } - - return 0; -} - static int qemuDomainDefSuggestDefaultAudioBackend(virQEMUDriver *driver, virDomainDef *def, @@ -4168,7 +4146,7 @@ qemuDomainDefClearDefaultAudioBackend(virQEMUDriver *= driver, return 0; } =20 -static int +int qemuDomainDefAddDefaultAudioBackend(virQEMUDriver *driver, virDomainDef *def) { @@ -4227,7 +4205,7 @@ qemuDomainGetSCSIControllerModel(const virDomainDef *= def, } =20 =20 -static virDomainPanicModel +virDomainPanicModel qemuDomainDefaultPanicModel(const virDomainDef *def) { if (qemuDomainIsPSeries(def)) @@ -4246,834 +4224,6 @@ qemuDomainDefaultPanicModel(const virDomainDef *def) } =20 =20 -static int -qemuDomainDefAddDefaultDevices(virQEMUDriver *driver, - virDomainDef *def, - virQEMUCaps *qemuCaps) -{ - bool addDefaultUSB =3D false; - int usbModel =3D -1; /* "default for machinetype" */ - int pciRoot; /* index within def->controllers */ - bool addImplicitSATA =3D false; - bool addPCIRoot =3D false; - bool addPCIeRoot =3D false; - bool addDefaultMemballoon =3D false; - bool addDefaultUSBKBD =3D false; - bool addDefaultUSBMouse =3D false; - bool addPanicDevice =3D false; - bool addITCOWatchdog =3D false; - bool addIOMMU =3D false; - - /* add implicit input devices */ - if (qemuDomainDefAddImplicitInputDevice(def, qemuCaps) < 0) - return -1; - - /* Add implicit PCI root controller if the machine has one */ - switch (def->os.arch) { - case VIR_ARCH_I686: - case VIR_ARCH_X86_64: - addDefaultMemballoon =3D true; - - if (STREQ(def->os.machine, "isapc")) { - break; - } - - addDefaultUSB =3D true; - - if (qemuDomainIsQ35(def)) { - addPCIeRoot =3D true; - addImplicitSATA =3D true; - addITCOWatchdog =3D true; - - if (virDomainDefGetVcpusMax(def) > QEMU_MAX_VCPUS_WITHOUT_EIM)= { - addIOMMU =3D true; - } - - /* Prefer adding a USB3 controller if supported, fall back - * to USB2 if there is no USB3 available, and if that's - * unavailable don't add anything. - */ - if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_QEMU_XHCI)) - usbModel =3D VIR_DOMAIN_CONTROLLER_MODEL_USB_QEMU_XHCI; - else if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_NEC_USB_XHCI)) - usbModel =3D VIR_DOMAIN_CONTROLLER_MODEL_USB_NEC_XHCI; - else if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_ICH9_USB_EHCI1)) - usbModel =3D VIR_DOMAIN_CONTROLLER_MODEL_USB_ICH9_EHCI1; - else - addDefaultUSB =3D false; - break; - } - if (qemuDomainIsI440FX(def)) - addPCIRoot =3D true; - break; - - case VIR_ARCH_ARMV6L: - case VIR_ARCH_ARMV7L: - case VIR_ARCH_ARMV7B: - case VIR_ARCH_AARCH64: - if (STREQ(def->os.machine, "versatilepb")) - addPCIRoot =3D true; - - /* Add default USB for the two machine types which historically - * supported -usb */ - if (STREQ(def->os.machine, "versatilepb") || - STRPREFIX(def->os.machine, "realview")) { - addDefaultUSB =3D true; - usbModel =3D VIR_DOMAIN_CONTROLLER_MODEL_USB_PCI_OHCI; - } - - if (qemuDomainIsARMVirt(def)) - addPCIeRoot =3D true; - - break; - - case VIR_ARCH_PPC64: - case VIR_ARCH_PPC64LE: - addPCIRoot =3D true; - addDefaultUSB =3D true; - addDefaultUSBKBD =3D true; - addDefaultUSBMouse =3D true; - addDefaultMemballoon =3D true; - /* For pSeries guests, the firmware provides the same - * functionality as the pvpanic device, so automatically - * add the definition if not already present */ - if (qemuDomainIsPSeries(def)) - addPanicDevice =3D true; - break; - - case VIR_ARCH_ALPHA: - case VIR_ARCH_PPC: - case VIR_ARCH_PPCEMB: - case VIR_ARCH_SH4: - case VIR_ARCH_SH4EB: - addDefaultUSB =3D true; - addDefaultMemballoon =3D true; - addPCIRoot =3D true; - break; - - case VIR_ARCH_RISCV32: - case VIR_ARCH_RISCV64: - addDefaultMemballoon =3D true; - if (qemuDomainIsRISCVVirt(def)) - addPCIeRoot =3D true; - break; - - case VIR_ARCH_S390: - case VIR_ARCH_S390X: - addDefaultMemballoon =3D true; - addPanicDevice =3D true; - addPCIRoot =3D virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_ZPCI); - break; - - case VIR_ARCH_SPARC64: - addDefaultUSB =3D true; - addDefaultMemballoon =3D true; - addPCIRoot =3D true; - break; - - case VIR_ARCH_MIPS: - case VIR_ARCH_MIPSEL: - case VIR_ARCH_MIPS64: - case VIR_ARCH_MIPS64EL: - addDefaultUSB =3D true; - addDefaultMemballoon =3D true; - if (qemuDomainIsMipsMalta(def)) - addPCIRoot =3D true; - break; - - case VIR_ARCH_LOONGARCH64: - addPCIeRoot =3D true; - break; - - case VIR_ARCH_CRIS: - case VIR_ARCH_ITANIUM: - case VIR_ARCH_LM32: - case VIR_ARCH_M68K: - case VIR_ARCH_MICROBLAZE: - case VIR_ARCH_MICROBLAZEEL: - case VIR_ARCH_OR32: - case VIR_ARCH_PARISC: - case VIR_ARCH_PARISC64: - case VIR_ARCH_PPCLE: - case VIR_ARCH_SPARC: - case VIR_ARCH_UNICORE32: - case VIR_ARCH_XTENSA: - case VIR_ARCH_XTENSAEB: - case VIR_ARCH_NONE: - case VIR_ARCH_LAST: - default: - break; - } - - if (addDefaultUSB && - virDomainControllerFind(def, VIR_DOMAIN_CONTROLLER_TYPE_USB, 0) < = 0 && - virDomainDefAddUSBController(def, 0, usbModel) < 0) - return -1; - - if (addImplicitSATA && - virDomainDefMaybeAddController( - def, VIR_DOMAIN_CONTROLLER_TYPE_SATA, 0, -1) < 0) - return -1; - - pciRoot =3D virDomainControllerFind(def, VIR_DOMAIN_CONTROLLER_TYPE_PC= I, 0); - - /* NB: any machine that sets addPCIRoot to true must also return - * true from the function qemuDomainSupportsPCI(). - */ - if (addPCIRoot) { - if (pciRoot >=3D 0) { - if (def->controllers[pciRoot]->model !=3D VIR_DOMAIN_CONTROLLE= R_MODEL_PCI_ROOT) { - virReportError(VIR_ERR_XML_ERROR, - _("The PCI controller with index=3D'0' must= be model=3D'pci-root' for this machine type, but model=3D'%1$s' was found = instead"), - virDomainControllerModelPCITypeToString(def= ->controllers[pciRoot]->model)); - return -1; - } - } else if (!virDomainDefAddController(def, VIR_DOMAIN_CONTROLLER_T= YPE_PCI, 0, - VIR_DOMAIN_CONTROLLER_MODEL_= PCI_ROOT)) { - return -1; - } - } - - /* When a machine has a pcie-root, make sure that there is always - * a dmi-to-pci-bridge controller added as bus 1, and a pci-bridge - * as bus 2, so that standard PCI devices can be connected - * - * NB: any machine that sets addPCIeRoot to true must also return - * true from the function qemuDomainSupportsPCI(). - */ - if (addPCIeRoot) { - if (pciRoot >=3D 0) { - if (def->controllers[pciRoot]->model !=3D VIR_DOMAIN_CONTROLLE= R_MODEL_PCIE_ROOT) { - virReportError(VIR_ERR_XML_ERROR, - _("The PCI controller with index=3D'0' must= be model=3D'pcie-root' for this machine type, but model=3D'%1$s' was found= instead"), - virDomainControllerModelPCITypeToString(def= ->controllers[pciRoot]->model)); - return -1; - } - } else if (!virDomainDefAddController(def, VIR_DOMAIN_CONTROLLER_T= YPE_PCI, 0, - VIR_DOMAIN_CONTROLLER_MODEL_P= CIE_ROOT)) { - return -1; - } - } - - if (addDefaultMemballoon && !def->memballoon) { - virDomainMemballoonDef *memballoon; - memballoon =3D g_new0(virDomainMemballoonDef, 1); - - memballoon->model =3D VIR_DOMAIN_MEMBALLOON_MODEL_VIRTIO; - def->memballoon =3D memballoon; - } - - if (addDefaultUSBMouse) { - bool hasUSBTablet =3D false; - size_t j; - - for (j =3D 0; j < def->ninputs; j++) { - if (def->inputs[j]->type =3D=3D VIR_DOMAIN_INPUT_TYPE_TABLET && - def->inputs[j]->bus =3D=3D VIR_DOMAIN_INPUT_BUS_USB) { - hasUSBTablet =3D true; - break; - } - } - - /* Historically, we have automatically added USB keyboard and - * mouse to some guests. While the former device is generally - * safe to have, adding the latter is undesiderable if a USB - * tablet is already present in the guest */ - if (hasUSBTablet) - addDefaultUSBMouse =3D false; - } - - if (addDefaultUSBKBD && - def->ngraphics > 0 && - virDomainDefMaybeAddInput(def, - VIR_DOMAIN_INPUT_TYPE_KBD, - VIR_DOMAIN_INPUT_BUS_USB) < 0) - return -1; - - if (addDefaultUSBMouse && - def->ngraphics > 0 && - virDomainDefMaybeAddInput(def, - VIR_DOMAIN_INPUT_TYPE_MOUSE, - VIR_DOMAIN_INPUT_BUS_USB) < 0) - return -1; - - if (addPanicDevice) { - virDomainPanicModel defaultModel =3D qemuDomainDefaultPanicModel(d= ef); - size_t j; - - for (j =3D 0; j < def->npanics; j++) { - if (def->panics[j]->model =3D=3D VIR_DOMAIN_PANIC_MODEL_DEFAUL= T || - def->panics[j]->model =3D=3D defaultModel) - break; - } - - if (j =3D=3D def->npanics) { - virDomainPanicDef *panic =3D g_new0(virDomainPanicDef, 1); - - VIR_APPEND_ELEMENT_COPY(def->panics, def->npanics, panic); - } - } - - if (addITCOWatchdog) { - size_t i =3D 0; - - for (i =3D 0; i < def->nwatchdogs; i++) { - if (def->watchdogs[i]->model =3D=3D VIR_DOMAIN_WATCHDOG_MODEL_= ITCO) - break; - } - - if (i =3D=3D def->nwatchdogs) { - virDomainWatchdogDef *watchdog =3D g_new0(virDomainWatchdogDef= , 1); - - watchdog->model =3D VIR_DOMAIN_WATCHDOG_MODEL_ITCO; - if (def->nwatchdogs) - watchdog->action =3D def->watchdogs[0]->action; - else - watchdog->action =3D VIR_DOMAIN_WATCHDOG_ACTION_RESET; - - VIR_APPEND_ELEMENT(def->watchdogs, def->nwatchdogs, watchdog); - } - } - - if (addIOMMU && !def->iommu && - virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_INTEL_IOMMU) && - virQEMUCapsGet(qemuCaps, QEMU_CAPS_INTEL_IOMMU_INTREMAP) && - virQEMUCapsGet(qemuCaps, QEMU_CAPS_INTEL_IOMMU_EIM)) { - g_autoptr(virDomainIOMMUDef) iommu =3D NULL; - - iommu =3D virDomainIOMMUDefNew(); - iommu->model =3D VIR_DOMAIN_IOMMU_MODEL_INTEL; - /* eim requires intremap. */ - iommu->intremap =3D VIR_TRISTATE_SWITCH_ON; - iommu->eim =3D VIR_TRISTATE_SWITCH_ON; - - def->iommu =3D g_steal_pointer(&iommu); - } - - if (qemuDomainDefAddDefaultAudioBackend(driver, def) < 0) - return -1; - - return 0; -} - - -/** - * qemuDomainDefEnableDefaultFeatures: - * @def: domain definition - * @qemuCaps: QEMU capabilities - * - * Make sure that features that should be enabled by default are actually - * enabled and configure default values related to those features. - */ -static void -qemuDomainDefEnableDefaultFeatures(virDomainDef *def, - virQEMUCaps *qemuCaps) -{ - /* The virt machine type always uses GIC: if the relevant information - * was not included in the domain XML, we need to choose a suitable - * GIC version ourselves */ - if ((def->features[VIR_DOMAIN_FEATURE_GIC] =3D=3D VIR_TRISTATE_SWITCH_= ABSENT && - qemuDomainIsARMVirt(def)) || - (def->features[VIR_DOMAIN_FEATURE_GIC] =3D=3D VIR_TRISTATE_SWITCH_= ON && - def->gic_version =3D=3D VIR_GIC_VERSION_NONE)) { - virGICVersion version; - - VIR_DEBUG("Looking for usable GIC version in domain capabilities"); - for (version =3D VIR_GIC_VERSION_LAST - 1; - version > VIR_GIC_VERSION_NONE; - version--) { - - /* We want to use the highest available GIC version for guests; - * however, the emulated GICv3 is currently lacking a MSI cont= roller, - * making it unsuitable for the pure PCIe topology we aim for. - * - * For that reason, we skip this step entirely for TCG guests, - * and rely on the code below to pick the default version, GIC= v2, - * which supports all the features we need. - * - * See https://bugzilla.redhat.com/show_bug.cgi?id=3D1414081 */ - if (version =3D=3D VIR_GIC_VERSION_3 && - def->virtType =3D=3D VIR_DOMAIN_VIRT_QEMU) { - continue; - } - - if (virQEMUCapsSupportsGICVersion(qemuCaps, - def->virtType, - version)) { - VIR_DEBUG("Using GIC version %s", - virGICVersionTypeToString(version)); - def->gic_version =3D version; - break; - } - } - - /* Use the default GIC version (GICv2) as a last-ditch attempt - * if no match could be found above */ - if (def->gic_version =3D=3D VIR_GIC_VERSION_NONE) { - VIR_DEBUG("Using GIC version 2 (default)"); - def->gic_version =3D VIR_GIC_VERSION_2; - } - - /* Even if we haven't found a usable GIC version in the domain - * capabilities, we still want to enable this */ - def->features[VIR_DOMAIN_FEATURE_GIC] =3D VIR_TRISTATE_SWITCH_ON; - } -} - - -static int -qemuCanonicalizeMachine(virDomainDef *def, virQEMUCaps *qemuCaps) -{ - const char *canon; - - if (!(canon =3D virQEMUCapsGetCanonicalMachine(qemuCaps, def->virtType, - def->os.machine))) - return 0; - - if (STRNEQ(canon, def->os.machine)) { - char *tmp; - tmp =3D g_strdup(canon); - VIR_FREE(def->os.machine); - def->os.machine =3D tmp; - } - - return 0; -} - - -static int -qemuDomainRecheckInternalPaths(virDomainDef *def, - virQEMUDriverConfig *cfg, - unsigned int flags) -{ - size_t i =3D 0; - size_t j =3D 0; - - for (i =3D 0; i < def->ngraphics; ++i) { - virDomainGraphicsDef *graphics =3D def->graphics[i]; - - for (j =3D 0; j < graphics->nListens; ++j) { - virDomainGraphicsListenDef *glisten =3D &graphics->listens[j]; - - /* This will happen only if we parse XML from old libvirts whe= re - * unix socket was available only for VNC graphics. In this - * particular case we should follow the behavior and if we rem= ove - * the auto-generated socket based on config option from qemu.= conf - * we need to change the listen type to address. */ - if (graphics->type =3D=3D VIR_DOMAIN_GRAPHICS_TYPE_VNC && - glisten->type =3D=3D VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_SOCKE= T && - glisten->socket && - !glisten->autoGenerated && - STRPREFIX(glisten->socket, cfg->libDir)) { - if (flags & VIR_DOMAIN_DEF_PARSE_INACTIVE) { - VIR_FREE(glisten->socket); - glisten->type =3D VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_ADDR= ESS; - } else { - glisten->fromConfig =3D true; - } - } - } - } - - return 0; -} - - -static int -qemuDomainDefBootPostParse(virDomainDef *def, - virQEMUDriver *driver, - unsigned int parseFlags) -{ - bool abiUpdate =3D !!(parseFlags & VIR_DOMAIN_DEF_PARSE_ABI_UPDATE); - - /* If we're loading an existing configuration from disk, we - * should try as hard as possible to preserve historical - * behavior. In particular, firmware autoselection being enabled - * could never have resulted, before libvirt 9.2.0, in anything - * but a raw firmware image being selected. - * - * In order to ensure that existing domains keep working even if - * a firmware descriptor for a build with a different format is - * given higher priority, explicitly add this requirement to the - * definition before performing firmware selection */ - if (!abiUpdate && def->os.firmware) { - if (!def->os.loader) - def->os.loader =3D virDomainLoaderDefNew(); - if (!def->os.loader->format) - def->os.loader->format =3D VIR_STORAGE_FILE_RAW; - } - - /* Firmware selection can fail for a number of reasons, but the - * most likely one is that the requested configuration contains - * mistakes or includes constraints that are impossible to - * satisfy on the current system. - * - * If that happens, we have to react differently based on the - * situation: if we're defining a new domain or updating its ABI, - * we should let the user know immediately so that they can - * change the requested configuration, hopefully into one that we - * can work with; if we're loading the configuration of an - * existing domain from disk, however, we absolutely cannot error - * out here, or the domain will disappear. - * - * To handle the second case gracefully, we clear any reported - * errors and continue as if nothing had happened. When it's time - * to start the domain, qemuFirmwareFillDomain() will be run - * again, fail in the same way, and at that point we'll have a - * chance to inform the user of any issues */ - if (qemuFirmwareFillDomain(driver, def, abiUpdate) < 0) { - if (abiUpdate) { - return -1; - } else { - virResetLastError(); - return 0; - } - } - - return 0; -} - - -static int -qemuDomainDefMachinePostParse(virDomainDef *def, - virQEMUCaps *qemuCaps) -{ - if (!def->os.machine) { - const char *machine =3D virQEMUCapsGetPreferredMachine(qemuCaps, - def->virtType= ); - if (!machine) { - virReportError(VIR_ERR_INVALID_ARG, - _("could not get preferred machine for %1$s typ= e=3D%2$s"), - def->emulator, - virDomainVirtTypeToString(def->virtType)); - return -1; - } - - def->os.machine =3D g_strdup(machine); - } - - if (qemuCanonicalizeMachine(def, qemuCaps) < 0) - return -1; - - return 0; -} - - -static int -qemuDomainDefVcpusPostParse(virDomainDef *def) -{ - unsigned int maxvcpus =3D virDomainDefGetVcpusMax(def); - virDomainVcpuDef *vcpu; - virDomainVcpuDef *prevvcpu; - size_t i; - bool has_order =3D false; - - /* vcpu 0 needs to be present, first, and non-hotpluggable */ - vcpu =3D virDomainDefGetVcpu(def, 0); - if (!vcpu->online) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("vcpu 0 can't be offline")); - return -1; - } - if (vcpu->hotpluggable =3D=3D VIR_TRISTATE_BOOL_YES) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("vcpu0 can't be hotpluggable")); - return -1; - } - if (vcpu->order !=3D 0 && vcpu->order !=3D 1) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("vcpu0 must be enabled first")); - return -1; - } - - if (vcpu->order !=3D 0) - has_order =3D true; - - prevvcpu =3D vcpu; - - /* all online vcpus or non online vcpu need to have order set */ - for (i =3D 1; i < maxvcpus; i++) { - vcpu =3D virDomainDefGetVcpu(def, i); - - if (vcpu->online && - (vcpu->order !=3D 0) !=3D has_order) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("all vcpus must have either set or unset orde= r")); - return -1; - } - - /* few conditions for non-hotpluggable (thus online) vcpus */ - if (vcpu->hotpluggable =3D=3D VIR_TRISTATE_BOOL_NO) { - /* they can be ordered only at the beginning */ - if (prevvcpu->hotpluggable =3D=3D VIR_TRISTATE_BOOL_YES) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("online non-hotpluggable vcpus need to be= ordered prior to hotplugable vcpus")); - return -1; - } - - /* they need to be in order (qemu doesn't support any order ye= t). - * Also note that multiple vcpus may share order on some platf= orms */ - if (prevvcpu->order > vcpu->order) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("online non-hotpluggable vcpus must be or= dered in ascending order")); - return -1; - } - } - - prevvcpu =3D vcpu; - } - - return 0; -} - - -static int -qemuDomainDefSetDefaultCPU(virDomainDef *def, - virArch hostarch, - virQEMUCaps *qemuCaps) -{ - const char *model; - - if (def->cpu && - (def->cpu->mode !=3D VIR_CPU_MODE_CUSTOM || - def->cpu->model)) - return 0; - - if (!virCPUArchIsSupported(def->os.arch)) - return 0; - - /* Default CPU model info from QEMU is usable for TCG only except for - * x86, s390, and ppc64. */ - if (!ARCH_IS_X86(def->os.arch) && - !ARCH_IS_S390(def->os.arch) && - !ARCH_IS_PPC64(def->os.arch) && - def->virtType !=3D VIR_DOMAIN_VIRT_QEMU) - return 0; - - model =3D virQEMUCapsGetMachineDefaultCPU(qemuCaps, def->os.machine, d= ef->virtType); - if (!model) { - VIR_DEBUG("Unknown default CPU model for domain '%s'", def->name); - return 0; - } - - if (STREQ(model, "host") && def->virtType !=3D VIR_DOMAIN_VIRT_KVM) { - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("QEMU reports invalid default CPU model \"host\" = for non-kvm domain virt type")); - return -1; - } - - if (!def->cpu) - def->cpu =3D virCPUDefNew(); - - def->cpu->type =3D VIR_CPU_TYPE_GUEST; - - if (STREQ(model, "host")) { - if (ARCH_IS_S390(def->os.arch) && - virQEMUCapsIsCPUModeSupported(qemuCaps, hostarch, def->virtTyp= e, - VIR_CPU_MODE_HOST_MODEL, - def->os.machine)) { - def->cpu->mode =3D VIR_CPU_MODE_HOST_MODEL; - } else { - def->cpu->mode =3D VIR_CPU_MODE_HOST_PASSTHROUGH; - } - - VIR_DEBUG("Setting default CPU mode for domain '%s' to %s", - def->name, virCPUModeTypeToString(def->cpu->mode)); - } else { - /* We need to turn off all CPU checks when the domain is started - * because the default CPU (e.g., qemu64) may not be runnable on a= ny - * host. QEMU will just disable the unavailable features and we wi= ll - * update the CPU definition accordingly and set check to FULL when - * starting the domain. */ - def->cpu->check =3D VIR_CPU_CHECK_NONE; - def->cpu->mode =3D VIR_CPU_MODE_CUSTOM; - def->cpu->match =3D VIR_CPU_MATCH_EXACT; - def->cpu->fallback =3D VIR_CPU_FALLBACK_FORBID; - def->cpu->model =3D g_strdup(model); - - VIR_DEBUG("Setting default CPU model for domain '%s' to %s", - def->name, model); - } - - return 0; -} - - -static int -qemuDomainDefCPUPostParse(virDomainDef *def, - virQEMUCaps *qemuCaps) -{ - virCPUFeatureDef *sveFeature =3D NULL; - bool sveVectorLengthsProvided =3D false; - size_t i; - - if (!def->cpu) - return 0; - - for (i =3D 0; i < def->cpu->nfeatures; i++) { - virCPUFeatureDef *feature =3D &def->cpu->features[i]; - - if (STREQ(feature->name, "sve")) { - sveFeature =3D feature; - } else if (STRPREFIX(feature->name, "sve")) { - sveVectorLengthsProvided =3D true; - } - } - - if (sveVectorLengthsProvided) { - if (sveFeature) { - if (sveFeature->policy =3D=3D VIR_CPU_FEATURE_DISABLE || - sveFeature->policy =3D=3D VIR_CPU_FEATURE_FORBID) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("SVE disabled, but SVE vector lengths pro= vided")); - return -1; - } else { - sveFeature->policy =3D VIR_CPU_FEATURE_REQUIRE; - } - } else { - VIR_RESIZE_N(def->cpu->features, def->cpu->nfeatures_max, - def->cpu->nfeatures, 1); - - def->cpu->features[def->cpu->nfeatures].name =3D g_strdup("sve= "); - def->cpu->features[def->cpu->nfeatures].policy =3D VIR_CPU_FEA= TURE_REQUIRE; - - def->cpu->nfeatures++; - } - } - - /* Running domains were either started before QEMU_CAPS_CPU_MIGRATABLE= was - * introduced and thus we can't rely on it or they already have the - * migratable default set. */ - if (def->id =3D=3D -1 && - qemuCaps && - def->cpu->mode =3D=3D VIR_CPU_MODE_HOST_PASSTHROUGH && - def->cpu->migratable =3D=3D VIR_TRISTATE_SWITCH_ABSENT) { - if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_CPU_MIGRATABLE)) - def->cpu->migratable =3D VIR_TRISTATE_SWITCH_ON; - else if (ARCH_IS_X86(def->os.arch)) - def->cpu->migratable =3D VIR_TRISTATE_SWITCH_OFF; - } - - /* Nothing to be done if only CPU topology is specified. */ - if (def->cpu->mode =3D=3D VIR_CPU_MODE_CUSTOM && - !def->cpu->model) - return 0; - - if (def->cpu->check !=3D VIR_CPU_CHECK_DEFAULT) - return 0; - - switch ((virCPUMode) def->cpu->mode) { - case VIR_CPU_MODE_HOST_PASSTHROUGH: - case VIR_CPU_MODE_MAXIMUM: - def->cpu->check =3D VIR_CPU_CHECK_NONE; - break; - - case VIR_CPU_MODE_HOST_MODEL: - def->cpu->check =3D VIR_CPU_CHECK_PARTIAL; - break; - - case VIR_CPU_MODE_CUSTOM: - /* Custom CPUs in TCG mode are not compared to host CPU by default= . */ - if (def->virtType =3D=3D VIR_DOMAIN_VIRT_QEMU) - def->cpu->check =3D VIR_CPU_CHECK_NONE; - else - def->cpu->check =3D VIR_CPU_CHECK_PARTIAL; - break; - - case VIR_CPU_MODE_LAST: - break; - } - - return 0; -} - - -static int -qemuDomainDefTsegPostParse(virDomainDef *def, - virQEMUCaps *qemuCaps) -{ - if (def->features[VIR_DOMAIN_FEATURE_SMM] !=3D VIR_TRISTATE_SWITCH_ON) - return 0; - - if (!def->tseg_specified) - return 0; - - if (!qemuDomainIsQ35(def)) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("SMM TSEG is only supported with q35 machine type= ")); - return -1; - } - - if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_MCH_EXTENDED_TSEG_MBYTES)) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("Setting TSEG size is not supported with this QEM= U binary")); - return -1; - } - - if (def->tseg_size & ((1 << 20) - 1)) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("SMM TSEG size must be divisible by 1 MiB")); - return -1; - } - - return 0; -} - - -static int -qemuDomainDefNumaAutoAdd(virDomainDef *def, - unsigned int parseFlags) -{ - bool abiUpdate =3D !!(parseFlags & VIR_DOMAIN_DEF_PARSE_ABI_UPDATE); - unsigned long long nodeMem; - size_t i; - - if (!abiUpdate || - !virDomainDefHasMemoryHotplug(def) || - virDomainNumaGetNodeCount(def->numa) > 0) { - return 0; - } - - nodeMem =3D virDomainDefGetMemoryTotal(def); - - if (!def->numa) - def->numa =3D virDomainNumaNew(); - - virDomainNumaSetNodeCount(def->numa, 1); - - for (i =3D 0; i < def->nmems; i++) { - virDomainMemoryDef *mem =3D def->mems[i]; - - if (mem->size > nodeMem) { - virReportError(VIR_ERR_XML_ERROR, "%s", - _("Total size of memory devices exceeds the tot= al memory size")); - return -1; - } - - nodeMem -=3D mem->size; - - switch (mem->model) { - case VIR_DOMAIN_MEMORY_MODEL_DIMM: - case VIR_DOMAIN_MEMORY_MODEL_NVDIMM: - case VIR_DOMAIN_MEMORY_MODEL_VIRTIO_MEM: - if (mem->targetNode =3D=3D -1) - mem->targetNode =3D 0; - break; - - case VIR_DOMAIN_MEMORY_MODEL_NONE: - case VIR_DOMAIN_MEMORY_MODEL_VIRTIO_PMEM: - case VIR_DOMAIN_MEMORY_MODEL_SGX_EPC: - case VIR_DOMAIN_MEMORY_MODEL_LAST: - break; - } - } - - virDomainNumaSetNodeMemorySize(def->numa, 0, nodeMem); - - return 0; -} - - /** * qemuDomainDefNumaCPUsRectify: * @numa: pointer to numa definition @@ -5106,141 +4256,6 @@ qemuDomainDefNumaCPUsRectify(virDomainDef *def, } =20 =20 -static int -qemuDomainDefNumaCPUsPostParse(virDomainDef *def, - virQEMUCaps *qemuCaps, - unsigned int parseFlags) -{ - if (qemuDomainDefNumaAutoAdd(def, parseFlags) < 0) - return -1; - - return qemuDomainDefNumaCPUsRectify(def, qemuCaps); -} - - -static int -qemuDomainDefPostParseBasic(virDomainDef *def, - void *opaque G_GNUC_UNUSED) -{ - virQEMUDriver *driver =3D opaque; - - /* check for emulator and create a default one if needed */ - if (!def->emulator) { - if (!(def->emulator =3D virQEMUCapsGetDefaultEmulator( - driver->hostarch, def->os.arch))) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("No emulator found for arch '%1$s'"), - virArchToString(def->os.arch)); - return 1; - } - } - - return 0; -} - - -/** - * qemuDomainDefACPIPostParse: - * @def: domain definition - * @qemuCaps: qemu capabilities object - * - * Fixup the use of ACPI flag on certain architectures that never supporte= d it - * and users for some reason used it, which would break migration to newer - * libvirt versions which check whether given machine type supports ACPI. - * - * The fixup is done in post-parse as it's hard to update the ABI stability - * check on source of the migration. - */ -static void -qemuDomainDefACPIPostParse(virDomainDef *def, - virQEMUCaps *qemuCaps, - unsigned int parseFlags) -{ - /* Only cases when ACPI is enabled need to be fixed up */ - if (def->features[VIR_DOMAIN_FEATURE_ACPI] !=3D VIR_TRISTATE_SWITCH_ON) - return; - - /* Strip the feature only for non-fresh configs, in order to s= till - * produce an error if the feature is present in a newly defined one. - * - * The use of the VIR_DOMAIN_DEF_PARSE_ABI_UPDATE looks counter-intuit= ive, - * but it's used only in qemuDomainCreateXML/qemuDomainDefineXMLFlags = APIs - * */ - if (parseFlags & VIR_DOMAIN_DEF_PARSE_ABI_UPDATE) - return; - - /* This fixup is applicable _only_ on architectures which were present= as of - * libvirt-9.2 and *never* supported ACPI. The fixup is currently done= only - * for existing users of s390(x) to fix migration for configs which had - * despite being ignored. - */ - if (def->os.arch !=3D VIR_ARCH_S390 && - def->os.arch !=3D VIR_ARCH_S390X) - return; - - /* To be sure, we only strip ACPI if given machine type doesn't suppor= t it */ - if (virQEMUCapsMachineSupportsACPI(qemuCaps, def->virtType, def->os.ma= chine) !=3D VIR_TRISTATE_BOOL_NO) - return; - - def->features[VIR_DOMAIN_FEATURE_ACPI] =3D VIR_TRISTATE_SWITCH_ABSENT; -} - - -static int -qemuDomainDefPostParse(virDomainDef *def, - unsigned int parseFlags, - void *opaque, - void *parseOpaque) -{ - virQEMUDriver *driver =3D opaque; - g_autoptr(virQEMUDriverConfig) cfg =3D virQEMUDriverGetConfig(driver); - virQEMUCaps *qemuCaps =3D parseOpaque; - - /* Note that qemuCaps may be NULL when this function is called. This - * function shall not fail in that case. It will be re-run on VM start= up - * with the capabilities populated. - */ - if (!qemuCaps) - return 1; - - if (qemuDomainDefMachinePostParse(def, qemuCaps) < 0) - return -1; - - qemuDomainDefACPIPostParse(def, qemuCaps, parseFlags); - - if (qemuDomainDefBootPostParse(def, driver, parseFlags) < 0) - return -1; - - if (qemuDomainDefAddDefaultDevices(driver, def, qemuCaps) < 0) - return -1; - - if (qemuDomainDefSetDefaultCPU(def, driver->hostarch, qemuCaps) < 0) - return -1; - - qemuDomainDefEnableDefaultFeatures(def, qemuCaps); - - if (qemuDomainRecheckInternalPaths(def, cfg, parseFlags) < 0) - return -1; - - if (qemuSecurityVerify(driver->securityManager, def) < 0) - return -1; - - if (qemuDomainDefVcpusPostParse(def) < 0) - return -1; - - if (qemuDomainDefCPUPostParse(def, qemuCaps) < 0) - return -1; - - if (qemuDomainDefTsegPostParse(def, qemuCaps) < 0) - return -1; - - if (qemuDomainDefNumaCPUsPostParse(def, qemuCaps, parseFlags) < 0) - return -1; - - return 0; -} - - int qemuDomainValidateActualNetDef(const virDomainNetDef *net, virQEMUCaps *qemuCaps G_GNUC_UNUSED) @@ -5605,63 +4620,6 @@ qemuDomainValidateStorageSource(virStorageSource *sr= c, } =20 =20 -/** - * qemuDomainDefaultNetModel: - * @def: domain definition - * @qemuCaps: qemu capabilities - * - * Returns the default network model for a given domain. Note that if @qem= uCaps - * is NULL this function may return NULL if the default model depends on t= he - * capabilities. - */ -static int -qemuDomainDefaultNetModel(const virDomainDef *def, - virQEMUCaps *qemuCaps) -{ - /* When there are no backwards compatibility concerns getting in - * the way, virtio is a good default */ - if (ARCH_IS_S390(def->os.arch) || - qemuDomainIsLoongArchVirt(def) || - qemuDomainIsRISCVVirt(def)) { - return VIR_DOMAIN_NET_MODEL_VIRTIO; - } - - if (ARCH_IS_ARM(def->os.arch)) { - if (STREQ(def->os.machine, "versatilepb")) - return VIR_DOMAIN_NET_MODEL_SMC91C111; - - if (qemuDomainIsARMVirt(def)) - return VIR_DOMAIN_NET_MODEL_VIRTIO; - - /* Incomplete. vexpress (and a few others) use this, but not all - * arm boards */ - return VIR_DOMAIN_NET_MODEL_LAN9118; - } - - /* In all other cases the model depends on the capabilities. If they w= ere - * not provided don't report any default. */ - if (!qemuCaps) - return VIR_DOMAIN_NET_MODEL_UNKNOWN; - - /* Try several network devices in turn; each of these devices is - * less likely be supported out-of-the-box by the guest operating - * system than the previous one */ - if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_RTL8139)) - return VIR_DOMAIN_NET_MODEL_RTL8139; - - if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_E1000)) - return VIR_DOMAIN_NET_MODEL_E1000; - - if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_VIRTIO_NET)) - return VIR_DOMAIN_NET_MODEL_VIRTIO; - - /* We've had no luck detecting support for any network device, - * but we have to return something: might as well be rtl8139 */ - return VIR_DOMAIN_NET_MODEL_RTL8139; -} - - - static bool qemuDomainChrMatchDefaultPath(const char *prefix, const char *infix, @@ -5707,7 +4665,7 @@ qemuDomainChrMatchDefaultPath(const char *prefix, * Please note, as of libvirt 9.7.0 the channelTargetDir is no longer deri= ved * from cfg->libDir but rather cfg->stateDir. */ -static void +void qemuDomainChrDefDropDefaultPath(virDomainChrDef *chr, virQEMUDriver *driver) { @@ -5750,640 +4708,7 @@ qemuDomainChrDefDropDefaultPath(virDomainChrDef *ch= r, } =20 =20 -static int -qemuDomainShmemDefPostParse(virDomainShmemDef *shm) -{ - /* This was the default since the introduction of this device. */ - if (shm->model !=3D VIR_DOMAIN_SHMEM_MODEL_IVSHMEM_DOORBELL && !shm->s= ize) - shm->size =3D 4 << 20; - - /* Nothing more to check/change for IVSHMEM */ - if (shm->model =3D=3D VIR_DOMAIN_SHMEM_MODEL_IVSHMEM) - return 0; - - if (!shm->server.enabled) { - if (shm->model =3D=3D VIR_DOMAIN_SHMEM_MODEL_IVSHMEM_DOORBELL) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("shmem model '%1$s' is supported only with se= rver option enabled"), - virDomainShmemModelTypeToString(shm->model)); - return -1; - } - - if (shm->msi.enabled) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("shmem model '%1$s' doesn't support msi"), - virDomainShmemModelTypeToString(shm->model)); - } - } else { - if (shm->model =3D=3D VIR_DOMAIN_SHMEM_MODEL_IVSHMEM_PLAIN) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("shmem model '%1$s' is supported only with se= rver option disabled"), - virDomainShmemModelTypeToString(shm->model)); - return -1; - } - - if (shm->size) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("shmem model '%1$s' does not support size set= ting"), - virDomainShmemModelTypeToString(shm->model)); - return -1; - } - shm->msi.enabled =3D true; - if (!shm->msi.ioeventfd) - shm->msi.ioeventfd =3D VIR_TRISTATE_SWITCH_ON; - } - - return 0; -} - - -#define QEMU_USB_XHCI_MAXPORTS 15 - - -static int -qemuDomainControllerDefPostParse(virDomainControllerDef *cont, - const virDomainDef *def, - virQEMUCaps *qemuCaps, - unsigned int parseFlags) -{ - switch (cont->type) { - case VIR_DOMAIN_CONTROLLER_TYPE_SCSI: - /* Set the default SCSI controller model if not already set */ - cont->model =3D qemuDomainGetSCSIControllerModel(def, cont, qemuCa= ps); - - if (cont->model < 0) - return -1; - break; - - case VIR_DOMAIN_CONTROLLER_TYPE_USB: - if (cont->model =3D=3D VIR_DOMAIN_CONTROLLER_MODEL_USB_DEFAULT && = qemuCaps) { - /* Pick a suitable default model for the USB controller if none - * has been selected by the user and we have the qemuCaps for - * figuring out which controllers are supported. - * - * We rely on device availability instead of setting the model - * unconditionally because, for some machine types, there's a - * chance we will get away with using the legacy USB controller - * when the relevant device is not available. - * - * See qemuBuildControllersCommandLine() */ - - /* Default USB controller is piix3-uhci if available. Fall bac= k to - * 'pci-ohci' otherwise which is the default for non-x86 machi= nes - * which honour -usb */ - if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_PIIX3_USB_UHCI)) - cont->model =3D VIR_DOMAIN_CONTROLLER_MODEL_USB_PIIX3_UHCI; - else if (!ARCH_IS_X86(def->os.arch) && - virQEMUCapsGet(qemuCaps, QEMU_CAPS_PCI_OHCI)) - cont->model =3D VIR_DOMAIN_CONTROLLER_MODEL_USB_PCI_OHCI; - - if (ARCH_IS_S390(def->os.arch)) { - if (cont->info.type =3D=3D VIR_DOMAIN_DEVICE_ADDRESS_TYPE_= NONE) { - /* set the default USB model to none for s390 unless an - * address is found */ - cont->model =3D VIR_DOMAIN_CONTROLLER_MODEL_USB_NONE; - } - } else if (ARCH_IS_PPC64(def->os.arch)) { - /* To not break migration we need to set default USB contr= oller - * for ppc64 to pci-ohci if we cannot change ABI of the VM. - * The nec-usb-xhci or qemu-xhci controller is used as def= ault - * only for newly defined domains or devices. */ - if ((parseFlags & VIR_DOMAIN_DEF_PARSE_ABI_UPDATE) && - virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_QEMU_XHCI)) { - cont->model =3D VIR_DOMAIN_CONTROLLER_MODEL_USB_QEMU_X= HCI; - } else if ((parseFlags & VIR_DOMAIN_DEF_PARSE_ABI_UPDATE) = && - virQEMUCapsGet(qemuCaps, QEMU_CAPS_NEC_USB_XHCI)) { - cont->model =3D VIR_DOMAIN_CONTROLLER_MODEL_USB_NEC_XH= CI; - } else if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_PCI_OHCI)) { - cont->model =3D VIR_DOMAIN_CONTROLLER_MODEL_USB_PCI_OH= CI; - } else { - /* Explicitly fallback to legacy USB controller for PP= C64. */ - cont->model =3D VIR_DOMAIN_CONTROLLER_MODEL_USB_DEFAUL= T; - } - } else if (def->os.arch =3D=3D VIR_ARCH_AARCH64) { - if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_QEMU_XHCI)) - cont->model =3D VIR_DOMAIN_CONTROLLER_MODEL_USB_QEMU_X= HCI; - else if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_NEC_USB_XHCI)) - cont->model =3D VIR_DOMAIN_CONTROLLER_MODEL_USB_NEC_XH= CI; - } else if (ARCH_IS_LOONGARCH(def->os.arch)) { - if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_QEMU_XHCI)) - cont->model =3D VIR_DOMAIN_CONTROLLER_MODEL_USB_QEMU_X= HCI; - } - } - /* forbid usb model 'qusb1' and 'qusb2' in this kind of hyperviosr= */ - if (cont->model =3D=3D VIR_DOMAIN_CONTROLLER_MODEL_USB_QUSB1 || - cont->model =3D=3D VIR_DOMAIN_CONTROLLER_MODEL_USB_QUSB2) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("USB controller model type 'qusb1' or 'qusb2'= is not supported in %1$s"), - virDomainVirtTypeToString(def->virtType)); - return -1; - } - if ((cont->model =3D=3D VIR_DOMAIN_CONTROLLER_MODEL_USB_NEC_XHCI || - cont->model =3D=3D VIR_DOMAIN_CONTROLLER_MODEL_USB_QEMU_XHCI)= && - cont->opts.usbopts.ports > QEMU_USB_XHCI_MAXPORTS) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, - _("'%1$s' controller only supports up to '%2$u'= ports"), - virDomainControllerModelUSBTypeToString(cont->m= odel), - QEMU_USB_XHCI_MAXPORTS); - return -1; - } - break; - - case VIR_DOMAIN_CONTROLLER_TYPE_PCI: - - /* pSeries guests can have multiple pci-root controllers, - * but other machine types only support a single one */ - if (!qemuDomainIsPSeries(def) && - (cont->model =3D=3D VIR_DOMAIN_CONTROLLER_MODEL_PCI_ROOT || - cont->model =3D=3D VIR_DOMAIN_CONTROLLER_MODEL_PCIE_ROOT) && - cont->idx !=3D 0) { - virReportError(VIR_ERR_XML_ERROR, "%s", - _("pci-root and pcie-root controllers should ha= ve index 0")); - return -1; - } - - if (cont->model =3D=3D VIR_DOMAIN_CONTROLLER_MODEL_PCI_EXPANDER_BU= S && - !qemuDomainIsI440FX(def)) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("pci-expander-bus controllers are only suppor= ted on 440fx-based machinetypes")); - return -1; - } - if (cont->model =3D=3D VIR_DOMAIN_CONTROLLER_MODEL_PCIE_EXPANDER_B= US && - !(qemuDomainIsQ35(def) || qemuDomainIsARMVirt(def))) { - virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", - _("pcie-expander-bus controllers are not suppor= ted with this machine type")); - return -1; - } - - /* if a PCI expander bus or pci-root on Pseries has a NUMA node - * set, make sure that NUMA node is configured in the guest - * array. NUMA cell id's in this array are numbered - * from 0 .. size-1. - */ - if (cont->opts.pciopts.numaNode >=3D 0 && - cont->opts.pciopts.numaNode >=3D - (int)virDomainNumaGetNodeCount(def->numa)) { - virReportError(VIR_ERR_XML_ERROR, - _("%1$s with index %2$d is configured for a NUM= A node (%3$d) not present in the domain's array (%4$zu)"), - virDomainControllerModelPCITypeToString(cont->m= odel), - cont->idx, cont->opts.pciopts.numaNode, - virDomainNumaGetNodeCount(def->numa)); - return -1; - } - break; - - case VIR_DOMAIN_CONTROLLER_TYPE_SATA: - case VIR_DOMAIN_CONTROLLER_TYPE_VIRTIO_SERIAL: - case VIR_DOMAIN_CONTROLLER_TYPE_CCID: - case VIR_DOMAIN_CONTROLLER_TYPE_IDE: - case VIR_DOMAIN_CONTROLLER_TYPE_FDC: - case VIR_DOMAIN_CONTROLLER_TYPE_XENBUS: - case VIR_DOMAIN_CONTROLLER_TYPE_ISA: - case VIR_DOMAIN_CONTROLLER_TYPE_LAST: - break; - } - - return 0; -} - -static int -qemuDomainChrDefPostParse(virDomainChrDef *chr, - const virDomainDef *def, - virQEMUDriver *driver, - unsigned int parseFlags) -{ - /* Historically, isa-serial and the default matched, so in order to - * maintain backwards compatibility we map them here. The actual defau= lt - * will be picked below based on the architecture and machine type. */ - if (chr->deviceType =3D=3D VIR_DOMAIN_CHR_DEVICE_TYPE_SERIAL && - chr->targetType =3D=3D VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_ISA) { - chr->targetType =3D VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_NONE; - } - - /* Set the default serial type */ - if (chr->deviceType =3D=3D VIR_DOMAIN_CHR_DEVICE_TYPE_SERIAL && - chr->targetType =3D=3D VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_NONE) { - if (ARCH_IS_X86(def->os.arch)) { - chr->targetType =3D VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_ISA; - } else if (qemuDomainIsPSeries(def)) { - chr->targetType =3D VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_SPAPR_VI= O; - } else if (qemuDomainIsARMVirt(def) || - qemuDomainIsLoongArchVirt(def) || - qemuDomainIsRISCVVirt(def)) { - chr->targetType =3D VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_SYSTEM; - } else if (ARCH_IS_S390(def->os.arch)) { - chr->targetType =3D VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_SCLP; - } - } - - /* Set the default target model */ - if (chr->deviceType =3D=3D VIR_DOMAIN_CHR_DEVICE_TYPE_SERIAL && - chr->targetModel =3D=3D VIR_DOMAIN_CHR_SERIAL_TARGET_MODEL_NONE) { - switch ((virDomainChrSerialTargetType)chr->targetType) { - case VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_ISA: - chr->targetModel =3D VIR_DOMAIN_CHR_SERIAL_TARGET_MODEL_ISA_SE= RIAL; - break; - case VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_USB: - chr->targetModel =3D VIR_DOMAIN_CHR_SERIAL_TARGET_MODEL_USB_SE= RIAL; - break; - case VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_PCI: - chr->targetModel =3D VIR_DOMAIN_CHR_SERIAL_TARGET_MODEL_PCI_SE= RIAL; - break; - case VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_SPAPR_VIO: - chr->targetModel =3D VIR_DOMAIN_CHR_SERIAL_TARGET_MODEL_SPAPR_= VTY; - break; - case VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_SYSTEM: - if (qemuDomainIsARMVirt(def)) { - chr->targetModel =3D VIR_DOMAIN_CHR_SERIAL_TARGET_MODEL_PL= 011; - } else if (qemuDomainIsLoongArchVirt(def) || - qemuDomainIsRISCVVirt(def)) { - chr->targetModel =3D VIR_DOMAIN_CHR_SERIAL_TARGET_MODEL_16= 550A; - } - break; - case VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_SCLP: - chr->targetModel =3D VIR_DOMAIN_CHR_SERIAL_TARGET_MODEL_SCLPCO= NSOLE; - break; - case VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_ISA_DEBUG: - chr->targetModel =3D VIR_DOMAIN_CHR_SERIAL_TARGET_MODEL_ISA_DE= BUGCON; - break; - case VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_NONE: - case VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_LAST: - /* Nothing to do */ - break; - } - } - - /* clear auto generated unix socket path for inactive definitions */ - if (parseFlags & VIR_DOMAIN_DEF_PARSE_INACTIVE) { - qemuDomainChrDefDropDefaultPath(chr, driver); - - /* For UNIX chardev if no path is provided we generate one. - * This also implies that the mode is 'bind'. */ - if (chr->source && - chr->source->type =3D=3D VIR_DOMAIN_CHR_TYPE_UNIX && - !chr->source->data.nix.path) { - chr->source->data.nix.listen =3D true; - } - } - - return 0; -} - - -/** - * qemuDomainDeviceDiskDefPostParseRestoreSecAlias: - * - * Re-generate aliases for objects related to the storage source if they - * were not stored in the status XML by an older libvirt. - * - * Note that qemuCaps should be always present for a status XML. - */ -static int -qemuDomainDeviceDiskDefPostParseRestoreSecAlias(virDomainDiskDef *disk, - unsigned int parseFlags) -{ - qemuDomainStorageSourcePrivate *priv =3D QEMU_DOMAIN_STORAGE_SOURCE_PR= IVATE(disk->src); - bool restoreAuthSecret =3D false; - bool restoreEncSecret =3D false; - g_autofree char *authalias =3D NULL; - g_autofree char *encalias =3D NULL; - - if (!(parseFlags & VIR_DOMAIN_DEF_PARSE_STATUS) || - virStorageSourceIsEmpty(disk->src)) - return 0; - - /* network storage authentication secret */ - if (disk->src->auth && - (!priv || !priv->secinfo)) { - - /* only RBD and iSCSI (with capability) were supporting authentica= tion - * using secret object at the time we did not format the alias int= o the - * status XML */ - if (virStorageSourceGetActualType(disk->src) =3D=3D VIR_STORAGE_TY= PE_NETWORK && - (disk->src->protocol =3D=3D VIR_STORAGE_NET_PROTOCOL_RBD || - disk->src->protocol =3D=3D VIR_STORAGE_NET_PROTOCOL_ISCSI)) - restoreAuthSecret =3D true; - } - - /* disk encryption secret */ - if (disk->src->encryption && - disk->src->encryption->format =3D=3D VIR_STORAGE_ENCRYPTION_FORMAT= _LUKS && - (!priv || !priv->encinfo)) - restoreEncSecret =3D true; - - if (!restoreAuthSecret && !restoreEncSecret) - return 0; - - if (!priv) { - if (!(disk->src->privateData =3D qemuDomainStorageSourcePrivateNew= ())) - return -1; - - priv =3D QEMU_DOMAIN_STORAGE_SOURCE_PRIVATE(disk->src); - } - - if (restoreAuthSecret) { - authalias =3D g_strdup_printf("%s-secret0", disk->info.alias); - - if (qemuStorageSourcePrivateDataAssignSecinfo(&priv->secinfo, &aut= halias) < 0) - return -1; - } - - if (restoreEncSecret) { - if (!priv->encinfo) { - priv->enccount =3D 1; - priv->encinfo =3D g_new0(qemuDomainSecretInfo *, 1); - } - - encalias =3D g_strdup_printf("%s-luks-secret0", disk->info.alias); - - if (qemuStorageSourcePrivateDataAssignSecinfo(&priv->encinfo[0], &= encalias) < 0) - return -1; - } - - return 0; -} - - int -qemuDomainDeviceDiskDefPostParse(virDomainDiskDef *disk, - unsigned int parseFlags) -{ - virStorageSource *n; - - /* set default disk types and drivers */ - if (!virDomainDiskGetDriver(disk)) - virDomainDiskSetDriver(disk, "qemu"); - - /* default disk format for drives */ - if (virDomainDiskGetFormat(disk) =3D=3D VIR_STORAGE_FILE_NONE && - virDomainDiskGetType(disk) !=3D VIR_STORAGE_TYPE_VOLUME) - virDomainDiskSetFormat(disk, VIR_STORAGE_FILE_RAW); - - /* default disk format for mirrored drive */ - if (disk->mirror && - disk->mirror->format =3D=3D VIR_STORAGE_FILE_NONE) - disk->mirror->format =3D VIR_STORAGE_FILE_RAW; - - /* default disk encryption engine */ - for (n =3D disk->src; virStorageSourceIsBacking(n); n =3D n->backingSt= ore) { - if (n->encryption && n->encryption->engine =3D=3D VIR_STORAGE_ENCR= YPTION_ENGINE_DEFAULT) - n->encryption->engine =3D VIR_STORAGE_ENCRYPTION_ENGINE_QEMU; - } - - if (qemuDomainDeviceDiskDefPostParseRestoreSecAlias(disk, parseFlags) = < 0) - return -1; - - /* regenerate TLS alias for old status XMLs */ - if (parseFlags & VIR_DOMAIN_DEF_PARSE_STATUS && - disk->src->haveTLS =3D=3D VIR_TRISTATE_BOOL_YES && - !disk->src->tlsAlias && - !(disk->src->tlsAlias =3D qemuAliasTLSObjFromSrcAlias(disk->info.a= lias))) - return -1; - - return 0; -} - - -static int -qemuDomainDeviceNetDefPostParse(virDomainNetDef *net, - const virDomainDef *def, - virQEMUCaps *qemuCaps) -{ - if (net->type =3D=3D VIR_DOMAIN_NET_TYPE_VDPA && - !virDomainNetGetModelString(net)) { - net->model =3D VIR_DOMAIN_NET_MODEL_VIRTIO; - } else if (net->type !=3D VIR_DOMAIN_NET_TYPE_HOSTDEV && - !virDomainNetGetModelString(net) && - virDomainNetResolveActualType(net) !=3D VIR_DOMAIN_NET_TYPE_HOSTDE= V) { - net->model =3D qemuDomainDefaultNetModel(def, qemuCaps); - } - - if (net->type =3D=3D VIR_DOMAIN_NET_TYPE_USER && - net->backend.type =3D=3D VIR_DOMAIN_NET_BACKEND_DEFAULT) { - virDomainCapsDeviceNet netCaps =3D { }; - - virQEMUCapsFillDomainDeviceNetCaps(qemuCaps, &netCaps); - - if (!VIR_DOMAIN_CAPS_ENUM_IS_SET(netCaps.backendType, VIR_DOMAIN_N= ET_BACKEND_DEFAULT) && - VIR_DOMAIN_CAPS_ENUM_IS_SET(netCaps.backendType, VIR_DOMAIN_NE= T_BACKEND_PASST)) { - net->backend.type =3D VIR_DOMAIN_NET_BACKEND_PASST; - } - } - - return 0; -} - - -static int -qemuDomainDefaultVideoDevice(const virDomainDef *def, - virQEMUCaps *qemuCaps) -{ - if (ARCH_IS_PPC64(def->os.arch)) - return VIR_DOMAIN_VIDEO_TYPE_VGA; - if (qemuDomainIsARMVirt(def) || - qemuDomainIsLoongArchVirt(def) || - qemuDomainIsRISCVVirt(def) || - ARCH_IS_S390(def->os.arch)) { - return VIR_DOMAIN_VIDEO_TYPE_VIRTIO; - } - if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_CIRRUS_VGA)) - return VIR_DOMAIN_VIDEO_TYPE_CIRRUS; - if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_VGA)) - return VIR_DOMAIN_VIDEO_TYPE_VGA; - return VIR_DOMAIN_VIDEO_TYPE_DEFAULT; -} - - -static int -qemuDomainDeviceVideoDefPostParse(virDomainVideoDef *video, - const virDomainDef *def, - virQEMUCaps *qemuCaps) -{ - if (video->type =3D=3D VIR_DOMAIN_VIDEO_TYPE_DEFAULT) - video->type =3D qemuDomainDefaultVideoDevice(def, qemuCaps); - - if (video->type =3D=3D VIR_DOMAIN_VIDEO_TYPE_QXL && - !video->vgamem) { - video->vgamem =3D QEMU_QXL_VGAMEM_DEFAULT; - } - - return 0; -} - - -static int -qemuDomainDevicePanicDefPostParse(virDomainPanicDef *panic, - const virDomainDef *def) -{ - if (panic->model =3D=3D VIR_DOMAIN_PANIC_MODEL_DEFAULT) - panic->model =3D qemuDomainDefaultPanicModel(def); - - return 0; -} - - -static int -qemuDomainVsockDefPostParse(virDomainVsockDef *vsock) -{ - if (vsock->model =3D=3D VIR_DOMAIN_VSOCK_MODEL_DEFAULT) - vsock->model =3D VIR_DOMAIN_VSOCK_MODEL_VIRTIO; - - return 0; -} - - -/** - * qemuDomainDeviceHostdevDefPostParseRestoreSecAlias: - * - * Re-generate aliases for objects related to the storage source if they - * were not stored in the status XML by an older libvirt. - * - * Note that qemuCaps should be always present for a status XML. - */ -static int -qemuDomainDeviceHostdevDefPostParseRestoreSecAlias(virDomainHostdevDef *ho= stdev, - unsigned int parseFlags) -{ - qemuDomainStorageSourcePrivate *priv; - virDomainHostdevSubsysSCSI *scsisrc =3D &hostdev->source.subsys.u.scsi; - virDomainHostdevSubsysSCSIiSCSI *iscsisrc =3D &scsisrc->u.iscsi; - g_autofree char *authalias =3D NULL; - - if (!(parseFlags & VIR_DOMAIN_DEF_PARSE_STATUS)) - return 0; - - if (hostdev->mode !=3D VIR_DOMAIN_HOSTDEV_MODE_SUBSYS || - hostdev->source.subsys.type !=3D VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SC= SI || - scsisrc->protocol !=3D VIR_DOMAIN_HOSTDEV_SCSI_PROTOCOL_TYPE_ISCSI= || - !iscsisrc->src->auth) - return 0; - - if (!(priv =3D qemuDomainStorageSourcePrivateFetch(iscsisrc->src))) - return -1; - - if (priv->secinfo) - return 0; - - authalias =3D g_strdup_printf("%s-secret0", hostdev->info->alias); - - if (qemuStorageSourcePrivateDataAssignSecinfo(&priv->secinfo, &authali= as) < 0) - return -1; - - return 0; -} - - -/** - * qemuDomainDeviceHostdevDefPostParseRestoreBackendAlias: - * - * Re-generate backend alias if it wasn't stored in the status XML by an o= lder - * libvirtd. - * - * Note that qemuCaps should be always present for a status XML. - */ -static int -qemuDomainDeviceHostdevDefPostParseRestoreBackendAlias(virDomainHostdevDef= *hostdev, - unsigned int parseF= lags) -{ - virDomainHostdevSubsysSCSI *scsisrc =3D &hostdev->source.subsys.u.scsi; - virStorageSource *src; - - if (!(parseFlags & VIR_DOMAIN_DEF_PARSE_STATUS)) - return 0; - - if (hostdev->mode !=3D VIR_DOMAIN_HOSTDEV_MODE_SUBSYS || - hostdev->source.subsys.type !=3D VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SC= SI) - return 0; - - switch (scsisrc->protocol) { - case VIR_DOMAIN_HOSTDEV_SCSI_PROTOCOL_TYPE_NONE: - if (!scsisrc->u.host.src) - scsisrc->u.host.src =3D virStorageSourceNew(); - - src =3D scsisrc->u.host.src; - break; - - case VIR_DOMAIN_HOSTDEV_SCSI_PROTOCOL_TYPE_ISCSI: - src =3D scsisrc->u.iscsi.src; - break; - - case VIR_DOMAIN_HOSTDEV_SCSI_PROTOCOL_TYPE_LAST: - default: - virReportEnumRangeError(virDomainHostdevSCSIProtocolType, scsisrc-= >protocol); - return -1; - } - - if (!qemuBlockStorageSourceGetStorageNodename(src)) - qemuBlockStorageSourceSetStorageNodename(src, g_strdup_printf("lib= virt-%s-backend", hostdev->info->alias)); - - return 0; -} - - -static int -qemuDomainHostdevDefMdevPostParse(virDomainHostdevSubsysMediatedDev *mdevs= rc) -{ - /* QEMU 2.12 added support for vfio-pci display type, we default to - * 'display=3Doff' to stay safe from future changes */ - if (mdevsrc->model =3D=3D VIR_MDEV_MODEL_TYPE_VFIO_PCI && - mdevsrc->display =3D=3D VIR_TRISTATE_SWITCH_ABSENT) - mdevsrc->display =3D VIR_TRISTATE_SWITCH_OFF; - - return 0; -} - - -static int -qemuDomainHostdevDefPostParse(virDomainHostdevDef *hostdev, - unsigned int parseFlags) -{ - virDomainHostdevSubsys *subsys =3D &hostdev->source.subsys; - - if (qemuDomainDeviceHostdevDefPostParseRestoreSecAlias(hostdev, parseF= lags) < 0) - return -1; - - if (qemuDomainDeviceHostdevDefPostParseRestoreBackendAlias(hostdev, pa= rseFlags) < 0) - return -1; - - if (hostdev->mode =3D=3D VIR_DOMAIN_HOSTDEV_MODE_SUBSYS && - hostdev->source.subsys.type =3D=3D VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_= MDEV && - qemuDomainHostdevDefMdevPostParse(&subsys->u.mdev) < 0) - return -1; - - return 0; -} - - -static int -qemuDomainTPMDefPostParse(virDomainTPMDef *tpm, - const virDomainDef *def) -{ - if (tpm->model =3D=3D VIR_DOMAIN_TPM_MODEL_DEFAULT) { - if (ARCH_IS_PPC64(def->os.arch)) - tpm->model =3D VIR_DOMAIN_TPM_MODEL_SPAPR; - else - tpm->model =3D VIR_DOMAIN_TPM_MODEL_TIS; - } - - /* TPM 1.2 and 2 are not compatible, so we choose a specific version h= ere */ - if (tpm->type =3D=3D VIR_DOMAIN_TPM_TYPE_EMULATOR && - tpm->data.emulator.version =3D=3D VIR_DOMAIN_TPM_VERSION_DEFAULT) { - /* tpm-tis on x86 defaults to TPM 1.2 to preserve the - * historical behavior, but in all other scenarios we want - * TPM 2.0 instead */ - if (tpm->model =3D=3D VIR_DOMAIN_TPM_MODEL_TIS && - ARCH_IS_X86(def->os.arch)) { - tpm->data.emulator.version =3D VIR_DOMAIN_TPM_VERSION_1_2; - } else { - tpm->data.emulator.version =3D VIR_DOMAIN_TPM_VERSION_2_0; - } - } - - return 0; -} - - -static int qemuDomainNVDimmAlignSizePseries(virDomainMemoryDef *mem) { /* For NVDIMMs in ppc64 in we want to align down the guest @@ -6418,194 +4743,6 @@ qemuDomainNVDimmAlignSizePseries(virDomainMemoryDef= *mem) } =20 =20 -static int -qemuDomainMemoryDefPostParse(virDomainMemoryDef *mem, virArch arch, - unsigned int parseFlags) -{ - /* Memory alignment can't be done for migration or snapshot - * scenarios. This logic was defined by commit c7d7ba85a624. - * - * There is no easy way to replicate at this point the same conditions - * used to call qemuDomainAlignMemorySizes(), which means checking if - * we're not migrating and not in a snapshot. - * - * We can use the PARSE_ABI_UPDATE flag, which is more strict - - * existing guests will not activate the flag to avoid breaking - * boot ABI. This means that any alignment done here will be replicated - * later on by qemuDomainAlignMemorySizes() to contemplate existing - * guests as well. */ - if (parseFlags & VIR_DOMAIN_DEF_PARSE_ABI_UPDATE) { - if (ARCH_IS_PPC64(arch)) { - unsigned long long ppc64MemModuleAlign =3D 256 * 1024; - - if (mem->model =3D=3D VIR_DOMAIN_MEMORY_MODEL_NVDIMM) { - if (qemuDomainNVDimmAlignSizePseries(mem) < 0) - return -1; - } else { - mem->size =3D VIR_ROUND_UP(mem->size, ppc64MemModuleAlign); - } - } - } - - return 0; -} - - -static int -qemuDomainPstoreDefPostParse(virDomainPstoreDef *pstore, - const virDomainDef *def, - virQEMUDriver *driver) -{ - g_autoptr(virQEMUDriverConfig) cfg =3D virQEMUDriverGetConfig(driver); - - switch (pstore->backend) { - case VIR_DOMAIN_PSTORE_BACKEND_ACPI_ERST: - if (!pstore->path) - pstore->path =3D g_strdup_printf("%s/%s_PSTORE.raw", - cfg->nvramDir, def->name); - break; - - case VIR_DOMAIN_PSTORE_BACKEND_LAST: - break; - } - - return 0; -} - - -static int -qemuDomainIOMMUDefPostParse(virDomainIOMMUDef *iommu, - const virDomainDef *def, - virQEMUCaps *qemuCaps, - unsigned int parseFlags) -{ - /* In case domain has huge number of vCPUS and Extended Interrupt Mode - * (EIM) is not explicitly turned off, let's enable it. If we didn't t= hen - * guest will have troubles with interrupts. */ - if (parseFlags & VIR_DOMAIN_DEF_PARSE_ABI_UPDATE && - ARCH_IS_X86(def->os.arch) && - virDomainDefGetVcpusMax(def) > QEMU_MAX_VCPUS_WITHOUT_EIM && - qemuDomainIsQ35(def) && - iommu && iommu->model =3D=3D VIR_DOMAIN_IOMMU_MODEL_INTEL) { - - /* eim requires intremap. */ - if (iommu->intremap =3D=3D VIR_TRISTATE_SWITCH_ABSENT && - virQEMUCapsGet(qemuCaps, QEMU_CAPS_INTEL_IOMMU_INTREMAP)) { - iommu->intremap =3D VIR_TRISTATE_SWITCH_ON; - } - - if (iommu->eim =3D=3D VIR_TRISTATE_SWITCH_ABSENT && - virQEMUCapsGet(qemuCaps, QEMU_CAPS_INTEL_IOMMU_EIM)) { - iommu->eim =3D VIR_TRISTATE_SWITCH_ON; - } - } - - return 0; -} - - -static int -qemuDomainDeviceDefPostParse(virDomainDeviceDef *dev, - const virDomainDef *def, - unsigned int parseFlags, - void *opaque, - void *parseOpaque) -{ - virQEMUDriver *driver =3D opaque; - /* Note that qemuCaps may be NULL when this function is called. This - * function shall not fail in that case. It will be re-run on VM start= up - * with the capabilities populated. */ - virQEMUCaps *qemuCaps =3D parseOpaque; - int ret =3D -1; - - switch (dev->type) { - case VIR_DOMAIN_DEVICE_NET: - ret =3D qemuDomainDeviceNetDefPostParse(dev->data.net, def, qemuCa= ps); - break; - - case VIR_DOMAIN_DEVICE_DISK: - ret =3D qemuDomainDeviceDiskDefPostParse(dev->data.disk, parseFlag= s); - break; - - case VIR_DOMAIN_DEVICE_VIDEO: - ret =3D qemuDomainDeviceVideoDefPostParse(dev->data.video, def, qe= muCaps); - break; - - case VIR_DOMAIN_DEVICE_PANIC: - ret =3D qemuDomainDevicePanicDefPostParse(dev->data.panic, def); - break; - - case VIR_DOMAIN_DEVICE_CONTROLLER: - ret =3D qemuDomainControllerDefPostParse(dev->data.controller, def, - qemuCaps, parseFlags); - break; - - case VIR_DOMAIN_DEVICE_SHMEM: - ret =3D qemuDomainShmemDefPostParse(dev->data.shmem); - break; - - case VIR_DOMAIN_DEVICE_CHR: - ret =3D qemuDomainChrDefPostParse(dev->data.chr, def, driver, pars= eFlags); - break; - - case VIR_DOMAIN_DEVICE_VSOCK: - ret =3D qemuDomainVsockDefPostParse(dev->data.vsock); - break; - - case VIR_DOMAIN_DEVICE_HOSTDEV: - ret =3D qemuDomainHostdevDefPostParse(dev->data.hostdev, parseFlag= s); - break; - - case VIR_DOMAIN_DEVICE_TPM: - ret =3D qemuDomainTPMDefPostParse(dev->data.tpm, def); - break; - - case VIR_DOMAIN_DEVICE_MEMORY: - ret =3D qemuDomainMemoryDefPostParse(dev->data.memory, def->os.arc= h, - parseFlags); - break; - - case VIR_DOMAIN_DEVICE_PSTORE: - ret =3D qemuDomainPstoreDefPostParse(dev->data.pstore, def, driver= ); - break; - - case VIR_DOMAIN_DEVICE_IOMMU: - ret =3D qemuDomainIOMMUDefPostParse(dev->data.iommu, def, - qemuCaps, parseFlags); - break; - - case VIR_DOMAIN_DEVICE_LEASE: - case VIR_DOMAIN_DEVICE_FS: - case VIR_DOMAIN_DEVICE_INPUT: - case VIR_DOMAIN_DEVICE_SOUND: - case VIR_DOMAIN_DEVICE_WATCHDOG: - case VIR_DOMAIN_DEVICE_GRAPHICS: - case VIR_DOMAIN_DEVICE_HUB: - case VIR_DOMAIN_DEVICE_REDIRDEV: - case VIR_DOMAIN_DEVICE_SMARTCARD: - case VIR_DOMAIN_DEVICE_MEMBALLOON: - case VIR_DOMAIN_DEVICE_NVRAM: - case VIR_DOMAIN_DEVICE_RNG: - case VIR_DOMAIN_DEVICE_AUDIO: - case VIR_DOMAIN_DEVICE_CRYPTO: - ret =3D 0; - break; - - case VIR_DOMAIN_DEVICE_NONE: - virReportError(VIR_ERR_INTERNAL_ERROR, "%s", - _("unexpected VIR_DOMAIN_DEVICE_NONE")); - break; - - case VIR_DOMAIN_DEVICE_LAST: - default: - virReportEnumRangeError(virDomainDeviceType, dev->type); - break; - } - - return ret; -} - - static int qemuDomainDefAssignAddresses(virDomainDef *def, unsigned int parseFlags G_GNUC_UNUSED, @@ -6630,31 +4767,6 @@ qemuDomainDefAssignAddresses(virDomainDef *def, } =20 =20 -static int -qemuDomainPostParseDataAlloc(const virDomainDef *def, - unsigned int parseFlags G_GNUC_UNUSED, - void *opaque, - void **parseOpaque) -{ - virQEMUDriver *driver =3D opaque; - - if (!(*parseOpaque =3D virQEMUCapsCacheLookup(driver->qemuCapsCache, - def->emulator))) - return 1; - - return 0; -} - - -static void -qemuDomainPostParseDataFree(void *parseOpaque) -{ - virQEMUCaps *qemuCaps =3D parseOpaque; - - virObjectUnref(qemuCaps); -} - - virDomainDefParserConfig virQEMUDriverDomainDefParserConfig =3D { .domainPostParseBasicCallback =3D qemuDomainDefPostParseBasic, .domainPostParseDataAlloc =3D qemuDomainPostParseDataAlloc, diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h index 047a11b7fe..1ae421e5f2 100644 --- a/src/qemu/qemu_domain.h +++ b/src/qemu/qemu_domain.h @@ -780,6 +780,9 @@ void qemuDomainCleanupRun(virQEMUDriver *driver, =20 void qemuDomainObjPrivateDataClear(qemuDomainObjPrivate *priv); =20 +int qemuStorageSourcePrivateDataAssignSecinfo(qemuDomainSecretInfo **secin= fo, + char **alias); + extern virDomainXMLPrivateDataCallbacks virQEMUDriverPrivateDataCallbacks; extern virXMLNamespace virQEMUDriverDomainXMLNamespace; extern virDomainDefParserConfig virQEMUDriverDomainDefParserConfig; @@ -849,6 +852,11 @@ int qemuDomainGetSCSIControllerModel(const virDomainDe= f *def, const virDomainControllerDef *cont, virQEMUCaps *qemuCaps); =20 +int qemuDomainDefAddDefaultAudioBackend(virQEMUDriver *driver, + virDomainDef *def); + +virDomainPanicModel qemuDomainDefaultPanicModel(const virDomainDef *def); + void qemuDomainUpdateCurrentMemorySize(virDomainObj *vm); =20 unsigned long long qemuDomainGetMemLockLimitBytes(virDomainDef *def); @@ -938,8 +946,12 @@ int qemuDomainSecretPrepare(virQEMUDriver *driver, virDomainObj *vm) ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2); =20 -int qemuDomainDeviceDiskDefPostParse(virDomainDiskDef *disk, - unsigned int parseFlags); +void +qemuDomainChrDefDropDefaultPath(virDomainChrDef *chr, + virQEMUDriver *driver); + +int +qemuDomainNVDimmAlignSizePseries(virDomainMemoryDef *mem); =20 int qemuDomainPrepareChannel(virDomainChrDef *chr, const char *domainChannelTargetDir) diff --git a/src/qemu/qemu_postparse.c b/src/qemu/qemu_postparse.c new file mode 100644 index 0000000000..11134fb030 --- /dev/null +++ b/src/qemu/qemu_postparse.c @@ -0,0 +1,1925 @@ +/* + * qemu_postparse.c: QEMU domain PostParse functions + * + * Copyright (C) 2006-2024 Red Hat, Inc. + * Copyright (C) 2006 Daniel P. Berrange + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * . + */ + +#include + +#include "qemu_postparse.h" +#include "qemu_alias.h" +#include "qemu_block.h" +#include "qemu_domain.h" +#include "qemu_firmware.h" +#include "qemu_security.h" +#include "qemu_validate.h" +#include "domain_conf.h" +#include "viralloc.h" +#include "virlog.h" + +#define QEMU_QXL_VGAMEM_DEFAULT 16 * 1024 + +#define VIR_FROM_THIS VIR_FROM_QEMU + +VIR_LOG_INIT("qemu.qemu_postparse"); + + +/** + * qemuDomainDefaultNetModel: + * @def: domain definition + * @qemuCaps: qemu capabilities + * + * Returns the default network model for a given domain. Note that if @qem= uCaps + * is NULL this function may return NULL if the default model depends on t= he + * capabilities. + */ +static int +qemuDomainDefaultNetModel(const virDomainDef *def, + virQEMUCaps *qemuCaps) +{ + /* When there are no backwards compatibility concerns getting in + * the way, virtio is a good default */ + if (ARCH_IS_S390(def->os.arch) || + qemuDomainIsLoongArchVirt(def) || + qemuDomainIsRISCVVirt(def)) { + return VIR_DOMAIN_NET_MODEL_VIRTIO; + } + + if (ARCH_IS_ARM(def->os.arch)) { + if (STREQ(def->os.machine, "versatilepb")) + return VIR_DOMAIN_NET_MODEL_SMC91C111; + + if (qemuDomainIsARMVirt(def)) + return VIR_DOMAIN_NET_MODEL_VIRTIO; + + /* Incomplete. vexpress (and a few others) use this, but not all + * arm boards */ + return VIR_DOMAIN_NET_MODEL_LAN9118; + } + + /* In all other cases the model depends on the capabilities. If they w= ere + * not provided don't report any default. */ + if (!qemuCaps) + return VIR_DOMAIN_NET_MODEL_UNKNOWN; + + /* Try several network devices in turn; each of these devices is + * less likely be supported out-of-the-box by the guest operating + * system than the previous one */ + if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_RTL8139)) + return VIR_DOMAIN_NET_MODEL_RTL8139; + + if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_E1000)) + return VIR_DOMAIN_NET_MODEL_E1000; + + if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_VIRTIO_NET)) + return VIR_DOMAIN_NET_MODEL_VIRTIO; + + /* We've had no luck detecting support for any network device, + * but we have to return something: might as well be rtl8139 */ + return VIR_DOMAIN_NET_MODEL_RTL8139; +} + + +static int +qemuDomainDeviceNetDefPostParse(virDomainNetDef *net, + const virDomainDef *def, + virQEMUCaps *qemuCaps) +{ + if (net->type =3D=3D VIR_DOMAIN_NET_TYPE_VDPA && + !virDomainNetGetModelString(net)) { + net->model =3D VIR_DOMAIN_NET_MODEL_VIRTIO; + } else if (net->type !=3D VIR_DOMAIN_NET_TYPE_HOSTDEV && + !virDomainNetGetModelString(net) && + virDomainNetResolveActualType(net) !=3D VIR_DOMAIN_NET_TYPE_HOSTDE= V) { + net->model =3D qemuDomainDefaultNetModel(def, qemuCaps); + } + + if (net->type =3D=3D VIR_DOMAIN_NET_TYPE_USER && + net->backend.type =3D=3D VIR_DOMAIN_NET_BACKEND_DEFAULT) { + virDomainCapsDeviceNet netCaps =3D { }; + + virQEMUCapsFillDomainDeviceNetCaps(qemuCaps, &netCaps); + + if (!VIR_DOMAIN_CAPS_ENUM_IS_SET(netCaps.backendType, VIR_DOMAIN_N= ET_BACKEND_DEFAULT) && + VIR_DOMAIN_CAPS_ENUM_IS_SET(netCaps.backendType, VIR_DOMAIN_NE= T_BACKEND_PASST)) { + net->backend.type =3D VIR_DOMAIN_NET_BACKEND_PASST; + } + } + + return 0; +} + + +/** + * qemuDomainDeviceDiskDefPostParseRestoreSecAlias: + * + * Re-generate aliases for objects related to the storage source if they + * were not stored in the status XML by an older libvirt. + * + * Note that qemuCaps should be always present for a status XML. + */ +static int +qemuDomainDeviceDiskDefPostParseRestoreSecAlias(virDomainDiskDef *disk, + unsigned int parseFlags) +{ + qemuDomainStorageSourcePrivate *priv =3D QEMU_DOMAIN_STORAGE_SOURCE_PR= IVATE(disk->src); + bool restoreAuthSecret =3D false; + bool restoreEncSecret =3D false; + g_autofree char *authalias =3D NULL; + g_autofree char *encalias =3D NULL; + + if (!(parseFlags & VIR_DOMAIN_DEF_PARSE_STATUS) || + virStorageSourceIsEmpty(disk->src)) + return 0; + + /* network storage authentication secret */ + if (disk->src->auth && + (!priv || !priv->secinfo)) { + + /* only RBD and iSCSI (with capability) were supporting authentica= tion + * using secret object at the time we did not format the alias int= o the + * status XML */ + if (virStorageSourceGetActualType(disk->src) =3D=3D VIR_STORAGE_TY= PE_NETWORK && + (disk->src->protocol =3D=3D VIR_STORAGE_NET_PROTOCOL_RBD || + disk->src->protocol =3D=3D VIR_STORAGE_NET_PROTOCOL_ISCSI)) + restoreAuthSecret =3D true; + } + + /* disk encryption secret */ + if (disk->src->encryption && + disk->src->encryption->format =3D=3D VIR_STORAGE_ENCRYPTION_FORMAT= _LUKS && + (!priv || !priv->encinfo)) + restoreEncSecret =3D true; + + if (!restoreAuthSecret && !restoreEncSecret) + return 0; + + if (!priv) { + if (!(disk->src->privateData =3D qemuDomainStorageSourcePrivateNew= ())) + return -1; + + priv =3D QEMU_DOMAIN_STORAGE_SOURCE_PRIVATE(disk->src); + } + + if (restoreAuthSecret) { + authalias =3D g_strdup_printf("%s-secret0", disk->info.alias); + + if (qemuStorageSourcePrivateDataAssignSecinfo(&priv->secinfo, &aut= halias) < 0) + return -1; + } + + if (restoreEncSecret) { + if (!priv->encinfo) { + priv->enccount =3D 1; + priv->encinfo =3D g_new0(qemuDomainSecretInfo *, 1); + } + + encalias =3D g_strdup_printf("%s-luks-secret0", disk->info.alias); + + if (qemuStorageSourcePrivateDataAssignSecinfo(&priv->encinfo[0], &= encalias) < 0) + return -1; + } + + return 0; +} + + +int +qemuDomainDeviceDiskDefPostParse(virDomainDiskDef *disk, + unsigned int parseFlags) +{ + virStorageSource *n; + + /* set default disk types and drivers */ + if (!virDomainDiskGetDriver(disk)) + virDomainDiskSetDriver(disk, "qemu"); + + /* default disk format for drives */ + if (virDomainDiskGetFormat(disk) =3D=3D VIR_STORAGE_FILE_NONE && + virDomainDiskGetType(disk) !=3D VIR_STORAGE_TYPE_VOLUME) + virDomainDiskSetFormat(disk, VIR_STORAGE_FILE_RAW); + + /* default disk format for mirrored drive */ + if (disk->mirror && + disk->mirror->format =3D=3D VIR_STORAGE_FILE_NONE) + disk->mirror->format =3D VIR_STORAGE_FILE_RAW; + + /* default disk encryption engine */ + for (n =3D disk->src; virStorageSourceIsBacking(n); n =3D n->backingSt= ore) { + if (n->encryption && n->encryption->engine =3D=3D VIR_STORAGE_ENCR= YPTION_ENGINE_DEFAULT) + n->encryption->engine =3D VIR_STORAGE_ENCRYPTION_ENGINE_QEMU; + } + + if (qemuDomainDeviceDiskDefPostParseRestoreSecAlias(disk, parseFlags) = < 0) + return -1; + + /* regenerate TLS alias for old status XMLs */ + if (parseFlags & VIR_DOMAIN_DEF_PARSE_STATUS && + disk->src->haveTLS =3D=3D VIR_TRISTATE_BOOL_YES && + !disk->src->tlsAlias && + !(disk->src->tlsAlias =3D qemuAliasTLSObjFromSrcAlias(disk->info.a= lias))) + return -1; + + return 0; +} + + +static int +qemuDomainDefaultVideoDevice(const virDomainDef *def, + virQEMUCaps *qemuCaps) +{ + if (ARCH_IS_PPC64(def->os.arch)) + return VIR_DOMAIN_VIDEO_TYPE_VGA; + if (qemuDomainIsARMVirt(def) || + qemuDomainIsLoongArchVirt(def) || + qemuDomainIsRISCVVirt(def) || + ARCH_IS_S390(def->os.arch)) { + return VIR_DOMAIN_VIDEO_TYPE_VIRTIO; + } + if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_CIRRUS_VGA)) + return VIR_DOMAIN_VIDEO_TYPE_CIRRUS; + if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_VGA)) + return VIR_DOMAIN_VIDEO_TYPE_VGA; + return VIR_DOMAIN_VIDEO_TYPE_DEFAULT; +} + + +static int +qemuDomainDeviceVideoDefPostParse(virDomainVideoDef *video, + const virDomainDef *def, + virQEMUCaps *qemuCaps) +{ + if (video->type =3D=3D VIR_DOMAIN_VIDEO_TYPE_DEFAULT) + video->type =3D qemuDomainDefaultVideoDevice(def, qemuCaps); + + if (video->type =3D=3D VIR_DOMAIN_VIDEO_TYPE_QXL && + !video->vgamem) { + video->vgamem =3D QEMU_QXL_VGAMEM_DEFAULT; + } + + return 0; +} + + +static int +qemuDomainDevicePanicDefPostParse(virDomainPanicDef *panic, + const virDomainDef *def) +{ + if (panic->model =3D=3D VIR_DOMAIN_PANIC_MODEL_DEFAULT) + panic->model =3D qemuDomainDefaultPanicModel(def); + + return 0; +} + + +#define QEMU_USB_XHCI_MAXPORTS 15 + +static int +qemuDomainControllerDefPostParse(virDomainControllerDef *cont, + const virDomainDef *def, + virQEMUCaps *qemuCaps, + unsigned int parseFlags) +{ + switch (cont->type) { + case VIR_DOMAIN_CONTROLLER_TYPE_SCSI: + /* Set the default SCSI controller model if not already set */ + cont->model =3D qemuDomainGetSCSIControllerModel(def, cont, qemuCa= ps); + + if (cont->model < 0) + return -1; + break; + + case VIR_DOMAIN_CONTROLLER_TYPE_USB: + if (cont->model =3D=3D VIR_DOMAIN_CONTROLLER_MODEL_USB_DEFAULT && = qemuCaps) { + /* Pick a suitable default model for the USB controller if none + * has been selected by the user and we have the qemuCaps for + * figuring out which controllers are supported. + * + * We rely on device availability instead of setting the model + * unconditionally because, for some machine types, there's a + * chance we will get away with using the legacy USB controller + * when the relevant device is not available. + * + * See qemuBuildControllersCommandLine() */ + + /* Default USB controller is piix3-uhci if available. Fall bac= k to + * 'pci-ohci' otherwise which is the default for non-x86 machi= nes + * which honour -usb */ + if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_PIIX3_USB_UHCI)) + cont->model =3D VIR_DOMAIN_CONTROLLER_MODEL_USB_PIIX3_UHCI; + else if (!ARCH_IS_X86(def->os.arch) && + virQEMUCapsGet(qemuCaps, QEMU_CAPS_PCI_OHCI)) + cont->model =3D VIR_DOMAIN_CONTROLLER_MODEL_USB_PCI_OHCI; + + if (ARCH_IS_S390(def->os.arch)) { + if (cont->info.type =3D=3D VIR_DOMAIN_DEVICE_ADDRESS_TYPE_= NONE) { + /* set the default USB model to none for s390 unless an + * address is found */ + cont->model =3D VIR_DOMAIN_CONTROLLER_MODEL_USB_NONE; + } + } else if (ARCH_IS_PPC64(def->os.arch)) { + /* To not break migration we need to set default USB contr= oller + * for ppc64 to pci-ohci if we cannot change ABI of the VM. + * The nec-usb-xhci or qemu-xhci controller is used as def= ault + * only for newly defined domains or devices. */ + if ((parseFlags & VIR_DOMAIN_DEF_PARSE_ABI_UPDATE) && + virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_QEMU_XHCI)) { + cont->model =3D VIR_DOMAIN_CONTROLLER_MODEL_USB_QEMU_X= HCI; + } else if ((parseFlags & VIR_DOMAIN_DEF_PARSE_ABI_UPDATE) = && + virQEMUCapsGet(qemuCaps, QEMU_CAPS_NEC_USB_XHCI)) { + cont->model =3D VIR_DOMAIN_CONTROLLER_MODEL_USB_NEC_XH= CI; + } else if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_PCI_OHCI)) { + cont->model =3D VIR_DOMAIN_CONTROLLER_MODEL_USB_PCI_OH= CI; + } else { + /* Explicitly fallback to legacy USB controller for PP= C64. */ + cont->model =3D VIR_DOMAIN_CONTROLLER_MODEL_USB_DEFAUL= T; + } + } else if (def->os.arch =3D=3D VIR_ARCH_AARCH64) { + if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_QEMU_XHCI)) + cont->model =3D VIR_DOMAIN_CONTROLLER_MODEL_USB_QEMU_X= HCI; + else if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_NEC_USB_XHCI)) + cont->model =3D VIR_DOMAIN_CONTROLLER_MODEL_USB_NEC_XH= CI; + } else if (ARCH_IS_LOONGARCH(def->os.arch)) { + if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_QEMU_XHCI)) + cont->model =3D VIR_DOMAIN_CONTROLLER_MODEL_USB_QEMU_X= HCI; + } + } + /* forbid usb model 'qusb1' and 'qusb2' in this kind of hyperviosr= */ + if (cont->model =3D=3D VIR_DOMAIN_CONTROLLER_MODEL_USB_QUSB1 || + cont->model =3D=3D VIR_DOMAIN_CONTROLLER_MODEL_USB_QUSB2) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("USB controller model type 'qusb1' or 'qusb2'= is not supported in %1$s"), + virDomainVirtTypeToString(def->virtType)); + return -1; + } + if ((cont->model =3D=3D VIR_DOMAIN_CONTROLLER_MODEL_USB_NEC_XHCI || + cont->model =3D=3D VIR_DOMAIN_CONTROLLER_MODEL_USB_QEMU_XHCI)= && + cont->opts.usbopts.ports > QEMU_USB_XHCI_MAXPORTS) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("'%1$s' controller only supports up to '%2$u'= ports"), + virDomainControllerModelUSBTypeToString(cont->m= odel), + QEMU_USB_XHCI_MAXPORTS); + return -1; + } + break; + + case VIR_DOMAIN_CONTROLLER_TYPE_PCI: + + /* pSeries guests can have multiple pci-root controllers, + * but other machine types only support a single one */ + if (!qemuDomainIsPSeries(def) && + (cont->model =3D=3D VIR_DOMAIN_CONTROLLER_MODEL_PCI_ROOT || + cont->model =3D=3D VIR_DOMAIN_CONTROLLER_MODEL_PCIE_ROOT) && + cont->idx !=3D 0) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("pci-root and pcie-root controllers should ha= ve index 0")); + return -1; + } + + if (cont->model =3D=3D VIR_DOMAIN_CONTROLLER_MODEL_PCI_EXPANDER_BU= S && + !qemuDomainIsI440FX(def)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("pci-expander-bus controllers are only suppor= ted on 440fx-based machinetypes")); + return -1; + } + if (cont->model =3D=3D VIR_DOMAIN_CONTROLLER_MODEL_PCIE_EXPANDER_B= US && + !(qemuDomainIsQ35(def) || qemuDomainIsARMVirt(def))) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("pcie-expander-bus controllers are not suppor= ted with this machine type")); + return -1; + } + + /* if a PCI expander bus or pci-root on Pseries has a NUMA node + * set, make sure that NUMA node is configured in the guest + * array. NUMA cell id's in this array are numbered + * from 0 .. size-1. + */ + if (cont->opts.pciopts.numaNode >=3D 0 && + cont->opts.pciopts.numaNode >=3D + (int)virDomainNumaGetNodeCount(def->numa)) { + virReportError(VIR_ERR_XML_ERROR, + _("%1$s with index %2$d is configured for a NUM= A node (%3$d) not present in the domain's array (%4$zu)"), + virDomainControllerModelPCITypeToString(cont->m= odel), + cont->idx, cont->opts.pciopts.numaNode, + virDomainNumaGetNodeCount(def->numa)); + return -1; + } + break; + + case VIR_DOMAIN_CONTROLLER_TYPE_SATA: + case VIR_DOMAIN_CONTROLLER_TYPE_VIRTIO_SERIAL: + case VIR_DOMAIN_CONTROLLER_TYPE_CCID: + case VIR_DOMAIN_CONTROLLER_TYPE_IDE: + case VIR_DOMAIN_CONTROLLER_TYPE_FDC: + case VIR_DOMAIN_CONTROLLER_TYPE_XENBUS: + case VIR_DOMAIN_CONTROLLER_TYPE_ISA: + case VIR_DOMAIN_CONTROLLER_TYPE_LAST: + break; + } + + return 0; +} + + +static int +qemuDomainShmemDefPostParse(virDomainShmemDef *shm) +{ + /* This was the default since the introduction of this device. */ + if (shm->model !=3D VIR_DOMAIN_SHMEM_MODEL_IVSHMEM_DOORBELL && !shm->s= ize) + shm->size =3D 4 << 20; + + /* Nothing more to check/change for IVSHMEM */ + if (shm->model =3D=3D VIR_DOMAIN_SHMEM_MODEL_IVSHMEM) + return 0; + + if (!shm->server.enabled) { + if (shm->model =3D=3D VIR_DOMAIN_SHMEM_MODEL_IVSHMEM_DOORBELL) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("shmem model '%1$s' is supported only with se= rver option enabled"), + virDomainShmemModelTypeToString(shm->model)); + return -1; + } + + if (shm->msi.enabled) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("shmem model '%1$s' doesn't support msi"), + virDomainShmemModelTypeToString(shm->model)); + } + } else { + if (shm->model =3D=3D VIR_DOMAIN_SHMEM_MODEL_IVSHMEM_PLAIN) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("shmem model '%1$s' is supported only with se= rver option disabled"), + virDomainShmemModelTypeToString(shm->model)); + return -1; + } + + if (shm->size) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("shmem model '%1$s' does not support size set= ting"), + virDomainShmemModelTypeToString(shm->model)); + return -1; + } + shm->msi.enabled =3D true; + if (!shm->msi.ioeventfd) + shm->msi.ioeventfd =3D VIR_TRISTATE_SWITCH_ON; + } + + return 0; +} + + +static int +qemuDomainChrDefPostParse(virDomainChrDef *chr, + const virDomainDef *def, + virQEMUDriver *driver, + unsigned int parseFlags) +{ + /* Historically, isa-serial and the default matched, so in order to + * maintain backwards compatibility we map them here. The actual defau= lt + * will be picked below based on the architecture and machine type. */ + if (chr->deviceType =3D=3D VIR_DOMAIN_CHR_DEVICE_TYPE_SERIAL && + chr->targetType =3D=3D VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_ISA) { + chr->targetType =3D VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_NONE; + } + + /* Set the default serial type */ + if (chr->deviceType =3D=3D VIR_DOMAIN_CHR_DEVICE_TYPE_SERIAL && + chr->targetType =3D=3D VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_NONE) { + if (ARCH_IS_X86(def->os.arch)) { + chr->targetType =3D VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_ISA; + } else if (qemuDomainIsPSeries(def)) { + chr->targetType =3D VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_SPAPR_VI= O; + } else if (qemuDomainIsARMVirt(def) || + qemuDomainIsLoongArchVirt(def) || + qemuDomainIsRISCVVirt(def)) { + chr->targetType =3D VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_SYSTEM; + } else if (ARCH_IS_S390(def->os.arch)) { + chr->targetType =3D VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_SCLP; + } + } + + /* Set the default target model */ + if (chr->deviceType =3D=3D VIR_DOMAIN_CHR_DEVICE_TYPE_SERIAL && + chr->targetModel =3D=3D VIR_DOMAIN_CHR_SERIAL_TARGET_MODEL_NONE) { + switch ((virDomainChrSerialTargetType)chr->targetType) { + case VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_ISA: + chr->targetModel =3D VIR_DOMAIN_CHR_SERIAL_TARGET_MODEL_ISA_SE= RIAL; + break; + case VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_USB: + chr->targetModel =3D VIR_DOMAIN_CHR_SERIAL_TARGET_MODEL_USB_SE= RIAL; + break; + case VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_PCI: + chr->targetModel =3D VIR_DOMAIN_CHR_SERIAL_TARGET_MODEL_PCI_SE= RIAL; + break; + case VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_SPAPR_VIO: + chr->targetModel =3D VIR_DOMAIN_CHR_SERIAL_TARGET_MODEL_SPAPR_= VTY; + break; + case VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_SYSTEM: + if (qemuDomainIsARMVirt(def)) { + chr->targetModel =3D VIR_DOMAIN_CHR_SERIAL_TARGET_MODEL_PL= 011; + } else if (qemuDomainIsLoongArchVirt(def) || + qemuDomainIsRISCVVirt(def)) { + chr->targetModel =3D VIR_DOMAIN_CHR_SERIAL_TARGET_MODEL_16= 550A; + } + break; + case VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_SCLP: + chr->targetModel =3D VIR_DOMAIN_CHR_SERIAL_TARGET_MODEL_SCLPCO= NSOLE; + break; + case VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_ISA_DEBUG: + chr->targetModel =3D VIR_DOMAIN_CHR_SERIAL_TARGET_MODEL_ISA_DE= BUGCON; + break; + case VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_NONE: + case VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_LAST: + /* Nothing to do */ + break; + } + } + + /* clear auto generated unix socket path for inactive definitions */ + if (parseFlags & VIR_DOMAIN_DEF_PARSE_INACTIVE) { + qemuDomainChrDefDropDefaultPath(chr, driver); + + /* For UNIX chardev if no path is provided we generate one. + * This also implies that the mode is 'bind'. */ + if (chr->source && + chr->source->type =3D=3D VIR_DOMAIN_CHR_TYPE_UNIX && + !chr->source->data.nix.path) { + chr->source->data.nix.listen =3D true; + } + } + + return 0; +} + + +static int +qemuDomainVsockDefPostParse(virDomainVsockDef *vsock) +{ + if (vsock->model =3D=3D VIR_DOMAIN_VSOCK_MODEL_DEFAULT) + vsock->model =3D VIR_DOMAIN_VSOCK_MODEL_VIRTIO; + + return 0; +} + + +/** + * qemuDomainDeviceHostdevDefPostParseRestoreSecAlias: + * + * Re-generate aliases for objects related to the storage source if they + * were not stored in the status XML by an older libvirt. + * + * Note that qemuCaps should be always present for a status XML. + */ +static int +qemuDomainDeviceHostdevDefPostParseRestoreSecAlias(virDomainHostdevDef *ho= stdev, + unsigned int parseFlags) +{ + qemuDomainStorageSourcePrivate *priv; + virDomainHostdevSubsysSCSI *scsisrc =3D &hostdev->source.subsys.u.scsi; + virDomainHostdevSubsysSCSIiSCSI *iscsisrc =3D &scsisrc->u.iscsi; + g_autofree char *authalias =3D NULL; + + if (!(parseFlags & VIR_DOMAIN_DEF_PARSE_STATUS)) + return 0; + + if (hostdev->mode !=3D VIR_DOMAIN_HOSTDEV_MODE_SUBSYS || + hostdev->source.subsys.type !=3D VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SC= SI || + scsisrc->protocol !=3D VIR_DOMAIN_HOSTDEV_SCSI_PROTOCOL_TYPE_ISCSI= || + !iscsisrc->src->auth) + return 0; + + if (!(priv =3D qemuDomainStorageSourcePrivateFetch(iscsisrc->src))) + return -1; + + if (priv->secinfo) + return 0; + + authalias =3D g_strdup_printf("%s-secret0", hostdev->info->alias); + + if (qemuStorageSourcePrivateDataAssignSecinfo(&priv->secinfo, &authali= as) < 0) + return -1; + + return 0; +} + + +/** + * qemuDomainDeviceHostdevDefPostParseRestoreBackendAlias: + * + * Re-generate backend alias if it wasn't stored in the status XML by an o= lder + * libvirtd. + * + * Note that qemuCaps should be always present for a status XML. + */ +static int +qemuDomainDeviceHostdevDefPostParseRestoreBackendAlias(virDomainHostdevDef= *hostdev, + unsigned int parseF= lags) +{ + virDomainHostdevSubsysSCSI *scsisrc =3D &hostdev->source.subsys.u.scsi; + virStorageSource *src; + + if (!(parseFlags & VIR_DOMAIN_DEF_PARSE_STATUS)) + return 0; + + if (hostdev->mode !=3D VIR_DOMAIN_HOSTDEV_MODE_SUBSYS || + hostdev->source.subsys.type !=3D VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SC= SI) + return 0; + + switch (scsisrc->protocol) { + case VIR_DOMAIN_HOSTDEV_SCSI_PROTOCOL_TYPE_NONE: + if (!scsisrc->u.host.src) + scsisrc->u.host.src =3D virStorageSourceNew(); + + src =3D scsisrc->u.host.src; + break; + + case VIR_DOMAIN_HOSTDEV_SCSI_PROTOCOL_TYPE_ISCSI: + src =3D scsisrc->u.iscsi.src; + break; + + case VIR_DOMAIN_HOSTDEV_SCSI_PROTOCOL_TYPE_LAST: + default: + virReportEnumRangeError(virDomainHostdevSCSIProtocolType, scsisrc-= >protocol); + return -1; + } + + if (!qemuBlockStorageSourceGetStorageNodename(src)) + qemuBlockStorageSourceSetStorageNodename(src, g_strdup_printf("lib= virt-%s-backend", hostdev->info->alias)); + + return 0; +} + + +static int +qemuDomainHostdevDefMdevPostParse(virDomainHostdevSubsysMediatedDev *mdevs= rc) +{ + /* QEMU 2.12 added support for vfio-pci display type, we default to + * 'display=3Doff' to stay safe from future changes */ + if (mdevsrc->model =3D=3D VIR_MDEV_MODEL_TYPE_VFIO_PCI && + mdevsrc->display =3D=3D VIR_TRISTATE_SWITCH_ABSENT) + mdevsrc->display =3D VIR_TRISTATE_SWITCH_OFF; + + return 0; +} + + +static int +qemuDomainHostdevDefPostParse(virDomainHostdevDef *hostdev, + unsigned int parseFlags) +{ + virDomainHostdevSubsys *subsys =3D &hostdev->source.subsys; + + if (qemuDomainDeviceHostdevDefPostParseRestoreSecAlias(hostdev, parseF= lags) < 0) + return -1; + + if (qemuDomainDeviceHostdevDefPostParseRestoreBackendAlias(hostdev, pa= rseFlags) < 0) + return -1; + + if (hostdev->mode =3D=3D VIR_DOMAIN_HOSTDEV_MODE_SUBSYS && + hostdev->source.subsys.type =3D=3D VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_= MDEV && + qemuDomainHostdevDefMdevPostParse(&subsys->u.mdev) < 0) + return -1; + + return 0; +} + + +static int +qemuDomainTPMDefPostParse(virDomainTPMDef *tpm, + const virDomainDef *def) +{ + if (tpm->model =3D=3D VIR_DOMAIN_TPM_MODEL_DEFAULT) { + if (ARCH_IS_PPC64(def->os.arch)) + tpm->model =3D VIR_DOMAIN_TPM_MODEL_SPAPR; + else + tpm->model =3D VIR_DOMAIN_TPM_MODEL_TIS; + } + + /* TPM 1.2 and 2 are not compatible, so we choose a specific version h= ere */ + if (tpm->type =3D=3D VIR_DOMAIN_TPM_TYPE_EMULATOR && + tpm->data.emulator.version =3D=3D VIR_DOMAIN_TPM_VERSION_DEFAULT) { + /* tpm-tis on x86 defaults to TPM 1.2 to preserve the + * historical behavior, but in all other scenarios we want + * TPM 2.0 instead */ + if (tpm->model =3D=3D VIR_DOMAIN_TPM_MODEL_TIS && + ARCH_IS_X86(def->os.arch)) { + tpm->data.emulator.version =3D VIR_DOMAIN_TPM_VERSION_1_2; + } else { + tpm->data.emulator.version =3D VIR_DOMAIN_TPM_VERSION_2_0; + } + } + + return 0; +} + + +static int +qemuDomainMemoryDefPostParse(virDomainMemoryDef *mem, virArch arch, + unsigned int parseFlags) +{ + /* Memory alignment can't be done for migration or snapshot + * scenarios. This logic was defined by commit c7d7ba85a624. + * + * There is no easy way to replicate at this point the same conditions + * used to call qemuDomainAlignMemorySizes(), which means checking if + * we're not migrating and not in a snapshot. + * + * We can use the PARSE_ABI_UPDATE flag, which is more strict - + * existing guests will not activate the flag to avoid breaking + * boot ABI. This means that any alignment done here will be replicated + * later on by qemuDomainAlignMemorySizes() to contemplate existing + * guests as well. */ + if (parseFlags & VIR_DOMAIN_DEF_PARSE_ABI_UPDATE) { + if (ARCH_IS_PPC64(arch)) { + unsigned long long ppc64MemModuleAlign =3D 256 * 1024; + + if (mem->model =3D=3D VIR_DOMAIN_MEMORY_MODEL_NVDIMM) { + if (qemuDomainNVDimmAlignSizePseries(mem) < 0) + return -1; + } else { + mem->size =3D VIR_ROUND_UP(mem->size, ppc64MemModuleAlign); + } + } + } + + return 0; +} + + +static int +qemuDomainPstoreDefPostParse(virDomainPstoreDef *pstore, + const virDomainDef *def, + virQEMUDriver *driver) +{ + g_autoptr(virQEMUDriverConfig) cfg =3D virQEMUDriverGetConfig(driver); + + switch (pstore->backend) { + case VIR_DOMAIN_PSTORE_BACKEND_ACPI_ERST: + if (!pstore->path) + pstore->path =3D g_strdup_printf("%s/%s_PSTORE.raw", + cfg->nvramDir, def->name); + break; + + case VIR_DOMAIN_PSTORE_BACKEND_LAST: + break; + } + + return 0; +} + + +static int +qemuDomainIOMMUDefPostParse(virDomainIOMMUDef *iommu, + const virDomainDef *def, + virQEMUCaps *qemuCaps, + unsigned int parseFlags) +{ + /* In case domain has huge number of vCPUS and Extended Interrupt Mode + * (EIM) is not explicitly turned off, let's enable it. If we didn't t= hen + * guest will have troubles with interrupts. */ + if (parseFlags & VIR_DOMAIN_DEF_PARSE_ABI_UPDATE && + ARCH_IS_X86(def->os.arch) && + virDomainDefGetVcpusMax(def) > QEMU_MAX_VCPUS_WITHOUT_EIM && + qemuDomainIsQ35(def) && + iommu && iommu->model =3D=3D VIR_DOMAIN_IOMMU_MODEL_INTEL) { + + /* eim requires intremap. */ + if (iommu->intremap =3D=3D VIR_TRISTATE_SWITCH_ABSENT && + virQEMUCapsGet(qemuCaps, QEMU_CAPS_INTEL_IOMMU_INTREMAP)) { + iommu->intremap =3D VIR_TRISTATE_SWITCH_ON; + } + + if (iommu->eim =3D=3D VIR_TRISTATE_SWITCH_ABSENT && + virQEMUCapsGet(qemuCaps, QEMU_CAPS_INTEL_IOMMU_EIM)) { + iommu->eim =3D VIR_TRISTATE_SWITCH_ON; + } + } + + return 0; +} + + +int +qemuDomainDeviceDefPostParse(virDomainDeviceDef *dev, + const virDomainDef *def, + unsigned int parseFlags, + void *opaque, + void *parseOpaque) +{ + virQEMUDriver *driver =3D opaque; + /* Note that qemuCaps may be NULL when this function is called. This + * function shall not fail in that case. It will be re-run on VM start= up + * with the capabilities populated. */ + virQEMUCaps *qemuCaps =3D parseOpaque; + int ret =3D -1; + + switch (dev->type) { + case VIR_DOMAIN_DEVICE_NET: + ret =3D qemuDomainDeviceNetDefPostParse(dev->data.net, def, qemuCa= ps); + break; + + case VIR_DOMAIN_DEVICE_DISK: + ret =3D qemuDomainDeviceDiskDefPostParse(dev->data.disk, parseFlag= s); + break; + + case VIR_DOMAIN_DEVICE_VIDEO: + ret =3D qemuDomainDeviceVideoDefPostParse(dev->data.video, def, qe= muCaps); + break; + + case VIR_DOMAIN_DEVICE_PANIC: + ret =3D qemuDomainDevicePanicDefPostParse(dev->data.panic, def); + break; + + case VIR_DOMAIN_DEVICE_CONTROLLER: + ret =3D qemuDomainControllerDefPostParse(dev->data.controller, def, + qemuCaps, parseFlags); + break; + + case VIR_DOMAIN_DEVICE_SHMEM: + ret =3D qemuDomainShmemDefPostParse(dev->data.shmem); + break; + + case VIR_DOMAIN_DEVICE_CHR: + ret =3D qemuDomainChrDefPostParse(dev->data.chr, def, driver, pars= eFlags); + break; + + case VIR_DOMAIN_DEVICE_VSOCK: + ret =3D qemuDomainVsockDefPostParse(dev->data.vsock); + break; + + case VIR_DOMAIN_DEVICE_HOSTDEV: + ret =3D qemuDomainHostdevDefPostParse(dev->data.hostdev, parseFlag= s); + break; + + case VIR_DOMAIN_DEVICE_TPM: + ret =3D qemuDomainTPMDefPostParse(dev->data.tpm, def); + break; + + case VIR_DOMAIN_DEVICE_MEMORY: + ret =3D qemuDomainMemoryDefPostParse(dev->data.memory, def->os.arc= h, + parseFlags); + break; + + case VIR_DOMAIN_DEVICE_PSTORE: + ret =3D qemuDomainPstoreDefPostParse(dev->data.pstore, def, driver= ); + break; + + case VIR_DOMAIN_DEVICE_IOMMU: + ret =3D qemuDomainIOMMUDefPostParse(dev->data.iommu, def, + qemuCaps, parseFlags); + break; + + case VIR_DOMAIN_DEVICE_LEASE: + case VIR_DOMAIN_DEVICE_FS: + case VIR_DOMAIN_DEVICE_INPUT: + case VIR_DOMAIN_DEVICE_SOUND: + case VIR_DOMAIN_DEVICE_WATCHDOG: + case VIR_DOMAIN_DEVICE_GRAPHICS: + case VIR_DOMAIN_DEVICE_HUB: + case VIR_DOMAIN_DEVICE_REDIRDEV: + case VIR_DOMAIN_DEVICE_SMARTCARD: + case VIR_DOMAIN_DEVICE_MEMBALLOON: + case VIR_DOMAIN_DEVICE_NVRAM: + case VIR_DOMAIN_DEVICE_RNG: + case VIR_DOMAIN_DEVICE_AUDIO: + case VIR_DOMAIN_DEVICE_CRYPTO: + ret =3D 0; + break; + + case VIR_DOMAIN_DEVICE_NONE: + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("unexpected VIR_DOMAIN_DEVICE_NONE")); + break; + + case VIR_DOMAIN_DEVICE_LAST: + default: + virReportEnumRangeError(virDomainDeviceType, dev->type); + break; + } + + return ret; +} + + +int +qemuDomainDefPostParseBasic(virDomainDef *def, + void *opaque G_GNUC_UNUSED) +{ + virQEMUDriver *driver =3D opaque; + + /* check for emulator and create a default one if needed */ + if (!def->emulator) { + if (!(def->emulator =3D virQEMUCapsGetDefaultEmulator( + driver->hostarch, def->os.arch))) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("No emulator found for arch '%1$s'"), + virArchToString(def->os.arch)); + return 1; + } + } + + return 0; +} + + +static int +qemuCanonicalizeMachine(virDomainDef *def, virQEMUCaps *qemuCaps) +{ + const char *canon; + + if (!(canon =3D virQEMUCapsGetCanonicalMachine(qemuCaps, def->virtType, + def->os.machine))) + return 0; + + if (STRNEQ(canon, def->os.machine)) { + char *tmp; + tmp =3D g_strdup(canon); + VIR_FREE(def->os.machine); + def->os.machine =3D tmp; + } + + return 0; +} + + +static int +qemuDomainDefMachinePostParse(virDomainDef *def, + virQEMUCaps *qemuCaps) +{ + if (!def->os.machine) { + const char *machine =3D virQEMUCapsGetPreferredMachine(qemuCaps, + def->virtType= ); + if (!machine) { + virReportError(VIR_ERR_INVALID_ARG, + _("could not get preferred machine for %1$s typ= e=3D%2$s"), + def->emulator, + virDomainVirtTypeToString(def->virtType)); + return -1; + } + + def->os.machine =3D g_strdup(machine); + } + + if (qemuCanonicalizeMachine(def, qemuCaps) < 0) + return -1; + + return 0; +} + + +/** + * qemuDomainDefACPIPostParse: + * @def: domain definition + * @qemuCaps: qemu capabilities object + * + * Fixup the use of ACPI flag on certain architectures that never supporte= d it + * and users for some reason used it, which would break migration to newer + * libvirt versions which check whether given machine type supports ACPI. + * + * The fixup is done in post-parse as it's hard to update the ABI stability + * check on source of the migration. + */ +static void +qemuDomainDefACPIPostParse(virDomainDef *def, + virQEMUCaps *qemuCaps, + unsigned int parseFlags) +{ + /* Only cases when ACPI is enabled need to be fixed up */ + if (def->features[VIR_DOMAIN_FEATURE_ACPI] !=3D VIR_TRISTATE_SWITCH_ON) + return; + + /* Strip the feature only for non-fresh configs, in order to s= till + * produce an error if the feature is present in a newly defined one. + * + * The use of the VIR_DOMAIN_DEF_PARSE_ABI_UPDATE looks counter-intuit= ive, + * but it's used only in qemuDomainCreateXML/qemuDomainDefineXMLFlags = APIs + * */ + if (parseFlags & VIR_DOMAIN_DEF_PARSE_ABI_UPDATE) + return; + + /* This fixup is applicable _only_ on architectures which were present= as of + * libvirt-9.2 and *never* supported ACPI. The fixup is currently done= only + * for existing users of s390(x) to fix migration for configs which had + * despite being ignored. + */ + if (def->os.arch !=3D VIR_ARCH_S390 && + def->os.arch !=3D VIR_ARCH_S390X) + return; + + /* To be sure, we only strip ACPI if given machine type doesn't suppor= t it */ + if (virQEMUCapsMachineSupportsACPI(qemuCaps, def->virtType, def->os.ma= chine) !=3D VIR_TRISTATE_BOOL_NO) + return; + + def->features[VIR_DOMAIN_FEATURE_ACPI] =3D VIR_TRISTATE_SWITCH_ABSENT; +} + + +static int +qemuDomainDefBootPostParse(virDomainDef *def, + virQEMUDriver *driver, + unsigned int parseFlags) +{ + bool abiUpdate =3D !!(parseFlags & VIR_DOMAIN_DEF_PARSE_ABI_UPDATE); + + /* If we're loading an existing configuration from disk, we + * should try as hard as possible to preserve historical + * behavior. In particular, firmware autoselection being enabled + * could never have resulted, before libvirt 9.2.0, in anything + * but a raw firmware image being selected. + * + * In order to ensure that existing domains keep working even if + * a firmware descriptor for a build with a different format is + * given higher priority, explicitly add this requirement to the + * definition before performing firmware selection */ + if (!abiUpdate && def->os.firmware) { + if (!def->os.loader) + def->os.loader =3D virDomainLoaderDefNew(); + if (!def->os.loader->format) + def->os.loader->format =3D VIR_STORAGE_FILE_RAW; + } + + /* Firmware selection can fail for a number of reasons, but the + * most likely one is that the requested configuration contains + * mistakes or includes constraints that are impossible to + * satisfy on the current system. + * + * If that happens, we have to react differently based on the + * situation: if we're defining a new domain or updating its ABI, + * we should let the user know immediately so that they can + * change the requested configuration, hopefully into one that we + * can work with; if we're loading the configuration of an + * existing domain from disk, however, we absolutely cannot error + * out here, or the domain will disappear. + * + * To handle the second case gracefully, we clear any reported + * errors and continue as if nothing had happened. When it's time + * to start the domain, qemuFirmwareFillDomain() will be run + * again, fail in the same way, and at that point we'll have a + * chance to inform the user of any issues */ + if (qemuFirmwareFillDomain(driver, def, abiUpdate) < 0) { + if (abiUpdate) { + return -1; + } else { + virResetLastError(); + return 0; + } + } + + return 0; +} + + +static int +qemuDomainDefAddImplicitInputDevice(virDomainDef *def, + virQEMUCaps *qemuCaps) +{ + if (virQEMUCapsSupportsI8042(qemuCaps, def) && + def->features[VIR_DOMAIN_FEATURE_PS2] !=3D VIR_TRISTATE_SWITCH_OFF= ) { + if (virDomainDefMaybeAddInput(def, + VIR_DOMAIN_INPUT_TYPE_MOUSE, + VIR_DOMAIN_INPUT_BUS_PS2) < 0) + return -1; + + if (virDomainDefMaybeAddInput(def, + VIR_DOMAIN_INPUT_TYPE_KBD, + VIR_DOMAIN_INPUT_BUS_PS2) < 0) + return -1; + } + + return 0; +} + + +static int +qemuDomainDefSetDefaultCPU(virDomainDef *def, + virArch hostarch, + virQEMUCaps *qemuCaps) +{ + const char *model; + + if (def->cpu && + (def->cpu->mode !=3D VIR_CPU_MODE_CUSTOM || + def->cpu->model)) + return 0; + + if (!virCPUArchIsSupported(def->os.arch)) + return 0; + + /* Default CPU model info from QEMU is usable for TCG only except for + * x86, s390, and ppc64. */ + if (!ARCH_IS_X86(def->os.arch) && + !ARCH_IS_S390(def->os.arch) && + !ARCH_IS_PPC64(def->os.arch) && + def->virtType !=3D VIR_DOMAIN_VIRT_QEMU) + return 0; + + model =3D virQEMUCapsGetMachineDefaultCPU(qemuCaps, def->os.machine, d= ef->virtType); + if (!model) { + VIR_DEBUG("Unknown default CPU model for domain '%s'", def->name); + return 0; + } + + if (STREQ(model, "host") && def->virtType !=3D VIR_DOMAIN_VIRT_KVM) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("QEMU reports invalid default CPU model \"host\" = for non-kvm domain virt type")); + return -1; + } + + if (!def->cpu) + def->cpu =3D virCPUDefNew(); + + def->cpu->type =3D VIR_CPU_TYPE_GUEST; + + if (STREQ(model, "host")) { + if (ARCH_IS_S390(def->os.arch) && + virQEMUCapsIsCPUModeSupported(qemuCaps, hostarch, def->virtTyp= e, + VIR_CPU_MODE_HOST_MODEL, + def->os.machine)) { + def->cpu->mode =3D VIR_CPU_MODE_HOST_MODEL; + } else { + def->cpu->mode =3D VIR_CPU_MODE_HOST_PASSTHROUGH; + } + + VIR_DEBUG("Setting default CPU mode for domain '%s' to %s", + def->name, virCPUModeTypeToString(def->cpu->mode)); + } else { + /* We need to turn off all CPU checks when the domain is started + * because the default CPU (e.g., qemu64) may not be runnable on a= ny + * host. QEMU will just disable the unavailable features and we wi= ll + * update the CPU definition accordingly and set check to FULL when + * starting the domain. */ + def->cpu->check =3D VIR_CPU_CHECK_NONE; + def->cpu->mode =3D VIR_CPU_MODE_CUSTOM; + def->cpu->match =3D VIR_CPU_MATCH_EXACT; + def->cpu->fallback =3D VIR_CPU_FALLBACK_FORBID; + def->cpu->model =3D g_strdup(model); + + VIR_DEBUG("Setting default CPU model for domain '%s' to %s", + def->name, model); + } + + return 0; +} + + +static int +qemuDomainDefAddDefaultDevices(virQEMUDriver *driver, + virDomainDef *def, + virQEMUCaps *qemuCaps) +{ + bool addDefaultUSB =3D false; + int usbModel =3D -1; /* "default for machinetype" */ + int pciRoot; /* index within def->controllers */ + bool addImplicitSATA =3D false; + bool addPCIRoot =3D false; + bool addPCIeRoot =3D false; + bool addDefaultMemballoon =3D false; + bool addDefaultUSBKBD =3D false; + bool addDefaultUSBMouse =3D false; + bool addPanicDevice =3D false; + bool addITCOWatchdog =3D false; + bool addIOMMU =3D false; + + /* add implicit input devices */ + if (qemuDomainDefAddImplicitInputDevice(def, qemuCaps) < 0) + return -1; + + /* Add implicit PCI root controller if the machine has one */ + switch (def->os.arch) { + case VIR_ARCH_I686: + case VIR_ARCH_X86_64: + addDefaultMemballoon =3D true; + + if (STREQ(def->os.machine, "isapc")) { + break; + } + + addDefaultUSB =3D true; + + if (qemuDomainIsQ35(def)) { + addPCIeRoot =3D true; + addImplicitSATA =3D true; + addITCOWatchdog =3D true; + + if (virDomainDefGetVcpusMax(def) > QEMU_MAX_VCPUS_WITHOUT_EIM)= { + addIOMMU =3D true; + } + + /* Prefer adding a USB3 controller if supported, fall back + * to USB2 if there is no USB3 available, and if that's + * unavailable don't add anything. + */ + if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_QEMU_XHCI)) + usbModel =3D VIR_DOMAIN_CONTROLLER_MODEL_USB_QEMU_XHCI; + else if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_NEC_USB_XHCI)) + usbModel =3D VIR_DOMAIN_CONTROLLER_MODEL_USB_NEC_XHCI; + else if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_ICH9_USB_EHCI1)) + usbModel =3D VIR_DOMAIN_CONTROLLER_MODEL_USB_ICH9_EHCI1; + else + addDefaultUSB =3D false; + break; + } + if (qemuDomainIsI440FX(def)) + addPCIRoot =3D true; + break; + + case VIR_ARCH_ARMV6L: + case VIR_ARCH_ARMV7L: + case VIR_ARCH_ARMV7B: + case VIR_ARCH_AARCH64: + if (STREQ(def->os.machine, "versatilepb")) + addPCIRoot =3D true; + + /* Add default USB for the two machine types which historically + * supported -usb */ + if (STREQ(def->os.machine, "versatilepb") || + STRPREFIX(def->os.machine, "realview")) { + addDefaultUSB =3D true; + usbModel =3D VIR_DOMAIN_CONTROLLER_MODEL_USB_PCI_OHCI; + } + + if (qemuDomainIsARMVirt(def)) + addPCIeRoot =3D true; + + break; + + case VIR_ARCH_PPC64: + case VIR_ARCH_PPC64LE: + addPCIRoot =3D true; + addDefaultUSB =3D true; + addDefaultUSBKBD =3D true; + addDefaultUSBMouse =3D true; + addDefaultMemballoon =3D true; + /* For pSeries guests, the firmware provides the same + * functionality as the pvpanic device, so automatically + * add the definition if not already present */ + if (qemuDomainIsPSeries(def)) + addPanicDevice =3D true; + break; + + case VIR_ARCH_ALPHA: + case VIR_ARCH_PPC: + case VIR_ARCH_PPCEMB: + case VIR_ARCH_SH4: + case VIR_ARCH_SH4EB: + addDefaultUSB =3D true; + addDefaultMemballoon =3D true; + addPCIRoot =3D true; + break; + + case VIR_ARCH_RISCV32: + case VIR_ARCH_RISCV64: + addDefaultMemballoon =3D true; + if (qemuDomainIsRISCVVirt(def)) + addPCIeRoot =3D true; + break; + + case VIR_ARCH_S390: + case VIR_ARCH_S390X: + addDefaultMemballoon =3D true; + addPanicDevice =3D true; + addPCIRoot =3D virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_ZPCI); + break; + + case VIR_ARCH_SPARC64: + addDefaultUSB =3D true; + addDefaultMemballoon =3D true; + addPCIRoot =3D true; + break; + + case VIR_ARCH_MIPS: + case VIR_ARCH_MIPSEL: + case VIR_ARCH_MIPS64: + case VIR_ARCH_MIPS64EL: + addDefaultUSB =3D true; + addDefaultMemballoon =3D true; + if (qemuDomainIsMipsMalta(def)) + addPCIRoot =3D true; + break; + + case VIR_ARCH_LOONGARCH64: + addPCIeRoot =3D true; + break; + + case VIR_ARCH_CRIS: + case VIR_ARCH_ITANIUM: + case VIR_ARCH_LM32: + case VIR_ARCH_M68K: + case VIR_ARCH_MICROBLAZE: + case VIR_ARCH_MICROBLAZEEL: + case VIR_ARCH_OR32: + case VIR_ARCH_PARISC: + case VIR_ARCH_PARISC64: + case VIR_ARCH_PPCLE: + case VIR_ARCH_SPARC: + case VIR_ARCH_UNICORE32: + case VIR_ARCH_XTENSA: + case VIR_ARCH_XTENSAEB: + case VIR_ARCH_NONE: + case VIR_ARCH_LAST: + default: + break; + } + + if (addDefaultUSB && + virDomainControllerFind(def, VIR_DOMAIN_CONTROLLER_TYPE_USB, 0) < = 0 && + virDomainDefAddUSBController(def, 0, usbModel) < 0) + return -1; + + if (addImplicitSATA && + virDomainDefMaybeAddController( + def, VIR_DOMAIN_CONTROLLER_TYPE_SATA, 0, -1) < 0) + return -1; + + pciRoot =3D virDomainControllerFind(def, VIR_DOMAIN_CONTROLLER_TYPE_PC= I, 0); + + /* NB: any machine that sets addPCIRoot to true must also return + * true from the function qemuDomainSupportsPCI(). + */ + if (addPCIRoot) { + if (pciRoot >=3D 0) { + if (def->controllers[pciRoot]->model !=3D VIR_DOMAIN_CONTROLLE= R_MODEL_PCI_ROOT) { + virReportError(VIR_ERR_XML_ERROR, + _("The PCI controller with index=3D'0' must= be model=3D'pci-root' for this machine type, but model=3D'%1$s' was found = instead"), + virDomainControllerModelPCITypeToString(def= ->controllers[pciRoot]->model)); + return -1; + } + } else if (!virDomainDefAddController(def, VIR_DOMAIN_CONTROLLER_T= YPE_PCI, 0, + VIR_DOMAIN_CONTROLLER_MODEL_= PCI_ROOT)) { + return -1; + } + } + + /* When a machine has a pcie-root, make sure that there is always + * a dmi-to-pci-bridge controller added as bus 1, and a pci-bridge + * as bus 2, so that standard PCI devices can be connected + * + * NB: any machine that sets addPCIeRoot to true must also return + * true from the function qemuDomainSupportsPCI(). + */ + if (addPCIeRoot) { + if (pciRoot >=3D 0) { + if (def->controllers[pciRoot]->model !=3D VIR_DOMAIN_CONTROLLE= R_MODEL_PCIE_ROOT) { + virReportError(VIR_ERR_XML_ERROR, + _("The PCI controller with index=3D'0' must= be model=3D'pcie-root' for this machine type, but model=3D'%1$s' was found= instead"), + virDomainControllerModelPCITypeToString(def= ->controllers[pciRoot]->model)); + return -1; + } + } else if (!virDomainDefAddController(def, VIR_DOMAIN_CONTROLLER_T= YPE_PCI, 0, + VIR_DOMAIN_CONTROLLER_MODEL_P= CIE_ROOT)) { + return -1; + } + } + + if (addDefaultMemballoon && !def->memballoon) { + virDomainMemballoonDef *memballoon; + memballoon =3D g_new0(virDomainMemballoonDef, 1); + + memballoon->model =3D VIR_DOMAIN_MEMBALLOON_MODEL_VIRTIO; + def->memballoon =3D memballoon; + } + + if (addDefaultUSBMouse) { + bool hasUSBTablet =3D false; + size_t j; + + for (j =3D 0; j < def->ninputs; j++) { + if (def->inputs[j]->type =3D=3D VIR_DOMAIN_INPUT_TYPE_TABLET && + def->inputs[j]->bus =3D=3D VIR_DOMAIN_INPUT_BUS_USB) { + hasUSBTablet =3D true; + break; + } + } + + /* Historically, we have automatically added USB keyboard and + * mouse to some guests. While the former device is generally + * safe to have, adding the latter is undesiderable if a USB + * tablet is already present in the guest */ + if (hasUSBTablet) + addDefaultUSBMouse =3D false; + } + + if (addDefaultUSBKBD && + def->ngraphics > 0 && + virDomainDefMaybeAddInput(def, + VIR_DOMAIN_INPUT_TYPE_KBD, + VIR_DOMAIN_INPUT_BUS_USB) < 0) + return -1; + + if (addDefaultUSBMouse && + def->ngraphics > 0 && + virDomainDefMaybeAddInput(def, + VIR_DOMAIN_INPUT_TYPE_MOUSE, + VIR_DOMAIN_INPUT_BUS_USB) < 0) + return -1; + + if (addPanicDevice) { + virDomainPanicModel defaultModel =3D qemuDomainDefaultPanicModel(d= ef); + size_t j; + + for (j =3D 0; j < def->npanics; j++) { + if (def->panics[j]->model =3D=3D VIR_DOMAIN_PANIC_MODEL_DEFAUL= T || + def->panics[j]->model =3D=3D defaultModel) + break; + } + + if (j =3D=3D def->npanics) { + virDomainPanicDef *panic =3D g_new0(virDomainPanicDef, 1); + + VIR_APPEND_ELEMENT_COPY(def->panics, def->npanics, panic); + } + } + + if (addITCOWatchdog) { + size_t i =3D 0; + + for (i =3D 0; i < def->nwatchdogs; i++) { + if (def->watchdogs[i]->model =3D=3D VIR_DOMAIN_WATCHDOG_MODEL_= ITCO) + break; + } + + if (i =3D=3D def->nwatchdogs) { + virDomainWatchdogDef *watchdog =3D g_new0(virDomainWatchdogDef= , 1); + + watchdog->model =3D VIR_DOMAIN_WATCHDOG_MODEL_ITCO; + if (def->nwatchdogs) + watchdog->action =3D def->watchdogs[0]->action; + else + watchdog->action =3D VIR_DOMAIN_WATCHDOG_ACTION_RESET; + + VIR_APPEND_ELEMENT(def->watchdogs, def->nwatchdogs, watchdog); + } + } + + if (addIOMMU && !def->iommu && + virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_INTEL_IOMMU) && + virQEMUCapsGet(qemuCaps, QEMU_CAPS_INTEL_IOMMU_INTREMAP) && + virQEMUCapsGet(qemuCaps, QEMU_CAPS_INTEL_IOMMU_EIM)) { + g_autoptr(virDomainIOMMUDef) iommu =3D NULL; + + iommu =3D virDomainIOMMUDefNew(); + iommu->model =3D VIR_DOMAIN_IOMMU_MODEL_INTEL; + /* eim requires intremap. */ + iommu->intremap =3D VIR_TRISTATE_SWITCH_ON; + iommu->eim =3D VIR_TRISTATE_SWITCH_ON; + + def->iommu =3D g_steal_pointer(&iommu); + } + + if (qemuDomainDefAddDefaultAudioBackend(driver, def) < 0) + return -1; + + return 0; +} + + +/** + * qemuDomainDefEnableDefaultFeatures: + * @def: domain definition + * @qemuCaps: QEMU capabilities + * + * Make sure that features that should be enabled by default are actually + * enabled and configure default values related to those features. + */ +static void +qemuDomainDefEnableDefaultFeatures(virDomainDef *def, + virQEMUCaps *qemuCaps) +{ + /* The virt machine type always uses GIC: if the relevant information + * was not included in the domain XML, we need to choose a suitable + * GIC version ourselves */ + if ((def->features[VIR_DOMAIN_FEATURE_GIC] =3D=3D VIR_TRISTATE_SWITCH_= ABSENT && + qemuDomainIsARMVirt(def)) || + (def->features[VIR_DOMAIN_FEATURE_GIC] =3D=3D VIR_TRISTATE_SWITCH_= ON && + def->gic_version =3D=3D VIR_GIC_VERSION_NONE)) { + virGICVersion version; + + VIR_DEBUG("Looking for usable GIC version in domain capabilities"); + for (version =3D VIR_GIC_VERSION_LAST - 1; + version > VIR_GIC_VERSION_NONE; + version--) { + + /* We want to use the highest available GIC version for guests; + * however, the emulated GICv3 is currently lacking a MSI cont= roller, + * making it unsuitable for the pure PCIe topology we aim for. + * + * For that reason, we skip this step entirely for TCG guests, + * and rely on the code below to pick the default version, GIC= v2, + * which supports all the features we need. + * + * See https://bugzilla.redhat.com/show_bug.cgi?id=3D1414081 */ + if (version =3D=3D VIR_GIC_VERSION_3 && + def->virtType =3D=3D VIR_DOMAIN_VIRT_QEMU) { + continue; + } + + if (virQEMUCapsSupportsGICVersion(qemuCaps, + def->virtType, + version)) { + VIR_DEBUG("Using GIC version %s", + virGICVersionTypeToString(version)); + def->gic_version =3D version; + break; + } + } + + /* Use the default GIC version (GICv2) as a last-ditch attempt + * if no match could be found above */ + if (def->gic_version =3D=3D VIR_GIC_VERSION_NONE) { + VIR_DEBUG("Using GIC version 2 (default)"); + def->gic_version =3D VIR_GIC_VERSION_2; + } + + /* Even if we haven't found a usable GIC version in the domain + * capabilities, we still want to enable this */ + def->features[VIR_DOMAIN_FEATURE_GIC] =3D VIR_TRISTATE_SWITCH_ON; + } +} + + +static int +qemuDomainRecheckInternalPaths(virDomainDef *def, + virQEMUDriverConfig *cfg, + unsigned int flags) +{ + size_t i =3D 0; + size_t j =3D 0; + + for (i =3D 0; i < def->ngraphics; ++i) { + virDomainGraphicsDef *graphics =3D def->graphics[i]; + + for (j =3D 0; j < graphics->nListens; ++j) { + virDomainGraphicsListenDef *glisten =3D &graphics->listens[j]; + + /* This will happen only if we parse XML from old libvirts whe= re + * unix socket was available only for VNC graphics. In this + * particular case we should follow the behavior and if we rem= ove + * the auto-generated socket based on config option from qemu.= conf + * we need to change the listen type to address. */ + if (graphics->type =3D=3D VIR_DOMAIN_GRAPHICS_TYPE_VNC && + glisten->type =3D=3D VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_SOCKE= T && + glisten->socket && + !glisten->autoGenerated && + STRPREFIX(glisten->socket, cfg->libDir)) { + if (flags & VIR_DOMAIN_DEF_PARSE_INACTIVE) { + VIR_FREE(glisten->socket); + glisten->type =3D VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_ADDR= ESS; + } else { + glisten->fromConfig =3D true; + } + } + } + } + + return 0; +} + + +static int +qemuDomainDefVcpusPostParse(virDomainDef *def) +{ + unsigned int maxvcpus =3D virDomainDefGetVcpusMax(def); + virDomainVcpuDef *vcpu; + virDomainVcpuDef *prevvcpu; + size_t i; + bool has_order =3D false; + + /* vcpu 0 needs to be present, first, and non-hotpluggable */ + vcpu =3D virDomainDefGetVcpu(def, 0); + if (!vcpu->online) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("vcpu 0 can't be offline")); + return -1; + } + if (vcpu->hotpluggable =3D=3D VIR_TRISTATE_BOOL_YES) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("vcpu0 can't be hotpluggable")); + return -1; + } + if (vcpu->order !=3D 0 && vcpu->order !=3D 1) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("vcpu0 must be enabled first")); + return -1; + } + + if (vcpu->order !=3D 0) + has_order =3D true; + + prevvcpu =3D vcpu; + + /* all online vcpus or non online vcpu need to have order set */ + for (i =3D 1; i < maxvcpus; i++) { + vcpu =3D virDomainDefGetVcpu(def, i); + + if (vcpu->online && + (vcpu->order !=3D 0) !=3D has_order) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("all vcpus must have either set or unset orde= r")); + return -1; + } + + /* few conditions for non-hotpluggable (thus online) vcpus */ + if (vcpu->hotpluggable =3D=3D VIR_TRISTATE_BOOL_NO) { + /* they can be ordered only at the beginning */ + if (prevvcpu->hotpluggable =3D=3D VIR_TRISTATE_BOOL_YES) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("online non-hotpluggable vcpus need to be= ordered prior to hotplugable vcpus")); + return -1; + } + + /* they need to be in order (qemu doesn't support any order ye= t). + * Also note that multiple vcpus may share order on some platf= orms */ + if (prevvcpu->order > vcpu->order) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("online non-hotpluggable vcpus must be or= dered in ascending order")); + return -1; + } + } + + prevvcpu =3D vcpu; + } + + return 0; +} + + +static int +qemuDomainDefCPUPostParse(virDomainDef *def, + virQEMUCaps *qemuCaps) +{ + virCPUFeatureDef *sveFeature =3D NULL; + bool sveVectorLengthsProvided =3D false; + size_t i; + + if (!def->cpu) + return 0; + + for (i =3D 0; i < def->cpu->nfeatures; i++) { + virCPUFeatureDef *feature =3D &def->cpu->features[i]; + + if (STREQ(feature->name, "sve")) { + sveFeature =3D feature; + } else if (STRPREFIX(feature->name, "sve")) { + sveVectorLengthsProvided =3D true; + } + } + + if (sveVectorLengthsProvided) { + if (sveFeature) { + if (sveFeature->policy =3D=3D VIR_CPU_FEATURE_DISABLE || + sveFeature->policy =3D=3D VIR_CPU_FEATURE_FORBID) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("SVE disabled, but SVE vector lengths pro= vided")); + return -1; + } else { + sveFeature->policy =3D VIR_CPU_FEATURE_REQUIRE; + } + } else { + VIR_RESIZE_N(def->cpu->features, def->cpu->nfeatures_max, + def->cpu->nfeatures, 1); + + def->cpu->features[def->cpu->nfeatures].name =3D g_strdup("sve= "); + def->cpu->features[def->cpu->nfeatures].policy =3D VIR_CPU_FEA= TURE_REQUIRE; + + def->cpu->nfeatures++; + } + } + + /* Running domains were either started before QEMU_CAPS_CPU_MIGRATABLE= was + * introduced and thus we can't rely on it or they already have the + * migratable default set. */ + if (def->id =3D=3D -1 && + qemuCaps && + def->cpu->mode =3D=3D VIR_CPU_MODE_HOST_PASSTHROUGH && + def->cpu->migratable =3D=3D VIR_TRISTATE_SWITCH_ABSENT) { + if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_CPU_MIGRATABLE)) + def->cpu->migratable =3D VIR_TRISTATE_SWITCH_ON; + else if (ARCH_IS_X86(def->os.arch)) + def->cpu->migratable =3D VIR_TRISTATE_SWITCH_OFF; + } + + /* Nothing to be done if only CPU topology is specified. */ + if (def->cpu->mode =3D=3D VIR_CPU_MODE_CUSTOM && + !def->cpu->model) + return 0; + + if (def->cpu->check !=3D VIR_CPU_CHECK_DEFAULT) + return 0; + + switch ((virCPUMode) def->cpu->mode) { + case VIR_CPU_MODE_HOST_PASSTHROUGH: + case VIR_CPU_MODE_MAXIMUM: + def->cpu->check =3D VIR_CPU_CHECK_NONE; + break; + + case VIR_CPU_MODE_HOST_MODEL: + def->cpu->check =3D VIR_CPU_CHECK_PARTIAL; + break; + + case VIR_CPU_MODE_CUSTOM: + /* Custom CPUs in TCG mode are not compared to host CPU by default= . */ + if (def->virtType =3D=3D VIR_DOMAIN_VIRT_QEMU) + def->cpu->check =3D VIR_CPU_CHECK_NONE; + else + def->cpu->check =3D VIR_CPU_CHECK_PARTIAL; + break; + + case VIR_CPU_MODE_LAST: + break; + } + + return 0; +} + + +static int +qemuDomainDefTsegPostParse(virDomainDef *def, + virQEMUCaps *qemuCaps) +{ + if (def->features[VIR_DOMAIN_FEATURE_SMM] !=3D VIR_TRISTATE_SWITCH_ON) + return 0; + + if (!def->tseg_specified) + return 0; + + if (!qemuDomainIsQ35(def)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("SMM TSEG is only supported with q35 machine type= ")); + return -1; + } + + if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_MCH_EXTENDED_TSEG_MBYTES)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("Setting TSEG size is not supported with this QEM= U binary")); + return -1; + } + + if (def->tseg_size & ((1 << 20) - 1)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("SMM TSEG size must be divisible by 1 MiB")); + return -1; + } + + return 0; +} + + +static int +qemuDomainDefNumaAutoAdd(virDomainDef *def, + unsigned int parseFlags) +{ + bool abiUpdate =3D !!(parseFlags & VIR_DOMAIN_DEF_PARSE_ABI_UPDATE); + unsigned long long nodeMem; + size_t i; + + if (!abiUpdate || + !virDomainDefHasMemoryHotplug(def) || + virDomainNumaGetNodeCount(def->numa) > 0) { + return 0; + } + + nodeMem =3D virDomainDefGetMemoryTotal(def); + + if (!def->numa) + def->numa =3D virDomainNumaNew(); + + virDomainNumaSetNodeCount(def->numa, 1); + + for (i =3D 0; i < def->nmems; i++) { + virDomainMemoryDef *mem =3D def->mems[i]; + + if (mem->size > nodeMem) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("Total size of memory devices exceeds the tot= al memory size")); + return -1; + } + + nodeMem -=3D mem->size; + + switch (mem->model) { + case VIR_DOMAIN_MEMORY_MODEL_DIMM: + case VIR_DOMAIN_MEMORY_MODEL_NVDIMM: + case VIR_DOMAIN_MEMORY_MODEL_VIRTIO_MEM: + if (mem->targetNode =3D=3D -1) + mem->targetNode =3D 0; + break; + + case VIR_DOMAIN_MEMORY_MODEL_NONE: + case VIR_DOMAIN_MEMORY_MODEL_VIRTIO_PMEM: + case VIR_DOMAIN_MEMORY_MODEL_SGX_EPC: + case VIR_DOMAIN_MEMORY_MODEL_LAST: + break; + } + } + + virDomainNumaSetNodeMemorySize(def->numa, 0, nodeMem); + + return 0; +} + + +static int +qemuDomainDefNumaCPUsPostParse(virDomainDef *def, + virQEMUCaps *qemuCaps, + unsigned int parseFlags) +{ + if (qemuDomainDefNumaAutoAdd(def, parseFlags) < 0) + return -1; + + return qemuDomainDefNumaCPUsRectify(def, qemuCaps); +} + + +int +qemuDomainDefPostParse(virDomainDef *def, + unsigned int parseFlags, + void *opaque, + void *parseOpaque) +{ + virQEMUDriver *driver =3D opaque; + g_autoptr(virQEMUDriverConfig) cfg =3D virQEMUDriverGetConfig(driver); + virQEMUCaps *qemuCaps =3D parseOpaque; + + /* Note that qemuCaps may be NULL when this function is called. This + * function shall not fail in that case. It will be re-run on VM start= up + * with the capabilities populated. + */ + if (!qemuCaps) + return 1; + + if (qemuDomainDefMachinePostParse(def, qemuCaps) < 0) + return -1; + + qemuDomainDefACPIPostParse(def, qemuCaps, parseFlags); + + if (qemuDomainDefBootPostParse(def, driver, parseFlags) < 0) + return -1; + + if (qemuDomainDefAddDefaultDevices(driver, def, qemuCaps) < 0) + return -1; + + if (qemuDomainDefSetDefaultCPU(def, driver->hostarch, qemuCaps) < 0) + return -1; + + qemuDomainDefEnableDefaultFeatures(def, qemuCaps); + + if (qemuDomainRecheckInternalPaths(def, cfg, parseFlags) < 0) + return -1; + + if (qemuSecurityVerify(driver->securityManager, def) < 0) + return -1; + + if (qemuDomainDefVcpusPostParse(def) < 0) + return -1; + + if (qemuDomainDefCPUPostParse(def, qemuCaps) < 0) + return -1; + + if (qemuDomainDefTsegPostParse(def, qemuCaps) < 0) + return -1; + + if (qemuDomainDefNumaCPUsPostParse(def, qemuCaps, parseFlags) < 0) + return -1; + + return 0; +} + + +int +qemuDomainPostParseDataAlloc(const virDomainDef *def, + unsigned int parseFlags G_GNUC_UNUSED, + void *opaque, + void **parseOpaque) +{ + virQEMUDriver *driver =3D opaque; + + if (!(*parseOpaque =3D virQEMUCapsCacheLookup(driver->qemuCapsCache, + def->emulator))) + return 1; + + return 0; +} + + +void +qemuDomainPostParseDataFree(void *parseOpaque) +{ + virQEMUCaps *qemuCaps =3D parseOpaque; + + virObjectUnref(qemuCaps); +} diff --git a/src/qemu/qemu_postparse.h b/src/qemu/qemu_postparse.h new file mode 100644 index 0000000000..ac69c14604 --- /dev/null +++ b/src/qemu/qemu_postparse.h @@ -0,0 +1,54 @@ +/* + * qemu_postparse.h: QEMU domain PostParse functions + * + * Copyright (C) 2006-2024 Red Hat, Inc. + * Copyright (C) 2006 Daniel P. Berrange + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * . + */ + +#pragma once + +#include "virconftypes.h" + +int +qemuDomainDeviceDiskDefPostParse(virDomainDiskDef *disk, + unsigned int parseFlags); + +int +qemuDomainDeviceDefPostParse(virDomainDeviceDef *dev, + const virDomainDef *def, + unsigned int parseFlags, + void *opaque, + void *parseOpaque); + +int +qemuDomainDefPostParseBasic(virDomainDef *def, + void *opaque); + +int +qemuDomainDefPostParse(virDomainDef *def, + unsigned int parseFlags, + void *opaque, + void *parseOpaque); + +int +qemuDomainPostParseDataAlloc(const virDomainDef *def, + unsigned int parseFlags, + void *opaque, + void **parseOpaque); + +void +qemuDomainPostParseDataFree(void *parseOpaque); diff --git a/tests/qemublocktest.c b/tests/qemublocktest.c index 7ffa445c37..ac4d87b527 100644 --- a/tests/qemublocktest.c +++ b/tests/qemublocktest.c @@ -27,6 +27,7 @@ #include "qemu/qemu_monitor_json.h" #include "qemu/qemu_backup.h" #include "qemu/qemu_checkpoint.h" +#include "qemu/qemu_postparse.h" #include "qemu/qemu_validate.h" =20 #define LIBVIRT_SNAPSHOT_CONF_PRIV_H_ALLOW --=20 2.45.2