[PATCH] qemu: Move PostParse functions out of qemu_domain.c

Michal Privoznik posted 1 patch 1 week ago
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
[PATCH] qemu: Move PostParse functions out of qemu_domain.c
Posted by Michal Privoznik 1 week ago
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 <mprivozn@redhat.com>
---

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 = [
   '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 <sys/time.h>
 #include <fcntl.h>
 
-#define QEMU_QXL_VGAMEM_DEFAULT 16 * 1024
-
 #define VIR_FROM_THIS VIR_FROM_QEMU
 
 VIR_LOG_INIT("qemu.qemu_domain");
@@ -2020,7 +2018,7 @@ qemuDomainObjPrivateAlloc(void *opaque)
 }
 
 
-static int
+int
 qemuStorageSourcePrivateDataAssignSecinfo(qemuDomainSecretInfo **secinfo,
                                           char **alias)
 {
@@ -4001,26 +3999,6 @@ virXMLNamespace virQEMUDriverDomainXMLNamespace = {
 };
 
 
-static int
-qemuDomainDefAddImplicitInputDevice(virDomainDef *def,
-                                    virQEMUCaps *qemuCaps)
-{
-    if (virQEMUCapsSupportsI8042(qemuCaps, def) &&
-        def->features[VIR_DOMAIN_FEATURE_PS2] != 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;
 }
 
-static int
+int
 qemuDomainDefAddDefaultAudioBackend(virQEMUDriver *driver,
                                     virDomainDef *def)
 {
@@ -4227,7 +4205,7 @@ qemuDomainGetSCSIControllerModel(const virDomainDef *def,
 }
 
 
-static virDomainPanicModel
+virDomainPanicModel
 qemuDomainDefaultPanicModel(const virDomainDef *def)
 {
     if (qemuDomainIsPSeries(def))
@@ -4246,834 +4224,6 @@ qemuDomainDefaultPanicModel(const virDomainDef *def)
 }
 
 
-static int
-qemuDomainDefAddDefaultDevices(virQEMUDriver *driver,
-                               virDomainDef *def,
-                               virQEMUCaps *qemuCaps)
-{
-    bool addDefaultUSB = false;
-    int usbModel = -1; /* "default for machinetype" */
-    int pciRoot;       /* index within def->controllers */
-    bool addImplicitSATA = false;
-    bool addPCIRoot = false;
-    bool addPCIeRoot = false;
-    bool addDefaultMemballoon = false;
-    bool addDefaultUSBKBD = false;
-    bool addDefaultUSBMouse = false;
-    bool addPanicDevice = false;
-    bool addITCOWatchdog = false;
-    bool addIOMMU = 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 = true;
-
-        if (STREQ(def->os.machine, "isapc")) {
-            break;
-        }
-
-        addDefaultUSB = true;
-
-        if (qemuDomainIsQ35(def)) {
-            addPCIeRoot = true;
-            addImplicitSATA = true;
-            addITCOWatchdog = true;
-
-            if (virDomainDefGetVcpusMax(def) > QEMU_MAX_VCPUS_WITHOUT_EIM) {
-                addIOMMU = 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 = VIR_DOMAIN_CONTROLLER_MODEL_USB_QEMU_XHCI;
-            else if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_NEC_USB_XHCI))
-                usbModel = VIR_DOMAIN_CONTROLLER_MODEL_USB_NEC_XHCI;
-            else if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_ICH9_USB_EHCI1))
-                usbModel = VIR_DOMAIN_CONTROLLER_MODEL_USB_ICH9_EHCI1;
-            else
-                addDefaultUSB = false;
-            break;
-        }
-        if (qemuDomainIsI440FX(def))
-            addPCIRoot = 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 = 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 = true;
-            usbModel = VIR_DOMAIN_CONTROLLER_MODEL_USB_PCI_OHCI;
-        }
-
-        if (qemuDomainIsARMVirt(def))
-            addPCIeRoot = true;
-
-        break;
-
-    case VIR_ARCH_PPC64:
-    case VIR_ARCH_PPC64LE:
-        addPCIRoot = true;
-        addDefaultUSB = true;
-        addDefaultUSBKBD = true;
-        addDefaultUSBMouse = true;
-        addDefaultMemballoon = 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 = true;
-        break;
-
-    case VIR_ARCH_ALPHA:
-    case VIR_ARCH_PPC:
-    case VIR_ARCH_PPCEMB:
-    case VIR_ARCH_SH4:
-    case VIR_ARCH_SH4EB:
-        addDefaultUSB = true;
-        addDefaultMemballoon = true;
-        addPCIRoot = true;
-        break;
-
-    case VIR_ARCH_RISCV32:
-    case VIR_ARCH_RISCV64:
-        addDefaultMemballoon = true;
-        if (qemuDomainIsRISCVVirt(def))
-            addPCIeRoot = true;
-        break;
-
-    case VIR_ARCH_S390:
-    case VIR_ARCH_S390X:
-        addDefaultMemballoon = true;
-        addPanicDevice = true;
-        addPCIRoot = virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_ZPCI);
-        break;
-
-    case VIR_ARCH_SPARC64:
-        addDefaultUSB = true;
-        addDefaultMemballoon = true;
-        addPCIRoot = true;
-        break;
-
-    case VIR_ARCH_MIPS:
-    case VIR_ARCH_MIPSEL:
-    case VIR_ARCH_MIPS64:
-    case VIR_ARCH_MIPS64EL:
-        addDefaultUSB = true;
-        addDefaultMemballoon = true;
-        if (qemuDomainIsMipsMalta(def))
-            addPCIRoot = true;
-        break;
-
-    case VIR_ARCH_LOONGARCH64:
-        addPCIeRoot = 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 = virDomainControllerFind(def, VIR_DOMAIN_CONTROLLER_TYPE_PCI, 0);
-
-    /* NB: any machine that sets addPCIRoot to true must also return
-     * true from the function qemuDomainSupportsPCI().
-     */
-    if (addPCIRoot) {
-        if (pciRoot >= 0) {
-            if (def->controllers[pciRoot]->model != VIR_DOMAIN_CONTROLLER_MODEL_PCI_ROOT) {
-                virReportError(VIR_ERR_XML_ERROR,
-                               _("The PCI controller with index='0' must be model='pci-root' for this machine type, but model='%1$s' was found instead"),
-                               virDomainControllerModelPCITypeToString(def->controllers[pciRoot]->model));
-                return -1;
-            }
-        } else if (!virDomainDefAddController(def, VIR_DOMAIN_CONTROLLER_TYPE_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 >= 0) {
-            if (def->controllers[pciRoot]->model != VIR_DOMAIN_CONTROLLER_MODEL_PCIE_ROOT) {
-                virReportError(VIR_ERR_XML_ERROR,
-                               _("The PCI controller with index='0' must be model='pcie-root' for this machine type, but model='%1$s' was found instead"),
-                               virDomainControllerModelPCITypeToString(def->controllers[pciRoot]->model));
-                return -1;
-            }
-        } else if (!virDomainDefAddController(def, VIR_DOMAIN_CONTROLLER_TYPE_PCI, 0,
-                                             VIR_DOMAIN_CONTROLLER_MODEL_PCIE_ROOT)) {
-            return -1;
-        }
-    }
-
-    if (addDefaultMemballoon && !def->memballoon) {
-        virDomainMemballoonDef *memballoon;
-        memballoon = g_new0(virDomainMemballoonDef, 1);
-
-        memballoon->model = VIR_DOMAIN_MEMBALLOON_MODEL_VIRTIO;
-        def->memballoon = memballoon;
-    }
-
-    if (addDefaultUSBMouse) {
-        bool hasUSBTablet = false;
-        size_t j;
-
-        for (j = 0; j < def->ninputs; j++) {
-            if (def->inputs[j]->type == VIR_DOMAIN_INPUT_TYPE_TABLET &&
-                def->inputs[j]->bus == VIR_DOMAIN_INPUT_BUS_USB) {
-                hasUSBTablet = 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 = 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 = qemuDomainDefaultPanicModel(def);
-        size_t j;
-
-        for (j = 0; j < def->npanics; j++) {
-            if (def->panics[j]->model == VIR_DOMAIN_PANIC_MODEL_DEFAULT ||
-                def->panics[j]->model == defaultModel)
-                break;
-        }
-
-        if (j == def->npanics) {
-            virDomainPanicDef *panic = g_new0(virDomainPanicDef, 1);
-
-            VIR_APPEND_ELEMENT_COPY(def->panics, def->npanics, panic);
-        }
-    }
-
-    if (addITCOWatchdog) {
-        size_t i = 0;
-
-        for (i = 0; i < def->nwatchdogs; i++) {
-            if (def->watchdogs[i]->model == VIR_DOMAIN_WATCHDOG_MODEL_ITCO)
-                break;
-        }
-
-        if (i == def->nwatchdogs) {
-            virDomainWatchdogDef *watchdog = g_new0(virDomainWatchdogDef, 1);
-
-            watchdog->model = VIR_DOMAIN_WATCHDOG_MODEL_ITCO;
-            if (def->nwatchdogs)
-                watchdog->action = def->watchdogs[0]->action;
-            else
-                watchdog->action = 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 = NULL;
-
-        iommu = virDomainIOMMUDefNew();
-        iommu->model = VIR_DOMAIN_IOMMU_MODEL_INTEL;
-        /* eim requires intremap. */
-        iommu->intremap = VIR_TRISTATE_SWITCH_ON;
-        iommu->eim = VIR_TRISTATE_SWITCH_ON;
-
-        def->iommu = 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] == VIR_TRISTATE_SWITCH_ABSENT &&
-         qemuDomainIsARMVirt(def)) ||
-        (def->features[VIR_DOMAIN_FEATURE_GIC] == VIR_TRISTATE_SWITCH_ON &&
-         def->gic_version == VIR_GIC_VERSION_NONE)) {
-        virGICVersion version;
-
-        VIR_DEBUG("Looking for usable GIC version in domain capabilities");
-        for (version = 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 controller,
-             * 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, GICv2,
-             * which supports all the features we need.
-             *
-             * See https://bugzilla.redhat.com/show_bug.cgi?id=1414081 */
-            if (version == VIR_GIC_VERSION_3 &&
-                def->virtType == VIR_DOMAIN_VIRT_QEMU) {
-                continue;
-            }
-
-            if (virQEMUCapsSupportsGICVersion(qemuCaps,
-                                              def->virtType,
-                                              version)) {
-                VIR_DEBUG("Using GIC version %s",
-                          virGICVersionTypeToString(version));
-                def->gic_version = version;
-                break;
-            }
-        }
-
-        /* Use the default GIC version (GICv2) as a last-ditch attempt
-         * if no match could be found above */
-        if (def->gic_version == VIR_GIC_VERSION_NONE) {
-            VIR_DEBUG("Using GIC version 2 (default)");
-            def->gic_version = 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] = VIR_TRISTATE_SWITCH_ON;
-    }
-}
-
-
-static int
-qemuCanonicalizeMachine(virDomainDef *def, virQEMUCaps *qemuCaps)
-{
-    const char *canon;
-
-    if (!(canon = virQEMUCapsGetCanonicalMachine(qemuCaps, def->virtType,
-                                                 def->os.machine)))
-        return 0;
-
-    if (STRNEQ(canon, def->os.machine)) {
-        char *tmp;
-        tmp = g_strdup(canon);
-        VIR_FREE(def->os.machine);
-        def->os.machine = tmp;
-    }
-
-    return 0;
-}
-
-
-static int
-qemuDomainRecheckInternalPaths(virDomainDef *def,
-                               virQEMUDriverConfig *cfg,
-                               unsigned int flags)
-{
-    size_t i = 0;
-    size_t j = 0;
-
-    for (i = 0; i < def->ngraphics; ++i) {
-        virDomainGraphicsDef *graphics = def->graphics[i];
-
-        for (j = 0; j < graphics->nListens; ++j) {
-            virDomainGraphicsListenDef *glisten =  &graphics->listens[j];
-
-            /* This will happen only if we parse XML from old libvirts where
-             * unix socket was available only for VNC graphics.  In this
-             * particular case we should follow the behavior and if we remove
-             * the auto-generated socket based on config option from qemu.conf
-             * we need to change the listen type to address. */
-            if (graphics->type == VIR_DOMAIN_GRAPHICS_TYPE_VNC &&
-                glisten->type == VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_SOCKET &&
-                glisten->socket &&
-                !glisten->autoGenerated &&
-                STRPREFIX(glisten->socket, cfg->libDir)) {
-                if (flags & VIR_DOMAIN_DEF_PARSE_INACTIVE) {
-                    VIR_FREE(glisten->socket);
-                    glisten->type = VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_ADDRESS;
-                } else {
-                    glisten->fromConfig = true;
-                }
-            }
-        }
-    }
-
-    return 0;
-}
-
-
-static int
-qemuDomainDefBootPostParse(virDomainDef *def,
-                           virQEMUDriver *driver,
-                           unsigned int parseFlags)
-{
-    bool abiUpdate = !!(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 = virDomainLoaderDefNew();
-        if (!def->os.loader->format)
-            def->os.loader->format = 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 = virQEMUCapsGetPreferredMachine(qemuCaps,
-                                                             def->virtType);
-        if (!machine) {
-            virReportError(VIR_ERR_INVALID_ARG,
-                           _("could not get preferred machine for %1$s type=%2$s"),
-                           def->emulator,
-                           virDomainVirtTypeToString(def->virtType));
-            return -1;
-        }
-
-        def->os.machine = g_strdup(machine);
-    }
-
-    if (qemuCanonicalizeMachine(def, qemuCaps) < 0)
-        return -1;
-
-    return 0;
-}
-
-
-static int
-qemuDomainDefVcpusPostParse(virDomainDef *def)
-{
-    unsigned int maxvcpus = virDomainDefGetVcpusMax(def);
-    virDomainVcpuDef *vcpu;
-    virDomainVcpuDef *prevvcpu;
-    size_t i;
-    bool has_order = false;
-
-    /* vcpu 0 needs to be present, first, and non-hotpluggable */
-    vcpu = virDomainDefGetVcpu(def, 0);
-    if (!vcpu->online) {
-        virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
-                       _("vcpu 0 can't be offline"));
-        return -1;
-    }
-    if (vcpu->hotpluggable == VIR_TRISTATE_BOOL_YES) {
-        virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
-                       _("vcpu0 can't be hotpluggable"));
-        return -1;
-    }
-    if (vcpu->order != 0 && vcpu->order != 1) {
-        virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
-                       _("vcpu0 must be enabled first"));
-        return -1;
-    }
-
-    if (vcpu->order != 0)
-        has_order = true;
-
-    prevvcpu = vcpu;
-
-    /* all online vcpus or non online vcpu need to have order set */
-    for (i = 1; i < maxvcpus; i++) {
-        vcpu = virDomainDefGetVcpu(def, i);
-
-        if (vcpu->online &&
-            (vcpu->order != 0) != has_order) {
-            virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
-                           _("all vcpus must have either set or unset order"));
-            return -1;
-        }
-
-        /* few conditions for non-hotpluggable (thus online) vcpus */
-        if (vcpu->hotpluggable == VIR_TRISTATE_BOOL_NO) {
-            /* they can be ordered only at the beginning */
-            if (prevvcpu->hotpluggable == 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 yet).
-             * Also note that multiple vcpus may share order on some platforms */
-            if (prevvcpu->order > vcpu->order) {
-                virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
-                               _("online non-hotpluggable vcpus must be ordered in ascending order"));
-                return -1;
-            }
-        }
-
-        prevvcpu = vcpu;
-    }
-
-    return 0;
-}
-
-
-static int
-qemuDomainDefSetDefaultCPU(virDomainDef *def,
-                           virArch hostarch,
-                           virQEMUCaps *qemuCaps)
-{
-    const char *model;
-
-    if (def->cpu &&
-        (def->cpu->mode != 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 != VIR_DOMAIN_VIRT_QEMU)
-        return 0;
-
-    model = virQEMUCapsGetMachineDefaultCPU(qemuCaps, def->os.machine, def->virtType);
-    if (!model) {
-        VIR_DEBUG("Unknown default CPU model for domain '%s'", def->name);
-        return 0;
-    }
-
-    if (STREQ(model, "host") && def->virtType != 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 = virCPUDefNew();
-
-    def->cpu->type = VIR_CPU_TYPE_GUEST;
-
-    if (STREQ(model, "host")) {
-        if (ARCH_IS_S390(def->os.arch) &&
-            virQEMUCapsIsCPUModeSupported(qemuCaps, hostarch, def->virtType,
-                                          VIR_CPU_MODE_HOST_MODEL,
-                                          def->os.machine)) {
-            def->cpu->mode = VIR_CPU_MODE_HOST_MODEL;
-        } else {
-            def->cpu->mode = 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 any
-         * host. QEMU will just disable the unavailable features and we will
-         * update the CPU definition accordingly and set check to FULL when
-         * starting the domain. */
-        def->cpu->check = VIR_CPU_CHECK_NONE;
-        def->cpu->mode = VIR_CPU_MODE_CUSTOM;
-        def->cpu->match = VIR_CPU_MATCH_EXACT;
-        def->cpu->fallback = VIR_CPU_FALLBACK_FORBID;
-        def->cpu->model = 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 = NULL;
-    bool sveVectorLengthsProvided = false;
-    size_t i;
-
-    if (!def->cpu)
-        return 0;
-
-    for (i = 0; i < def->cpu->nfeatures; i++) {
-        virCPUFeatureDef *feature = &def->cpu->features[i];
-
-        if (STREQ(feature->name, "sve")) {
-            sveFeature = feature;
-        } else if (STRPREFIX(feature->name, "sve")) {
-            sveVectorLengthsProvided = true;
-        }
-    }
-
-    if (sveVectorLengthsProvided) {
-        if (sveFeature) {
-            if (sveFeature->policy == VIR_CPU_FEATURE_DISABLE ||
-                sveFeature->policy == VIR_CPU_FEATURE_FORBID) {
-                virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
-                               _("SVE disabled, but SVE vector lengths provided"));
-                return -1;
-            } else {
-                sveFeature->policy = 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 = g_strdup("sve");
-            def->cpu->features[def->cpu->nfeatures].policy = VIR_CPU_FEATURE_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 == -1 &&
-        qemuCaps &&
-        def->cpu->mode == VIR_CPU_MODE_HOST_PASSTHROUGH &&
-        def->cpu->migratable == VIR_TRISTATE_SWITCH_ABSENT) {
-        if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_CPU_MIGRATABLE))
-            def->cpu->migratable = VIR_TRISTATE_SWITCH_ON;
-        else if (ARCH_IS_X86(def->os.arch))
-            def->cpu->migratable = VIR_TRISTATE_SWITCH_OFF;
-    }
-
-    /* Nothing to be done if only CPU topology is specified. */
-    if (def->cpu->mode == VIR_CPU_MODE_CUSTOM &&
-        !def->cpu->model)
-        return 0;
-
-    if (def->cpu->check != 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 = VIR_CPU_CHECK_NONE;
-        break;
-
-    case VIR_CPU_MODE_HOST_MODEL:
-        def->cpu->check = 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 == VIR_DOMAIN_VIRT_QEMU)
-            def->cpu->check = VIR_CPU_CHECK_NONE;
-        else
-            def->cpu->check = 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] != 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 QEMU 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 = !!(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 = virDomainDefGetMemoryTotal(def);
-
-    if (!def->numa)
-        def->numa = virDomainNumaNew();
-
-    virDomainNumaSetNodeCount(def->numa, 1);
-
-    for (i = 0; i < def->nmems; i++) {
-        virDomainMemoryDef *mem = def->mems[i];
-
-        if (mem->size > nodeMem) {
-            virReportError(VIR_ERR_XML_ERROR, "%s",
-                           _("Total size of memory devices exceeds the total memory size"));
-            return -1;
-        }
-
-        nodeMem -= 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 == -1)
-                mem->targetNode = 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,
 }
 
 
-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 = opaque;
-
-    /* check for emulator and create a default one if needed */
-    if (!def->emulator) {
-        if (!(def->emulator = 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 supported 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] != VIR_TRISTATE_SWITCH_ON)
-        return;
-
-    /* Strip the <acpi/> feature only for non-fresh configs, in order to still
-     * 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-intuitive,
-     * 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
-     * <acpi/> despite being ignored.
-     */
-    if (def->os.arch != VIR_ARCH_S390 &&
-        def->os.arch != VIR_ARCH_S390X)
-        return;
-
-    /* To be sure, we only strip ACPI if given machine type doesn't support it */
-    if (virQEMUCapsMachineSupportsACPI(qemuCaps, def->virtType, def->os.machine) != VIR_TRISTATE_BOOL_NO)
-        return;
-
-    def->features[VIR_DOMAIN_FEATURE_ACPI] = VIR_TRISTATE_SWITCH_ABSENT;
-}
-
-
-static int
-qemuDomainDefPostParse(virDomainDef *def,
-                       unsigned int parseFlags,
-                       void *opaque,
-                       void *parseOpaque)
-{
-    virQEMUDriver *driver = opaque;
-    g_autoptr(virQEMUDriverConfig) cfg = virQEMUDriverGetConfig(driver);
-    virQEMUCaps *qemuCaps = 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 startup
-     * 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 *src,
 }
 
 
-/**
- * qemuDomainDefaultNetModel:
- * @def: domain definition
- * @qemuCaps: qemu capabilities
- *
- * Returns the default network model for a given domain. Note that if @qemuCaps
- * is NULL this function may return NULL if the default model depends on the
- * 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 were
-     * 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 derived
  * from cfg->libDir but rather cfg->stateDir.
  */
-static void
+void
 qemuDomainChrDefDropDefaultPath(virDomainChrDef *chr,
                                 virQEMUDriver *driver)
 {
@@ -5750,640 +4708,7 @@ qemuDomainChrDefDropDefaultPath(virDomainChrDef *chr,
 }
 
 
-static int
-qemuDomainShmemDefPostParse(virDomainShmemDef *shm)
-{
-    /* This was the default since the introduction of this device. */
-    if (shm->model != VIR_DOMAIN_SHMEM_MODEL_IVSHMEM_DOORBELL && !shm->size)
-        shm->size = 4 << 20;
-
-    /* Nothing more to check/change for IVSHMEM */
-    if (shm->model == VIR_DOMAIN_SHMEM_MODEL_IVSHMEM)
-        return 0;
-
-    if (!shm->server.enabled) {
-        if (shm->model == VIR_DOMAIN_SHMEM_MODEL_IVSHMEM_DOORBELL) {
-            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
-                           _("shmem model '%1$s' is supported only with server 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 == VIR_DOMAIN_SHMEM_MODEL_IVSHMEM_PLAIN) {
-            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
-                           _("shmem model '%1$s' is supported only with server option disabled"),
-                           virDomainShmemModelTypeToString(shm->model));
-            return -1;
-        }
-
-        if (shm->size) {
-            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
-                           _("shmem model '%1$s' does not support size setting"),
-                           virDomainShmemModelTypeToString(shm->model));
-            return -1;
-        }
-        shm->msi.enabled = true;
-        if (!shm->msi.ioeventfd)
-            shm->msi.ioeventfd = 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 = qemuDomainGetSCSIControllerModel(def, cont, qemuCaps);
-
-        if (cont->model < 0)
-            return -1;
-        break;
-
-    case VIR_DOMAIN_CONTROLLER_TYPE_USB:
-        if (cont->model == 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 back to
-             * 'pci-ohci' otherwise which is the default for non-x86 machines
-             * which honour -usb */
-            if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_PIIX3_USB_UHCI))
-                cont->model = VIR_DOMAIN_CONTROLLER_MODEL_USB_PIIX3_UHCI;
-            else if (!ARCH_IS_X86(def->os.arch) &&
-                     virQEMUCapsGet(qemuCaps, QEMU_CAPS_PCI_OHCI))
-                cont->model = VIR_DOMAIN_CONTROLLER_MODEL_USB_PCI_OHCI;
-
-            if (ARCH_IS_S390(def->os.arch)) {
-                if (cont->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) {
-                    /* set the default USB model to none for s390 unless an
-                     * address is found */
-                    cont->model = VIR_DOMAIN_CONTROLLER_MODEL_USB_NONE;
-                }
-            } else if (ARCH_IS_PPC64(def->os.arch)) {
-                /* To not break migration we need to set default USB controller
-                 * for ppc64 to pci-ohci if we cannot change ABI of the VM.
-                 * The nec-usb-xhci or qemu-xhci controller is used as default
-                 * only for newly defined domains or devices. */
-                if ((parseFlags & VIR_DOMAIN_DEF_PARSE_ABI_UPDATE) &&
-                    virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_QEMU_XHCI)) {
-                    cont->model = VIR_DOMAIN_CONTROLLER_MODEL_USB_QEMU_XHCI;
-                } else if ((parseFlags & VIR_DOMAIN_DEF_PARSE_ABI_UPDATE) &&
-                    virQEMUCapsGet(qemuCaps, QEMU_CAPS_NEC_USB_XHCI)) {
-                    cont->model = VIR_DOMAIN_CONTROLLER_MODEL_USB_NEC_XHCI;
-                } else if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_PCI_OHCI)) {
-                    cont->model = VIR_DOMAIN_CONTROLLER_MODEL_USB_PCI_OHCI;
-                } else {
-                    /* Explicitly fallback to legacy USB controller for PPC64. */
-                    cont->model = VIR_DOMAIN_CONTROLLER_MODEL_USB_DEFAULT;
-                }
-            } else if (def->os.arch == VIR_ARCH_AARCH64) {
-                if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_QEMU_XHCI))
-                    cont->model = VIR_DOMAIN_CONTROLLER_MODEL_USB_QEMU_XHCI;
-                else if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_NEC_USB_XHCI))
-                    cont->model = VIR_DOMAIN_CONTROLLER_MODEL_USB_NEC_XHCI;
-            } else if (ARCH_IS_LOONGARCH(def->os.arch)) {
-                if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_QEMU_XHCI))
-                    cont->model = VIR_DOMAIN_CONTROLLER_MODEL_USB_QEMU_XHCI;
-            }
-        }
-        /* forbid usb model 'qusb1' and 'qusb2' in this kind of hyperviosr */
-        if (cont->model == VIR_DOMAIN_CONTROLLER_MODEL_USB_QUSB1 ||
-            cont->model == 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 == VIR_DOMAIN_CONTROLLER_MODEL_USB_NEC_XHCI ||
-             cont->model == 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->model),
-                           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 == VIR_DOMAIN_CONTROLLER_MODEL_PCI_ROOT ||
-             cont->model == VIR_DOMAIN_CONTROLLER_MODEL_PCIE_ROOT) &&
-            cont->idx != 0) {
-            virReportError(VIR_ERR_XML_ERROR, "%s",
-                           _("pci-root and pcie-root controllers should have index 0"));
-            return -1;
-        }
-
-        if (cont->model == VIR_DOMAIN_CONTROLLER_MODEL_PCI_EXPANDER_BUS &&
-            !qemuDomainIsI440FX(def)) {
-            virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
-                           _("pci-expander-bus controllers are only supported on 440fx-based machinetypes"));
-            return -1;
-        }
-        if (cont->model == VIR_DOMAIN_CONTROLLER_MODEL_PCIE_EXPANDER_BUS &&
-            !(qemuDomainIsQ35(def) || qemuDomainIsARMVirt(def))) {
-            virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
-                           _("pcie-expander-bus controllers are not supported 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
-         * <cpu><numa> array. NUMA cell id's in this array are numbered
-         * from 0 .. size-1.
-         */
-        if (cont->opts.pciopts.numaNode >= 0 &&
-            cont->opts.pciopts.numaNode >=
-            (int)virDomainNumaGetNodeCount(def->numa)) {
-            virReportError(VIR_ERR_XML_ERROR,
-                           _("%1$s with index %2$d is configured for a NUMA node (%3$d) not present in the domain's <cpu><numa> array (%4$zu)"),
-                           virDomainControllerModelPCITypeToString(cont->model),
-                           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 default
-     * will be picked below based on the architecture and machine type. */
-    if (chr->deviceType == VIR_DOMAIN_CHR_DEVICE_TYPE_SERIAL &&
-        chr->targetType == VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_ISA) {
-        chr->targetType = VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_NONE;
-    }
-
-    /* Set the default serial type */
-    if (chr->deviceType == VIR_DOMAIN_CHR_DEVICE_TYPE_SERIAL &&
-        chr->targetType == VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_NONE) {
-        if (ARCH_IS_X86(def->os.arch)) {
-            chr->targetType = VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_ISA;
-        } else if (qemuDomainIsPSeries(def)) {
-            chr->targetType = VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_SPAPR_VIO;
-        } else if (qemuDomainIsARMVirt(def) ||
-                   qemuDomainIsLoongArchVirt(def) ||
-                   qemuDomainIsRISCVVirt(def)) {
-            chr->targetType = VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_SYSTEM;
-        } else if (ARCH_IS_S390(def->os.arch)) {
-            chr->targetType = VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_SCLP;
-        }
-    }
-
-    /* Set the default target model */
-    if (chr->deviceType == VIR_DOMAIN_CHR_DEVICE_TYPE_SERIAL &&
-        chr->targetModel == VIR_DOMAIN_CHR_SERIAL_TARGET_MODEL_NONE) {
-        switch ((virDomainChrSerialTargetType)chr->targetType) {
-        case VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_ISA:
-            chr->targetModel = VIR_DOMAIN_CHR_SERIAL_TARGET_MODEL_ISA_SERIAL;
-            break;
-        case VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_USB:
-            chr->targetModel = VIR_DOMAIN_CHR_SERIAL_TARGET_MODEL_USB_SERIAL;
-            break;
-        case VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_PCI:
-            chr->targetModel = VIR_DOMAIN_CHR_SERIAL_TARGET_MODEL_PCI_SERIAL;
-            break;
-        case VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_SPAPR_VIO:
-            chr->targetModel = VIR_DOMAIN_CHR_SERIAL_TARGET_MODEL_SPAPR_VTY;
-            break;
-        case VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_SYSTEM:
-            if (qemuDomainIsARMVirt(def)) {
-                chr->targetModel = VIR_DOMAIN_CHR_SERIAL_TARGET_MODEL_PL011;
-            } else if (qemuDomainIsLoongArchVirt(def) ||
-                       qemuDomainIsRISCVVirt(def)) {
-                chr->targetModel = VIR_DOMAIN_CHR_SERIAL_TARGET_MODEL_16550A;
-            }
-            break;
-        case VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_SCLP:
-            chr->targetModel = VIR_DOMAIN_CHR_SERIAL_TARGET_MODEL_SCLPCONSOLE;
-            break;
-        case VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_ISA_DEBUG:
-            chr->targetModel = VIR_DOMAIN_CHR_SERIAL_TARGET_MODEL_ISA_DEBUGCON;
-            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 == VIR_DOMAIN_CHR_TYPE_UNIX &&
-            !chr->source->data.nix.path) {
-            chr->source->data.nix.listen = 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 = QEMU_DOMAIN_STORAGE_SOURCE_PRIVATE(disk->src);
-    bool restoreAuthSecret = false;
-    bool restoreEncSecret = false;
-    g_autofree char *authalias = NULL;
-    g_autofree char *encalias = 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 authentication
-         * using secret object at the time we did not format the alias into the
-         * status XML */
-        if (virStorageSourceGetActualType(disk->src) == VIR_STORAGE_TYPE_NETWORK &&
-            (disk->src->protocol == VIR_STORAGE_NET_PROTOCOL_RBD ||
-             disk->src->protocol == VIR_STORAGE_NET_PROTOCOL_ISCSI))
-            restoreAuthSecret = true;
-    }
-
-    /* disk encryption secret */
-    if (disk->src->encryption &&
-        disk->src->encryption->format == VIR_STORAGE_ENCRYPTION_FORMAT_LUKS &&
-        (!priv || !priv->encinfo))
-        restoreEncSecret = true;
-
-    if (!restoreAuthSecret && !restoreEncSecret)
-        return 0;
-
-    if (!priv) {
-        if (!(disk->src->privateData = qemuDomainStorageSourcePrivateNew()))
-            return -1;
-
-        priv = QEMU_DOMAIN_STORAGE_SOURCE_PRIVATE(disk->src);
-    }
-
-    if (restoreAuthSecret) {
-        authalias = g_strdup_printf("%s-secret0", disk->info.alias);
-
-        if (qemuStorageSourcePrivateDataAssignSecinfo(&priv->secinfo, &authalias) < 0)
-            return -1;
-    }
-
-    if (restoreEncSecret) {
-        if (!priv->encinfo) {
-            priv->enccount = 1;
-            priv->encinfo = g_new0(qemuDomainSecretInfo *, 1);
-        }
-
-        encalias = 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) == VIR_STORAGE_FILE_NONE &&
-        virDomainDiskGetType(disk) != VIR_STORAGE_TYPE_VOLUME)
-        virDomainDiskSetFormat(disk, VIR_STORAGE_FILE_RAW);
-
-    /* default disk format for mirrored drive */
-    if (disk->mirror &&
-        disk->mirror->format == VIR_STORAGE_FILE_NONE)
-        disk->mirror->format = VIR_STORAGE_FILE_RAW;
-
-    /* default disk encryption engine */
-    for (n = disk->src; virStorageSourceIsBacking(n); n = n->backingStore) {
-        if (n->encryption && n->encryption->engine == VIR_STORAGE_ENCRYPTION_ENGINE_DEFAULT)
-            n->encryption->engine = 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 == VIR_TRISTATE_BOOL_YES &&
-        !disk->src->tlsAlias &&
-        !(disk->src->tlsAlias = qemuAliasTLSObjFromSrcAlias(disk->info.alias)))
-        return -1;
-
-    return 0;
-}
-
-
-static int
-qemuDomainDeviceNetDefPostParse(virDomainNetDef *net,
-                                const virDomainDef *def,
-                                virQEMUCaps *qemuCaps)
-{
-    if (net->type == VIR_DOMAIN_NET_TYPE_VDPA &&
-        !virDomainNetGetModelString(net)) {
-        net->model = VIR_DOMAIN_NET_MODEL_VIRTIO;
-    } else if (net->type != VIR_DOMAIN_NET_TYPE_HOSTDEV &&
-        !virDomainNetGetModelString(net) &&
-        virDomainNetResolveActualType(net) != VIR_DOMAIN_NET_TYPE_HOSTDEV) {
-        net->model = qemuDomainDefaultNetModel(def, qemuCaps);
-    }
-
-    if (net->type == VIR_DOMAIN_NET_TYPE_USER &&
-        net->backend.type == VIR_DOMAIN_NET_BACKEND_DEFAULT) {
-        virDomainCapsDeviceNet netCaps = { };
-
-        virQEMUCapsFillDomainDeviceNetCaps(qemuCaps, &netCaps);
-
-        if (!VIR_DOMAIN_CAPS_ENUM_IS_SET(netCaps.backendType, VIR_DOMAIN_NET_BACKEND_DEFAULT) &&
-            VIR_DOMAIN_CAPS_ENUM_IS_SET(netCaps.backendType, VIR_DOMAIN_NET_BACKEND_PASST)) {
-            net->backend.type = 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 == VIR_DOMAIN_VIDEO_TYPE_DEFAULT)
-        video->type = qemuDomainDefaultVideoDevice(def, qemuCaps);
-
-    if (video->type == VIR_DOMAIN_VIDEO_TYPE_QXL &&
-        !video->vgamem) {
-        video->vgamem = QEMU_QXL_VGAMEM_DEFAULT;
-    }
-
-    return 0;
-}
-
-
-static int
-qemuDomainDevicePanicDefPostParse(virDomainPanicDef *panic,
-                                  const virDomainDef *def)
-{
-    if (panic->model == VIR_DOMAIN_PANIC_MODEL_DEFAULT)
-        panic->model = qemuDomainDefaultPanicModel(def);
-
-    return 0;
-}
-
-
-static int
-qemuDomainVsockDefPostParse(virDomainVsockDef *vsock)
-{
-    if (vsock->model == VIR_DOMAIN_VSOCK_MODEL_DEFAULT)
-        vsock->model = 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 *hostdev,
-                                                   unsigned int parseFlags)
-{
-    qemuDomainStorageSourcePrivate *priv;
-    virDomainHostdevSubsysSCSI *scsisrc = &hostdev->source.subsys.u.scsi;
-    virDomainHostdevSubsysSCSIiSCSI *iscsisrc = &scsisrc->u.iscsi;
-    g_autofree char *authalias = NULL;
-
-    if (!(parseFlags & VIR_DOMAIN_DEF_PARSE_STATUS))
-        return 0;
-
-    if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS ||
-        hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI ||
-        scsisrc->protocol != VIR_DOMAIN_HOSTDEV_SCSI_PROTOCOL_TYPE_ISCSI ||
-        !iscsisrc->src->auth)
-        return 0;
-
-    if (!(priv = qemuDomainStorageSourcePrivateFetch(iscsisrc->src)))
-        return -1;
-
-    if (priv->secinfo)
-        return 0;
-
-    authalias = g_strdup_printf("%s-secret0", hostdev->info->alias);
-
-    if (qemuStorageSourcePrivateDataAssignSecinfo(&priv->secinfo, &authalias) < 0)
-        return -1;
-
-    return 0;
-}
-
-
-/**
- * qemuDomainDeviceHostdevDefPostParseRestoreBackendAlias:
- *
- * Re-generate backend alias if it wasn't stored in the status XML by an older
- * libvirtd.
- *
- * Note that qemuCaps should be always present for a status XML.
- */
-static int
-qemuDomainDeviceHostdevDefPostParseRestoreBackendAlias(virDomainHostdevDef *hostdev,
-                                                       unsigned int parseFlags)
-{
-    virDomainHostdevSubsysSCSI *scsisrc = &hostdev->source.subsys.u.scsi;
-    virStorageSource *src;
-
-    if (!(parseFlags & VIR_DOMAIN_DEF_PARSE_STATUS))
-        return 0;
-
-    if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS ||
-        hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI)
-        return 0;
-
-    switch (scsisrc->protocol) {
-    case VIR_DOMAIN_HOSTDEV_SCSI_PROTOCOL_TYPE_NONE:
-        if (!scsisrc->u.host.src)
-            scsisrc->u.host.src = virStorageSourceNew();
-
-        src = scsisrc->u.host.src;
-        break;
-
-    case VIR_DOMAIN_HOSTDEV_SCSI_PROTOCOL_TYPE_ISCSI:
-        src = 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("libvirt-%s-backend", hostdev->info->alias));
-
-    return 0;
-}
-
-
-static int
-qemuDomainHostdevDefMdevPostParse(virDomainHostdevSubsysMediatedDev *mdevsrc)
-{
-    /* QEMU 2.12 added support for vfio-pci display type, we default to
-     * 'display=off' to stay safe from future changes */
-    if (mdevsrc->model == VIR_MDEV_MODEL_TYPE_VFIO_PCI &&
-        mdevsrc->display == VIR_TRISTATE_SWITCH_ABSENT)
-        mdevsrc->display = VIR_TRISTATE_SWITCH_OFF;
-
-    return 0;
-}
-
-
-static int
-qemuDomainHostdevDefPostParse(virDomainHostdevDef *hostdev,
-                              unsigned int parseFlags)
-{
-    virDomainHostdevSubsys *subsys = &hostdev->source.subsys;
-
-    if (qemuDomainDeviceHostdevDefPostParseRestoreSecAlias(hostdev, parseFlags) < 0)
-        return -1;
-
-    if (qemuDomainDeviceHostdevDefPostParseRestoreBackendAlias(hostdev, parseFlags) < 0)
-        return -1;
-
-    if (hostdev->mode == VIR_DOMAIN_HOSTDEV_MODE_SUBSYS &&
-        hostdev->source.subsys.type == 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 == VIR_DOMAIN_TPM_MODEL_DEFAULT) {
-        if (ARCH_IS_PPC64(def->os.arch))
-            tpm->model = VIR_DOMAIN_TPM_MODEL_SPAPR;
-        else
-            tpm->model = VIR_DOMAIN_TPM_MODEL_TIS;
-    }
-
-    /* TPM 1.2 and 2 are not compatible, so we choose a specific version here */
-    if (tpm->type == VIR_DOMAIN_TPM_TYPE_EMULATOR &&
-        tpm->data.emulator.version == 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 == VIR_DOMAIN_TPM_MODEL_TIS &&
-            ARCH_IS_X86(def->os.arch)) {
-            tpm->data.emulator.version = VIR_DOMAIN_TPM_VERSION_1_2;
-        } else {
-            tpm->data.emulator.version = 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)
 }
 
 
-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 = 256 * 1024;
-
-            if (mem->model == VIR_DOMAIN_MEMORY_MODEL_NVDIMM) {
-                if (qemuDomainNVDimmAlignSizePseries(mem) < 0)
-                    return -1;
-            } else {
-                mem->size = VIR_ROUND_UP(mem->size, ppc64MemModuleAlign);
-            }
-        }
-    }
-
-    return 0;
-}
-
-
-static int
-qemuDomainPstoreDefPostParse(virDomainPstoreDef *pstore,
-                             const virDomainDef *def,
-                             virQEMUDriver *driver)
-{
-    g_autoptr(virQEMUDriverConfig) cfg = virQEMUDriverGetConfig(driver);
-
-    switch (pstore->backend) {
-    case VIR_DOMAIN_PSTORE_BACKEND_ACPI_ERST:
-        if (!pstore->path)
-            pstore->path = 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 then
-     * 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 == VIR_DOMAIN_IOMMU_MODEL_INTEL) {
-
-        /* eim requires intremap. */
-        if (iommu->intremap == VIR_TRISTATE_SWITCH_ABSENT &&
-            virQEMUCapsGet(qemuCaps, QEMU_CAPS_INTEL_IOMMU_INTREMAP)) {
-            iommu->intremap = VIR_TRISTATE_SWITCH_ON;
-        }
-
-        if (iommu->eim == VIR_TRISTATE_SWITCH_ABSENT &&
-            virQEMUCapsGet(qemuCaps, QEMU_CAPS_INTEL_IOMMU_EIM)) {
-            iommu->eim = VIR_TRISTATE_SWITCH_ON;
-        }
-    }
-
-    return 0;
-}
-
-
-static int
-qemuDomainDeviceDefPostParse(virDomainDeviceDef *dev,
-                             const virDomainDef *def,
-                             unsigned int parseFlags,
-                             void *opaque,
-                             void *parseOpaque)
-{
-    virQEMUDriver *driver = 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 startup
-     * with the capabilities populated. */
-    virQEMUCaps *qemuCaps = parseOpaque;
-    int ret = -1;
-
-    switch (dev->type) {
-    case VIR_DOMAIN_DEVICE_NET:
-        ret = qemuDomainDeviceNetDefPostParse(dev->data.net, def, qemuCaps);
-        break;
-
-    case VIR_DOMAIN_DEVICE_DISK:
-        ret = qemuDomainDeviceDiskDefPostParse(dev->data.disk, parseFlags);
-        break;
-
-    case VIR_DOMAIN_DEVICE_VIDEO:
-        ret = qemuDomainDeviceVideoDefPostParse(dev->data.video, def, qemuCaps);
-        break;
-
-    case VIR_DOMAIN_DEVICE_PANIC:
-        ret = qemuDomainDevicePanicDefPostParse(dev->data.panic, def);
-        break;
-
-    case VIR_DOMAIN_DEVICE_CONTROLLER:
-        ret = qemuDomainControllerDefPostParse(dev->data.controller, def,
-                                               qemuCaps, parseFlags);
-        break;
-
-    case VIR_DOMAIN_DEVICE_SHMEM:
-        ret = qemuDomainShmemDefPostParse(dev->data.shmem);
-        break;
-
-    case VIR_DOMAIN_DEVICE_CHR:
-        ret = qemuDomainChrDefPostParse(dev->data.chr, def, driver, parseFlags);
-        break;
-
-    case VIR_DOMAIN_DEVICE_VSOCK:
-        ret = qemuDomainVsockDefPostParse(dev->data.vsock);
-        break;
-
-    case VIR_DOMAIN_DEVICE_HOSTDEV:
-        ret = qemuDomainHostdevDefPostParse(dev->data.hostdev, parseFlags);
-        break;
-
-    case VIR_DOMAIN_DEVICE_TPM:
-        ret = qemuDomainTPMDefPostParse(dev->data.tpm, def);
-        break;
-
-    case VIR_DOMAIN_DEVICE_MEMORY:
-        ret = qemuDomainMemoryDefPostParse(dev->data.memory, def->os.arch,
-                                           parseFlags);
-        break;
-
-    case VIR_DOMAIN_DEVICE_PSTORE:
-        ret = qemuDomainPstoreDefPostParse(dev->data.pstore, def, driver);
-        break;
-
-    case VIR_DOMAIN_DEVICE_IOMMU:
-        ret = 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 = 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,
 }
 
 
-static int
-qemuDomainPostParseDataAlloc(const virDomainDef *def,
-                             unsigned int parseFlags G_GNUC_UNUSED,
-                             void *opaque,
-                             void **parseOpaque)
-{
-    virQEMUDriver *driver = opaque;
-
-    if (!(*parseOpaque = virQEMUCapsCacheLookup(driver->qemuCapsCache,
-                                                def->emulator)))
-        return 1;
-
-    return 0;
-}
-
-
-static void
-qemuDomainPostParseDataFree(void *parseOpaque)
-{
-    virQEMUCaps *qemuCaps = parseOpaque;
-
-    virObjectUnref(qemuCaps);
-}
-
-
 virDomainDefParserConfig virQEMUDriverDomainDefParserConfig = {
     .domainPostParseBasicCallback = qemuDomainDefPostParseBasic,
     .domainPostParseDataAlloc = 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,
 
 void qemuDomainObjPrivateDataClear(qemuDomainObjPrivate *priv);
 
+int qemuStorageSourcePrivateDataAssignSecinfo(qemuDomainSecretInfo **secinfo,
+                                              char **alias);
+
 extern virDomainXMLPrivateDataCallbacks virQEMUDriverPrivateDataCallbacks;
 extern virXMLNamespace virQEMUDriverDomainXMLNamespace;
 extern virDomainDefParserConfig virQEMUDriverDomainDefParserConfig;
@@ -849,6 +852,11 @@ int qemuDomainGetSCSIControllerModel(const virDomainDef *def,
                                      const virDomainControllerDef *cont,
                                      virQEMUCaps *qemuCaps);
 
+int qemuDomainDefAddDefaultAudioBackend(virQEMUDriver *driver,
+                                        virDomainDef *def);
+
+virDomainPanicModel qemuDomainDefaultPanicModel(const virDomainDef *def);
+
 void qemuDomainUpdateCurrentMemorySize(virDomainObj *vm);
 
 unsigned long long qemuDomainGetMemLockLimitBytes(virDomainDef *def);
@@ -938,8 +946,12 @@ int qemuDomainSecretPrepare(virQEMUDriver *driver,
                             virDomainObj *vm)
     ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2);
 
-int qemuDomainDeviceDiskDefPostParse(virDomainDiskDef *disk,
-                                     unsigned int parseFlags);
+void
+qemuDomainChrDefDropDefaultPath(virDomainChrDef *chr,
+                                virQEMUDriver *driver);
+
+int
+qemuDomainNVDimmAlignSizePseries(virDomainMemoryDef *mem);
 
 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
+ * <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+
+#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 @qemuCaps
+ * is NULL this function may return NULL if the default model depends on the
+ * 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 were
+     * 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 == VIR_DOMAIN_NET_TYPE_VDPA &&
+        !virDomainNetGetModelString(net)) {
+        net->model = VIR_DOMAIN_NET_MODEL_VIRTIO;
+    } else if (net->type != VIR_DOMAIN_NET_TYPE_HOSTDEV &&
+        !virDomainNetGetModelString(net) &&
+        virDomainNetResolveActualType(net) != VIR_DOMAIN_NET_TYPE_HOSTDEV) {
+        net->model = qemuDomainDefaultNetModel(def, qemuCaps);
+    }
+
+    if (net->type == VIR_DOMAIN_NET_TYPE_USER &&
+        net->backend.type == VIR_DOMAIN_NET_BACKEND_DEFAULT) {
+        virDomainCapsDeviceNet netCaps = { };
+
+        virQEMUCapsFillDomainDeviceNetCaps(qemuCaps, &netCaps);
+
+        if (!VIR_DOMAIN_CAPS_ENUM_IS_SET(netCaps.backendType, VIR_DOMAIN_NET_BACKEND_DEFAULT) &&
+            VIR_DOMAIN_CAPS_ENUM_IS_SET(netCaps.backendType, VIR_DOMAIN_NET_BACKEND_PASST)) {
+            net->backend.type = 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 = QEMU_DOMAIN_STORAGE_SOURCE_PRIVATE(disk->src);
+    bool restoreAuthSecret = false;
+    bool restoreEncSecret = false;
+    g_autofree char *authalias = NULL;
+    g_autofree char *encalias = 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 authentication
+         * using secret object at the time we did not format the alias into the
+         * status XML */
+        if (virStorageSourceGetActualType(disk->src) == VIR_STORAGE_TYPE_NETWORK &&
+            (disk->src->protocol == VIR_STORAGE_NET_PROTOCOL_RBD ||
+             disk->src->protocol == VIR_STORAGE_NET_PROTOCOL_ISCSI))
+            restoreAuthSecret = true;
+    }
+
+    /* disk encryption secret */
+    if (disk->src->encryption &&
+        disk->src->encryption->format == VIR_STORAGE_ENCRYPTION_FORMAT_LUKS &&
+        (!priv || !priv->encinfo))
+        restoreEncSecret = true;
+
+    if (!restoreAuthSecret && !restoreEncSecret)
+        return 0;
+
+    if (!priv) {
+        if (!(disk->src->privateData = qemuDomainStorageSourcePrivateNew()))
+            return -1;
+
+        priv = QEMU_DOMAIN_STORAGE_SOURCE_PRIVATE(disk->src);
+    }
+
+    if (restoreAuthSecret) {
+        authalias = g_strdup_printf("%s-secret0", disk->info.alias);
+
+        if (qemuStorageSourcePrivateDataAssignSecinfo(&priv->secinfo, &authalias) < 0)
+            return -1;
+    }
+
+    if (restoreEncSecret) {
+        if (!priv->encinfo) {
+            priv->enccount = 1;
+            priv->encinfo = g_new0(qemuDomainSecretInfo *, 1);
+        }
+
+        encalias = 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) == VIR_STORAGE_FILE_NONE &&
+        virDomainDiskGetType(disk) != VIR_STORAGE_TYPE_VOLUME)
+        virDomainDiskSetFormat(disk, VIR_STORAGE_FILE_RAW);
+
+    /* default disk format for mirrored drive */
+    if (disk->mirror &&
+        disk->mirror->format == VIR_STORAGE_FILE_NONE)
+        disk->mirror->format = VIR_STORAGE_FILE_RAW;
+
+    /* default disk encryption engine */
+    for (n = disk->src; virStorageSourceIsBacking(n); n = n->backingStore) {
+        if (n->encryption && n->encryption->engine == VIR_STORAGE_ENCRYPTION_ENGINE_DEFAULT)
+            n->encryption->engine = 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 == VIR_TRISTATE_BOOL_YES &&
+        !disk->src->tlsAlias &&
+        !(disk->src->tlsAlias = qemuAliasTLSObjFromSrcAlias(disk->info.alias)))
+        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 == VIR_DOMAIN_VIDEO_TYPE_DEFAULT)
+        video->type = qemuDomainDefaultVideoDevice(def, qemuCaps);
+
+    if (video->type == VIR_DOMAIN_VIDEO_TYPE_QXL &&
+        !video->vgamem) {
+        video->vgamem = QEMU_QXL_VGAMEM_DEFAULT;
+    }
+
+    return 0;
+}
+
+
+static int
+qemuDomainDevicePanicDefPostParse(virDomainPanicDef *panic,
+                                  const virDomainDef *def)
+{
+    if (panic->model == VIR_DOMAIN_PANIC_MODEL_DEFAULT)
+        panic->model = 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 = qemuDomainGetSCSIControllerModel(def, cont, qemuCaps);
+
+        if (cont->model < 0)
+            return -1;
+        break;
+
+    case VIR_DOMAIN_CONTROLLER_TYPE_USB:
+        if (cont->model == 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 back to
+             * 'pci-ohci' otherwise which is the default for non-x86 machines
+             * which honour -usb */
+            if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_PIIX3_USB_UHCI))
+                cont->model = VIR_DOMAIN_CONTROLLER_MODEL_USB_PIIX3_UHCI;
+            else if (!ARCH_IS_X86(def->os.arch) &&
+                     virQEMUCapsGet(qemuCaps, QEMU_CAPS_PCI_OHCI))
+                cont->model = VIR_DOMAIN_CONTROLLER_MODEL_USB_PCI_OHCI;
+
+            if (ARCH_IS_S390(def->os.arch)) {
+                if (cont->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) {
+                    /* set the default USB model to none for s390 unless an
+                     * address is found */
+                    cont->model = VIR_DOMAIN_CONTROLLER_MODEL_USB_NONE;
+                }
+            } else if (ARCH_IS_PPC64(def->os.arch)) {
+                /* To not break migration we need to set default USB controller
+                 * for ppc64 to pci-ohci if we cannot change ABI of the VM.
+                 * The nec-usb-xhci or qemu-xhci controller is used as default
+                 * only for newly defined domains or devices. */
+                if ((parseFlags & VIR_DOMAIN_DEF_PARSE_ABI_UPDATE) &&
+                    virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_QEMU_XHCI)) {
+                    cont->model = VIR_DOMAIN_CONTROLLER_MODEL_USB_QEMU_XHCI;
+                } else if ((parseFlags & VIR_DOMAIN_DEF_PARSE_ABI_UPDATE) &&
+                    virQEMUCapsGet(qemuCaps, QEMU_CAPS_NEC_USB_XHCI)) {
+                    cont->model = VIR_DOMAIN_CONTROLLER_MODEL_USB_NEC_XHCI;
+                } else if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_PCI_OHCI)) {
+                    cont->model = VIR_DOMAIN_CONTROLLER_MODEL_USB_PCI_OHCI;
+                } else {
+                    /* Explicitly fallback to legacy USB controller for PPC64. */
+                    cont->model = VIR_DOMAIN_CONTROLLER_MODEL_USB_DEFAULT;
+                }
+            } else if (def->os.arch == VIR_ARCH_AARCH64) {
+                if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_QEMU_XHCI))
+                    cont->model = VIR_DOMAIN_CONTROLLER_MODEL_USB_QEMU_XHCI;
+                else if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_NEC_USB_XHCI))
+                    cont->model = VIR_DOMAIN_CONTROLLER_MODEL_USB_NEC_XHCI;
+            } else if (ARCH_IS_LOONGARCH(def->os.arch)) {
+                if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_QEMU_XHCI))
+                    cont->model = VIR_DOMAIN_CONTROLLER_MODEL_USB_QEMU_XHCI;
+            }
+        }
+        /* forbid usb model 'qusb1' and 'qusb2' in this kind of hyperviosr */
+        if (cont->model == VIR_DOMAIN_CONTROLLER_MODEL_USB_QUSB1 ||
+            cont->model == 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 == VIR_DOMAIN_CONTROLLER_MODEL_USB_NEC_XHCI ||
+             cont->model == 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->model),
+                           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 == VIR_DOMAIN_CONTROLLER_MODEL_PCI_ROOT ||
+             cont->model == VIR_DOMAIN_CONTROLLER_MODEL_PCIE_ROOT) &&
+            cont->idx != 0) {
+            virReportError(VIR_ERR_XML_ERROR, "%s",
+                           _("pci-root and pcie-root controllers should have index 0"));
+            return -1;
+        }
+
+        if (cont->model == VIR_DOMAIN_CONTROLLER_MODEL_PCI_EXPANDER_BUS &&
+            !qemuDomainIsI440FX(def)) {
+            virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+                           _("pci-expander-bus controllers are only supported on 440fx-based machinetypes"));
+            return -1;
+        }
+        if (cont->model == VIR_DOMAIN_CONTROLLER_MODEL_PCIE_EXPANDER_BUS &&
+            !(qemuDomainIsQ35(def) || qemuDomainIsARMVirt(def))) {
+            virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+                           _("pcie-expander-bus controllers are not supported 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
+         * <cpu><numa> array. NUMA cell id's in this array are numbered
+         * from 0 .. size-1.
+         */
+        if (cont->opts.pciopts.numaNode >= 0 &&
+            cont->opts.pciopts.numaNode >=
+            (int)virDomainNumaGetNodeCount(def->numa)) {
+            virReportError(VIR_ERR_XML_ERROR,
+                           _("%1$s with index %2$d is configured for a NUMA node (%3$d) not present in the domain's <cpu><numa> array (%4$zu)"),
+                           virDomainControllerModelPCITypeToString(cont->model),
+                           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 != VIR_DOMAIN_SHMEM_MODEL_IVSHMEM_DOORBELL && !shm->size)
+        shm->size = 4 << 20;
+
+    /* Nothing more to check/change for IVSHMEM */
+    if (shm->model == VIR_DOMAIN_SHMEM_MODEL_IVSHMEM)
+        return 0;
+
+    if (!shm->server.enabled) {
+        if (shm->model == VIR_DOMAIN_SHMEM_MODEL_IVSHMEM_DOORBELL) {
+            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                           _("shmem model '%1$s' is supported only with server 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 == VIR_DOMAIN_SHMEM_MODEL_IVSHMEM_PLAIN) {
+            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                           _("shmem model '%1$s' is supported only with server option disabled"),
+                           virDomainShmemModelTypeToString(shm->model));
+            return -1;
+        }
+
+        if (shm->size) {
+            virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+                           _("shmem model '%1$s' does not support size setting"),
+                           virDomainShmemModelTypeToString(shm->model));
+            return -1;
+        }
+        shm->msi.enabled = true;
+        if (!shm->msi.ioeventfd)
+            shm->msi.ioeventfd = 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 default
+     * will be picked below based on the architecture and machine type. */
+    if (chr->deviceType == VIR_DOMAIN_CHR_DEVICE_TYPE_SERIAL &&
+        chr->targetType == VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_ISA) {
+        chr->targetType = VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_NONE;
+    }
+
+    /* Set the default serial type */
+    if (chr->deviceType == VIR_DOMAIN_CHR_DEVICE_TYPE_SERIAL &&
+        chr->targetType == VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_NONE) {
+        if (ARCH_IS_X86(def->os.arch)) {
+            chr->targetType = VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_ISA;
+        } else if (qemuDomainIsPSeries(def)) {
+            chr->targetType = VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_SPAPR_VIO;
+        } else if (qemuDomainIsARMVirt(def) ||
+                   qemuDomainIsLoongArchVirt(def) ||
+                   qemuDomainIsRISCVVirt(def)) {
+            chr->targetType = VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_SYSTEM;
+        } else if (ARCH_IS_S390(def->os.arch)) {
+            chr->targetType = VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_SCLP;
+        }
+    }
+
+    /* Set the default target model */
+    if (chr->deviceType == VIR_DOMAIN_CHR_DEVICE_TYPE_SERIAL &&
+        chr->targetModel == VIR_DOMAIN_CHR_SERIAL_TARGET_MODEL_NONE) {
+        switch ((virDomainChrSerialTargetType)chr->targetType) {
+        case VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_ISA:
+            chr->targetModel = VIR_DOMAIN_CHR_SERIAL_TARGET_MODEL_ISA_SERIAL;
+            break;
+        case VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_USB:
+            chr->targetModel = VIR_DOMAIN_CHR_SERIAL_TARGET_MODEL_USB_SERIAL;
+            break;
+        case VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_PCI:
+            chr->targetModel = VIR_DOMAIN_CHR_SERIAL_TARGET_MODEL_PCI_SERIAL;
+            break;
+        case VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_SPAPR_VIO:
+            chr->targetModel = VIR_DOMAIN_CHR_SERIAL_TARGET_MODEL_SPAPR_VTY;
+            break;
+        case VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_SYSTEM:
+            if (qemuDomainIsARMVirt(def)) {
+                chr->targetModel = VIR_DOMAIN_CHR_SERIAL_TARGET_MODEL_PL011;
+            } else if (qemuDomainIsLoongArchVirt(def) ||
+                       qemuDomainIsRISCVVirt(def)) {
+                chr->targetModel = VIR_DOMAIN_CHR_SERIAL_TARGET_MODEL_16550A;
+            }
+            break;
+        case VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_SCLP:
+            chr->targetModel = VIR_DOMAIN_CHR_SERIAL_TARGET_MODEL_SCLPCONSOLE;
+            break;
+        case VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_ISA_DEBUG:
+            chr->targetModel = VIR_DOMAIN_CHR_SERIAL_TARGET_MODEL_ISA_DEBUGCON;
+            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 == VIR_DOMAIN_CHR_TYPE_UNIX &&
+            !chr->source->data.nix.path) {
+            chr->source->data.nix.listen = true;
+        }
+    }
+
+    return 0;
+}
+
+
+static int
+qemuDomainVsockDefPostParse(virDomainVsockDef *vsock)
+{
+    if (vsock->model == VIR_DOMAIN_VSOCK_MODEL_DEFAULT)
+        vsock->model = 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 *hostdev,
+                                                   unsigned int parseFlags)
+{
+    qemuDomainStorageSourcePrivate *priv;
+    virDomainHostdevSubsysSCSI *scsisrc = &hostdev->source.subsys.u.scsi;
+    virDomainHostdevSubsysSCSIiSCSI *iscsisrc = &scsisrc->u.iscsi;
+    g_autofree char *authalias = NULL;
+
+    if (!(parseFlags & VIR_DOMAIN_DEF_PARSE_STATUS))
+        return 0;
+
+    if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS ||
+        hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI ||
+        scsisrc->protocol != VIR_DOMAIN_HOSTDEV_SCSI_PROTOCOL_TYPE_ISCSI ||
+        !iscsisrc->src->auth)
+        return 0;
+
+    if (!(priv = qemuDomainStorageSourcePrivateFetch(iscsisrc->src)))
+        return -1;
+
+    if (priv->secinfo)
+        return 0;
+
+    authalias = g_strdup_printf("%s-secret0", hostdev->info->alias);
+
+    if (qemuStorageSourcePrivateDataAssignSecinfo(&priv->secinfo, &authalias) < 0)
+        return -1;
+
+    return 0;
+}
+
+
+/**
+ * qemuDomainDeviceHostdevDefPostParseRestoreBackendAlias:
+ *
+ * Re-generate backend alias if it wasn't stored in the status XML by an older
+ * libvirtd.
+ *
+ * Note that qemuCaps should be always present for a status XML.
+ */
+static int
+qemuDomainDeviceHostdevDefPostParseRestoreBackendAlias(virDomainHostdevDef *hostdev,
+                                                       unsigned int parseFlags)
+{
+    virDomainHostdevSubsysSCSI *scsisrc = &hostdev->source.subsys.u.scsi;
+    virStorageSource *src;
+
+    if (!(parseFlags & VIR_DOMAIN_DEF_PARSE_STATUS))
+        return 0;
+
+    if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS ||
+        hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI)
+        return 0;
+
+    switch (scsisrc->protocol) {
+    case VIR_DOMAIN_HOSTDEV_SCSI_PROTOCOL_TYPE_NONE:
+        if (!scsisrc->u.host.src)
+            scsisrc->u.host.src = virStorageSourceNew();
+
+        src = scsisrc->u.host.src;
+        break;
+
+    case VIR_DOMAIN_HOSTDEV_SCSI_PROTOCOL_TYPE_ISCSI:
+        src = 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("libvirt-%s-backend", hostdev->info->alias));
+
+    return 0;
+}
+
+
+static int
+qemuDomainHostdevDefMdevPostParse(virDomainHostdevSubsysMediatedDev *mdevsrc)
+{
+    /* QEMU 2.12 added support for vfio-pci display type, we default to
+     * 'display=off' to stay safe from future changes */
+    if (mdevsrc->model == VIR_MDEV_MODEL_TYPE_VFIO_PCI &&
+        mdevsrc->display == VIR_TRISTATE_SWITCH_ABSENT)
+        mdevsrc->display = VIR_TRISTATE_SWITCH_OFF;
+
+    return 0;
+}
+
+
+static int
+qemuDomainHostdevDefPostParse(virDomainHostdevDef *hostdev,
+                              unsigned int parseFlags)
+{
+    virDomainHostdevSubsys *subsys = &hostdev->source.subsys;
+
+    if (qemuDomainDeviceHostdevDefPostParseRestoreSecAlias(hostdev, parseFlags) < 0)
+        return -1;
+
+    if (qemuDomainDeviceHostdevDefPostParseRestoreBackendAlias(hostdev, parseFlags) < 0)
+        return -1;
+
+    if (hostdev->mode == VIR_DOMAIN_HOSTDEV_MODE_SUBSYS &&
+        hostdev->source.subsys.type == 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 == VIR_DOMAIN_TPM_MODEL_DEFAULT) {
+        if (ARCH_IS_PPC64(def->os.arch))
+            tpm->model = VIR_DOMAIN_TPM_MODEL_SPAPR;
+        else
+            tpm->model = VIR_DOMAIN_TPM_MODEL_TIS;
+    }
+
+    /* TPM 1.2 and 2 are not compatible, so we choose a specific version here */
+    if (tpm->type == VIR_DOMAIN_TPM_TYPE_EMULATOR &&
+        tpm->data.emulator.version == 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 == VIR_DOMAIN_TPM_MODEL_TIS &&
+            ARCH_IS_X86(def->os.arch)) {
+            tpm->data.emulator.version = VIR_DOMAIN_TPM_VERSION_1_2;
+        } else {
+            tpm->data.emulator.version = 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 = 256 * 1024;
+
+            if (mem->model == VIR_DOMAIN_MEMORY_MODEL_NVDIMM) {
+                if (qemuDomainNVDimmAlignSizePseries(mem) < 0)
+                    return -1;
+            } else {
+                mem->size = VIR_ROUND_UP(mem->size, ppc64MemModuleAlign);
+            }
+        }
+    }
+
+    return 0;
+}
+
+
+static int
+qemuDomainPstoreDefPostParse(virDomainPstoreDef *pstore,
+                             const virDomainDef *def,
+                             virQEMUDriver *driver)
+{
+    g_autoptr(virQEMUDriverConfig) cfg = virQEMUDriverGetConfig(driver);
+
+    switch (pstore->backend) {
+    case VIR_DOMAIN_PSTORE_BACKEND_ACPI_ERST:
+        if (!pstore->path)
+            pstore->path = 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 then
+     * 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 == VIR_DOMAIN_IOMMU_MODEL_INTEL) {
+
+        /* eim requires intremap. */
+        if (iommu->intremap == VIR_TRISTATE_SWITCH_ABSENT &&
+            virQEMUCapsGet(qemuCaps, QEMU_CAPS_INTEL_IOMMU_INTREMAP)) {
+            iommu->intremap = VIR_TRISTATE_SWITCH_ON;
+        }
+
+        if (iommu->eim == VIR_TRISTATE_SWITCH_ABSENT &&
+            virQEMUCapsGet(qemuCaps, QEMU_CAPS_INTEL_IOMMU_EIM)) {
+            iommu->eim = VIR_TRISTATE_SWITCH_ON;
+        }
+    }
+
+    return 0;
+}
+
+
+int
+qemuDomainDeviceDefPostParse(virDomainDeviceDef *dev,
+                             const virDomainDef *def,
+                             unsigned int parseFlags,
+                             void *opaque,
+                             void *parseOpaque)
+{
+    virQEMUDriver *driver = 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 startup
+     * with the capabilities populated. */
+    virQEMUCaps *qemuCaps = parseOpaque;
+    int ret = -1;
+
+    switch (dev->type) {
+    case VIR_DOMAIN_DEVICE_NET:
+        ret = qemuDomainDeviceNetDefPostParse(dev->data.net, def, qemuCaps);
+        break;
+
+    case VIR_DOMAIN_DEVICE_DISK:
+        ret = qemuDomainDeviceDiskDefPostParse(dev->data.disk, parseFlags);
+        break;
+
+    case VIR_DOMAIN_DEVICE_VIDEO:
+        ret = qemuDomainDeviceVideoDefPostParse(dev->data.video, def, qemuCaps);
+        break;
+
+    case VIR_DOMAIN_DEVICE_PANIC:
+        ret = qemuDomainDevicePanicDefPostParse(dev->data.panic, def);
+        break;
+
+    case VIR_DOMAIN_DEVICE_CONTROLLER:
+        ret = qemuDomainControllerDefPostParse(dev->data.controller, def,
+                                               qemuCaps, parseFlags);
+        break;
+
+    case VIR_DOMAIN_DEVICE_SHMEM:
+        ret = qemuDomainShmemDefPostParse(dev->data.shmem);
+        break;
+
+    case VIR_DOMAIN_DEVICE_CHR:
+        ret = qemuDomainChrDefPostParse(dev->data.chr, def, driver, parseFlags);
+        break;
+
+    case VIR_DOMAIN_DEVICE_VSOCK:
+        ret = qemuDomainVsockDefPostParse(dev->data.vsock);
+        break;
+
+    case VIR_DOMAIN_DEVICE_HOSTDEV:
+        ret = qemuDomainHostdevDefPostParse(dev->data.hostdev, parseFlags);
+        break;
+
+    case VIR_DOMAIN_DEVICE_TPM:
+        ret = qemuDomainTPMDefPostParse(dev->data.tpm, def);
+        break;
+
+    case VIR_DOMAIN_DEVICE_MEMORY:
+        ret = qemuDomainMemoryDefPostParse(dev->data.memory, def->os.arch,
+                                           parseFlags);
+        break;
+
+    case VIR_DOMAIN_DEVICE_PSTORE:
+        ret = qemuDomainPstoreDefPostParse(dev->data.pstore, def, driver);
+        break;
+
+    case VIR_DOMAIN_DEVICE_IOMMU:
+        ret = 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 = 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 = opaque;
+
+    /* check for emulator and create a default one if needed */
+    if (!def->emulator) {
+        if (!(def->emulator = 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 = virQEMUCapsGetCanonicalMachine(qemuCaps, def->virtType,
+                                                 def->os.machine)))
+        return 0;
+
+    if (STRNEQ(canon, def->os.machine)) {
+        char *tmp;
+        tmp = g_strdup(canon);
+        VIR_FREE(def->os.machine);
+        def->os.machine = tmp;
+    }
+
+    return 0;
+}
+
+
+static int
+qemuDomainDefMachinePostParse(virDomainDef *def,
+                              virQEMUCaps *qemuCaps)
+{
+    if (!def->os.machine) {
+        const char *machine = virQEMUCapsGetPreferredMachine(qemuCaps,
+                                                             def->virtType);
+        if (!machine) {
+            virReportError(VIR_ERR_INVALID_ARG,
+                           _("could not get preferred machine for %1$s type=%2$s"),
+                           def->emulator,
+                           virDomainVirtTypeToString(def->virtType));
+            return -1;
+        }
+
+        def->os.machine = 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 supported 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] != VIR_TRISTATE_SWITCH_ON)
+        return;
+
+    /* Strip the <acpi/> feature only for non-fresh configs, in order to still
+     * 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-intuitive,
+     * 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
+     * <acpi/> despite being ignored.
+     */
+    if (def->os.arch != VIR_ARCH_S390 &&
+        def->os.arch != VIR_ARCH_S390X)
+        return;
+
+    /* To be sure, we only strip ACPI if given machine type doesn't support it */
+    if (virQEMUCapsMachineSupportsACPI(qemuCaps, def->virtType, def->os.machine) != VIR_TRISTATE_BOOL_NO)
+        return;
+
+    def->features[VIR_DOMAIN_FEATURE_ACPI] = VIR_TRISTATE_SWITCH_ABSENT;
+}
+
+
+static int
+qemuDomainDefBootPostParse(virDomainDef *def,
+                           virQEMUDriver *driver,
+                           unsigned int parseFlags)
+{
+    bool abiUpdate = !!(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 = virDomainLoaderDefNew();
+        if (!def->os.loader->format)
+            def->os.loader->format = 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] != 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 != 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 != VIR_DOMAIN_VIRT_QEMU)
+        return 0;
+
+    model = virQEMUCapsGetMachineDefaultCPU(qemuCaps, def->os.machine, def->virtType);
+    if (!model) {
+        VIR_DEBUG("Unknown default CPU model for domain '%s'", def->name);
+        return 0;
+    }
+
+    if (STREQ(model, "host") && def->virtType != 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 = virCPUDefNew();
+
+    def->cpu->type = VIR_CPU_TYPE_GUEST;
+
+    if (STREQ(model, "host")) {
+        if (ARCH_IS_S390(def->os.arch) &&
+            virQEMUCapsIsCPUModeSupported(qemuCaps, hostarch, def->virtType,
+                                          VIR_CPU_MODE_HOST_MODEL,
+                                          def->os.machine)) {
+            def->cpu->mode = VIR_CPU_MODE_HOST_MODEL;
+        } else {
+            def->cpu->mode = 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 any
+         * host. QEMU will just disable the unavailable features and we will
+         * update the CPU definition accordingly and set check to FULL when
+         * starting the domain. */
+        def->cpu->check = VIR_CPU_CHECK_NONE;
+        def->cpu->mode = VIR_CPU_MODE_CUSTOM;
+        def->cpu->match = VIR_CPU_MATCH_EXACT;
+        def->cpu->fallback = VIR_CPU_FALLBACK_FORBID;
+        def->cpu->model = 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 = false;
+    int usbModel = -1; /* "default for machinetype" */
+    int pciRoot;       /* index within def->controllers */
+    bool addImplicitSATA = false;
+    bool addPCIRoot = false;
+    bool addPCIeRoot = false;
+    bool addDefaultMemballoon = false;
+    bool addDefaultUSBKBD = false;
+    bool addDefaultUSBMouse = false;
+    bool addPanicDevice = false;
+    bool addITCOWatchdog = false;
+    bool addIOMMU = 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 = true;
+
+        if (STREQ(def->os.machine, "isapc")) {
+            break;
+        }
+
+        addDefaultUSB = true;
+
+        if (qemuDomainIsQ35(def)) {
+            addPCIeRoot = true;
+            addImplicitSATA = true;
+            addITCOWatchdog = true;
+
+            if (virDomainDefGetVcpusMax(def) > QEMU_MAX_VCPUS_WITHOUT_EIM) {
+                addIOMMU = 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 = VIR_DOMAIN_CONTROLLER_MODEL_USB_QEMU_XHCI;
+            else if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_NEC_USB_XHCI))
+                usbModel = VIR_DOMAIN_CONTROLLER_MODEL_USB_NEC_XHCI;
+            else if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_ICH9_USB_EHCI1))
+                usbModel = VIR_DOMAIN_CONTROLLER_MODEL_USB_ICH9_EHCI1;
+            else
+                addDefaultUSB = false;
+            break;
+        }
+        if (qemuDomainIsI440FX(def))
+            addPCIRoot = 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 = 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 = true;
+            usbModel = VIR_DOMAIN_CONTROLLER_MODEL_USB_PCI_OHCI;
+        }
+
+        if (qemuDomainIsARMVirt(def))
+            addPCIeRoot = true;
+
+        break;
+
+    case VIR_ARCH_PPC64:
+    case VIR_ARCH_PPC64LE:
+        addPCIRoot = true;
+        addDefaultUSB = true;
+        addDefaultUSBKBD = true;
+        addDefaultUSBMouse = true;
+        addDefaultMemballoon = 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 = true;
+        break;
+
+    case VIR_ARCH_ALPHA:
+    case VIR_ARCH_PPC:
+    case VIR_ARCH_PPCEMB:
+    case VIR_ARCH_SH4:
+    case VIR_ARCH_SH4EB:
+        addDefaultUSB = true;
+        addDefaultMemballoon = true;
+        addPCIRoot = true;
+        break;
+
+    case VIR_ARCH_RISCV32:
+    case VIR_ARCH_RISCV64:
+        addDefaultMemballoon = true;
+        if (qemuDomainIsRISCVVirt(def))
+            addPCIeRoot = true;
+        break;
+
+    case VIR_ARCH_S390:
+    case VIR_ARCH_S390X:
+        addDefaultMemballoon = true;
+        addPanicDevice = true;
+        addPCIRoot = virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_ZPCI);
+        break;
+
+    case VIR_ARCH_SPARC64:
+        addDefaultUSB = true;
+        addDefaultMemballoon = true;
+        addPCIRoot = true;
+        break;
+
+    case VIR_ARCH_MIPS:
+    case VIR_ARCH_MIPSEL:
+    case VIR_ARCH_MIPS64:
+    case VIR_ARCH_MIPS64EL:
+        addDefaultUSB = true;
+        addDefaultMemballoon = true;
+        if (qemuDomainIsMipsMalta(def))
+            addPCIRoot = true;
+        break;
+
+    case VIR_ARCH_LOONGARCH64:
+        addPCIeRoot = 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 = virDomainControllerFind(def, VIR_DOMAIN_CONTROLLER_TYPE_PCI, 0);
+
+    /* NB: any machine that sets addPCIRoot to true must also return
+     * true from the function qemuDomainSupportsPCI().
+     */
+    if (addPCIRoot) {
+        if (pciRoot >= 0) {
+            if (def->controllers[pciRoot]->model != VIR_DOMAIN_CONTROLLER_MODEL_PCI_ROOT) {
+                virReportError(VIR_ERR_XML_ERROR,
+                               _("The PCI controller with index='0' must be model='pci-root' for this machine type, but model='%1$s' was found instead"),
+                               virDomainControllerModelPCITypeToString(def->controllers[pciRoot]->model));
+                return -1;
+            }
+        } else if (!virDomainDefAddController(def, VIR_DOMAIN_CONTROLLER_TYPE_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 >= 0) {
+            if (def->controllers[pciRoot]->model != VIR_DOMAIN_CONTROLLER_MODEL_PCIE_ROOT) {
+                virReportError(VIR_ERR_XML_ERROR,
+                               _("The PCI controller with index='0' must be model='pcie-root' for this machine type, but model='%1$s' was found instead"),
+                               virDomainControllerModelPCITypeToString(def->controllers[pciRoot]->model));
+                return -1;
+            }
+        } else if (!virDomainDefAddController(def, VIR_DOMAIN_CONTROLLER_TYPE_PCI, 0,
+                                             VIR_DOMAIN_CONTROLLER_MODEL_PCIE_ROOT)) {
+            return -1;
+        }
+    }
+
+    if (addDefaultMemballoon && !def->memballoon) {
+        virDomainMemballoonDef *memballoon;
+        memballoon = g_new0(virDomainMemballoonDef, 1);
+
+        memballoon->model = VIR_DOMAIN_MEMBALLOON_MODEL_VIRTIO;
+        def->memballoon = memballoon;
+    }
+
+    if (addDefaultUSBMouse) {
+        bool hasUSBTablet = false;
+        size_t j;
+
+        for (j = 0; j < def->ninputs; j++) {
+            if (def->inputs[j]->type == VIR_DOMAIN_INPUT_TYPE_TABLET &&
+                def->inputs[j]->bus == VIR_DOMAIN_INPUT_BUS_USB) {
+                hasUSBTablet = 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 = 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 = qemuDomainDefaultPanicModel(def);
+        size_t j;
+
+        for (j = 0; j < def->npanics; j++) {
+            if (def->panics[j]->model == VIR_DOMAIN_PANIC_MODEL_DEFAULT ||
+                def->panics[j]->model == defaultModel)
+                break;
+        }
+
+        if (j == def->npanics) {
+            virDomainPanicDef *panic = g_new0(virDomainPanicDef, 1);
+
+            VIR_APPEND_ELEMENT_COPY(def->panics, def->npanics, panic);
+        }
+    }
+
+    if (addITCOWatchdog) {
+        size_t i = 0;
+
+        for (i = 0; i < def->nwatchdogs; i++) {
+            if (def->watchdogs[i]->model == VIR_DOMAIN_WATCHDOG_MODEL_ITCO)
+                break;
+        }
+
+        if (i == def->nwatchdogs) {
+            virDomainWatchdogDef *watchdog = g_new0(virDomainWatchdogDef, 1);
+
+            watchdog->model = VIR_DOMAIN_WATCHDOG_MODEL_ITCO;
+            if (def->nwatchdogs)
+                watchdog->action = def->watchdogs[0]->action;
+            else
+                watchdog->action = 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 = NULL;
+
+        iommu = virDomainIOMMUDefNew();
+        iommu->model = VIR_DOMAIN_IOMMU_MODEL_INTEL;
+        /* eim requires intremap. */
+        iommu->intremap = VIR_TRISTATE_SWITCH_ON;
+        iommu->eim = VIR_TRISTATE_SWITCH_ON;
+
+        def->iommu = 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] == VIR_TRISTATE_SWITCH_ABSENT &&
+         qemuDomainIsARMVirt(def)) ||
+        (def->features[VIR_DOMAIN_FEATURE_GIC] == VIR_TRISTATE_SWITCH_ON &&
+         def->gic_version == VIR_GIC_VERSION_NONE)) {
+        virGICVersion version;
+
+        VIR_DEBUG("Looking for usable GIC version in domain capabilities");
+        for (version = 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 controller,
+             * 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, GICv2,
+             * which supports all the features we need.
+             *
+             * See https://bugzilla.redhat.com/show_bug.cgi?id=1414081 */
+            if (version == VIR_GIC_VERSION_3 &&
+                def->virtType == VIR_DOMAIN_VIRT_QEMU) {
+                continue;
+            }
+
+            if (virQEMUCapsSupportsGICVersion(qemuCaps,
+                                              def->virtType,
+                                              version)) {
+                VIR_DEBUG("Using GIC version %s",
+                          virGICVersionTypeToString(version));
+                def->gic_version = version;
+                break;
+            }
+        }
+
+        /* Use the default GIC version (GICv2) as a last-ditch attempt
+         * if no match could be found above */
+        if (def->gic_version == VIR_GIC_VERSION_NONE) {
+            VIR_DEBUG("Using GIC version 2 (default)");
+            def->gic_version = 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] = VIR_TRISTATE_SWITCH_ON;
+    }
+}
+
+
+static int
+qemuDomainRecheckInternalPaths(virDomainDef *def,
+                               virQEMUDriverConfig *cfg,
+                               unsigned int flags)
+{
+    size_t i = 0;
+    size_t j = 0;
+
+    for (i = 0; i < def->ngraphics; ++i) {
+        virDomainGraphicsDef *graphics = def->graphics[i];
+
+        for (j = 0; j < graphics->nListens; ++j) {
+            virDomainGraphicsListenDef *glisten =  &graphics->listens[j];
+
+            /* This will happen only if we parse XML from old libvirts where
+             * unix socket was available only for VNC graphics.  In this
+             * particular case we should follow the behavior and if we remove
+             * the auto-generated socket based on config option from qemu.conf
+             * we need to change the listen type to address. */
+            if (graphics->type == VIR_DOMAIN_GRAPHICS_TYPE_VNC &&
+                glisten->type == VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_SOCKET &&
+                glisten->socket &&
+                !glisten->autoGenerated &&
+                STRPREFIX(glisten->socket, cfg->libDir)) {
+                if (flags & VIR_DOMAIN_DEF_PARSE_INACTIVE) {
+                    VIR_FREE(glisten->socket);
+                    glisten->type = VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_ADDRESS;
+                } else {
+                    glisten->fromConfig = true;
+                }
+            }
+        }
+    }
+
+    return 0;
+}
+
+
+static int
+qemuDomainDefVcpusPostParse(virDomainDef *def)
+{
+    unsigned int maxvcpus = virDomainDefGetVcpusMax(def);
+    virDomainVcpuDef *vcpu;
+    virDomainVcpuDef *prevvcpu;
+    size_t i;
+    bool has_order = false;
+
+    /* vcpu 0 needs to be present, first, and non-hotpluggable */
+    vcpu = virDomainDefGetVcpu(def, 0);
+    if (!vcpu->online) {
+        virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+                       _("vcpu 0 can't be offline"));
+        return -1;
+    }
+    if (vcpu->hotpluggable == VIR_TRISTATE_BOOL_YES) {
+        virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+                       _("vcpu0 can't be hotpluggable"));
+        return -1;
+    }
+    if (vcpu->order != 0 && vcpu->order != 1) {
+        virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+                       _("vcpu0 must be enabled first"));
+        return -1;
+    }
+
+    if (vcpu->order != 0)
+        has_order = true;
+
+    prevvcpu = vcpu;
+
+    /* all online vcpus or non online vcpu need to have order set */
+    for (i = 1; i < maxvcpus; i++) {
+        vcpu = virDomainDefGetVcpu(def, i);
+
+        if (vcpu->online &&
+            (vcpu->order != 0) != has_order) {
+            virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+                           _("all vcpus must have either set or unset order"));
+            return -1;
+        }
+
+        /* few conditions for non-hotpluggable (thus online) vcpus */
+        if (vcpu->hotpluggable == VIR_TRISTATE_BOOL_NO) {
+            /* they can be ordered only at the beginning */
+            if (prevvcpu->hotpluggable == 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 yet).
+             * Also note that multiple vcpus may share order on some platforms */
+            if (prevvcpu->order > vcpu->order) {
+                virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+                               _("online non-hotpluggable vcpus must be ordered in ascending order"));
+                return -1;
+            }
+        }
+
+        prevvcpu = vcpu;
+    }
+
+    return 0;
+}
+
+
+static int
+qemuDomainDefCPUPostParse(virDomainDef *def,
+                          virQEMUCaps *qemuCaps)
+{
+    virCPUFeatureDef *sveFeature = NULL;
+    bool sveVectorLengthsProvided = false;
+    size_t i;
+
+    if (!def->cpu)
+        return 0;
+
+    for (i = 0; i < def->cpu->nfeatures; i++) {
+        virCPUFeatureDef *feature = &def->cpu->features[i];
+
+        if (STREQ(feature->name, "sve")) {
+            sveFeature = feature;
+        } else if (STRPREFIX(feature->name, "sve")) {
+            sveVectorLengthsProvided = true;
+        }
+    }
+
+    if (sveVectorLengthsProvided) {
+        if (sveFeature) {
+            if (sveFeature->policy == VIR_CPU_FEATURE_DISABLE ||
+                sveFeature->policy == VIR_CPU_FEATURE_FORBID) {
+                virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+                               _("SVE disabled, but SVE vector lengths provided"));
+                return -1;
+            } else {
+                sveFeature->policy = 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 = g_strdup("sve");
+            def->cpu->features[def->cpu->nfeatures].policy = VIR_CPU_FEATURE_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 == -1 &&
+        qemuCaps &&
+        def->cpu->mode == VIR_CPU_MODE_HOST_PASSTHROUGH &&
+        def->cpu->migratable == VIR_TRISTATE_SWITCH_ABSENT) {
+        if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_CPU_MIGRATABLE))
+            def->cpu->migratable = VIR_TRISTATE_SWITCH_ON;
+        else if (ARCH_IS_X86(def->os.arch))
+            def->cpu->migratable = VIR_TRISTATE_SWITCH_OFF;
+    }
+
+    /* Nothing to be done if only CPU topology is specified. */
+    if (def->cpu->mode == VIR_CPU_MODE_CUSTOM &&
+        !def->cpu->model)
+        return 0;
+
+    if (def->cpu->check != 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 = VIR_CPU_CHECK_NONE;
+        break;
+
+    case VIR_CPU_MODE_HOST_MODEL:
+        def->cpu->check = 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 == VIR_DOMAIN_VIRT_QEMU)
+            def->cpu->check = VIR_CPU_CHECK_NONE;
+        else
+            def->cpu->check = 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] != 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 QEMU 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 = !!(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 = virDomainDefGetMemoryTotal(def);
+
+    if (!def->numa)
+        def->numa = virDomainNumaNew();
+
+    virDomainNumaSetNodeCount(def->numa, 1);
+
+    for (i = 0; i < def->nmems; i++) {
+        virDomainMemoryDef *mem = def->mems[i];
+
+        if (mem->size > nodeMem) {
+            virReportError(VIR_ERR_XML_ERROR, "%s",
+                           _("Total size of memory devices exceeds the total memory size"));
+            return -1;
+        }
+
+        nodeMem -= 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 == -1)
+                mem->targetNode = 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 = opaque;
+    g_autoptr(virQEMUDriverConfig) cfg = virQEMUDriverGetConfig(driver);
+    virQEMUCaps *qemuCaps = 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 startup
+     * 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 = opaque;
+
+    if (!(*parseOpaque = virQEMUCapsCacheLookup(driver->qemuCapsCache,
+                                                def->emulator)))
+        return 1;
+
+    return 0;
+}
+
+
+void
+qemuDomainPostParseDataFree(void *parseOpaque)
+{
+    virQEMUCaps *qemuCaps = 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
+ * <http://www.gnu.org/licenses/>.
+ */
+
+#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"
 
 #define LIBVIRT_SNAPSHOT_CONF_PRIV_H_ALLOW
-- 
2.45.2
Re: [PATCH] qemu: Move PostParse functions out of qemu_domain.c
Posted by Martin Kletzander 1 week ago
On Wed, Nov 13, 2024 at 11:35:19AM +0100, Michal Privoznik wrote:
>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 <mprivozn@redhat.com>

Reviewed-by: Martin Kletzander <mkletzan@redhat.com>