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@xxxxxxxxxx> --- 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