[PATCH v2] ch: Enable callbacks for ch domain events
by Praveen K Paladugu
From: Praveen K Paladugu <prapal(a)linux.microsoft.com>
Enable callbacks for define, undefine, started, booted, stopped,
destroyed events of ch guests.
Signed-off-by: Praveen K Paladugu <praveenkpaladugu(a)gmail.com>
---
src/ch/ch_conf.h | 4 +++
src/ch/ch_driver.c | 82 ++++++++++++++++++++++++++++++++++++++++++++--
2 files changed, 84 insertions(+), 2 deletions(-)
diff --git a/src/ch/ch_conf.h b/src/ch/ch_conf.h
index a77cad7a2a..97c6c24aa5 100644
--- a/src/ch/ch_conf.h
+++ b/src/ch/ch_conf.h
@@ -24,6 +24,7 @@
#include "virthread.h"
#include "ch_capabilities.h"
#include "virebtables.h"
+#include "object_event.h"
#define CH_DRIVER_NAME "CH"
#define CH_CMD "cloud-hypervisor"
@@ -75,6 +76,9 @@ struct _virCHDriver
* then lockless thereafter */
virCHDriverConfig *config;
+ /* Immutable pointer, self-locking APIs */
+ virObjectEventState *domainEventState;
+
/* pid file FD, ensures two copies of the driver can't use the same root */
int lockFD;
diff --git a/src/ch/ch_driver.c b/src/ch/ch_driver.c
index dab025edc1..d18f266387 100644
--- a/src/ch/ch_driver.c
+++ b/src/ch/ch_driver.c
@@ -28,6 +28,7 @@
#include "ch_monitor.h"
#include "ch_process.h"
#include "domain_cgroup.h"
+#include "domain_event.h"
#include "datatypes.h"
#include "driver.h"
#include "viraccessapicheck.h"
@@ -263,6 +264,7 @@ chDomainCreateWithFlags(virDomainPtr dom, unsigned int flags)
virCHDriver *driver = dom->conn->privateData;
virDomainObj *vm;
virCHDomainObjPrivate *priv;
+ virObjectEvent *event;
g_autofree char *managed_save_path = NULL;
int ret = -1;
@@ -304,6 +306,14 @@ chDomainCreateWithFlags(virDomainPtr dom, unsigned int flags)
ret = virCHProcessStart(driver, vm, VIR_DOMAIN_RUNNING_BOOTED);
}
+ if (ret == 0) {
+ event = virDomainEventLifecycleNewFromObj(vm,
+ VIR_DOMAIN_EVENT_STARTED,
+ VIR_DOMAIN_EVENT_STARTED_BOOTED);
+ if (event)
+ virObjectEventStateQueue(driver->domainEventState, event);
+ }
+
endjob:
virDomainObjEndJob(vm);
@@ -323,8 +333,10 @@ chDomainDefineXMLFlags(virConnectPtr conn, const char *xml, unsigned int flags)
{
virCHDriver *driver = conn->privateData;
g_autoptr(virDomainDef) vmdef = NULL;
+ g_autoptr(virDomainDef) oldDef = NULL;
virDomainObj *vm = NULL;
virDomainPtr dom = NULL;
+ virObjectEvent *event = NULL;
g_autofree char *managed_save_path = NULL;
unsigned int parse_flags = VIR_DOMAIN_DEF_PARSE_INACTIVE;
@@ -345,7 +357,7 @@ chDomainDefineXMLFlags(virConnectPtr conn, const char *xml, unsigned int flags)
if (!(vm = virDomainObjListAdd(driver->domains, &vmdef,
driver->xmlopt,
- 0, NULL)))
+ 0, &oldDef)))
goto cleanup;
/* cleanup if there's any stale managedsave dir */
@@ -358,11 +370,17 @@ chDomainDefineXMLFlags(virConnectPtr conn, const char *xml, unsigned int flags)
}
vm->persistent = 1;
-
+ event = virDomainEventLifecycleNewFromObj(vm,
+ VIR_DOMAIN_EVENT_DEFINED,
+ !oldDef ?
+ VIR_DOMAIN_EVENT_DEFINED_ADDED :
+ VIR_DOMAIN_EVENT_DEFINED_UPDATED);
dom = virGetDomain(conn, vm->def->name, vm->def->uuid, vm->def->id);
cleanup:
virDomainObjEndAPI(&vm);
+ virObjectEventStateQueue(driver->domainEventState, event);
+
return dom;
}
@@ -378,6 +396,7 @@ chDomainUndefineFlags(virDomainPtr dom,
{
virCHDriver *driver = dom->conn->privateData;
virDomainObj *vm;
+ virObjectEvent *event = NULL;
int ret = -1;
virCheckFlags(0, -1);
@@ -393,6 +412,9 @@ chDomainUndefineFlags(virDomainPtr dom,
"%s", _("Cannot undefine transient domain"));
goto cleanup;
}
+ event = virDomainEventLifecycleNewFromObj(vm,
+ VIR_DOMAIN_EVENT_UNDEFINED,
+ VIR_DOMAIN_EVENT_UNDEFINED_REMOVED);
vm->persistent = 0;
if (!virDomainObjIsActive(vm)) {
@@ -403,6 +425,8 @@ chDomainUndefineFlags(virDomainPtr dom,
cleanup:
virDomainObjEndAPI(&vm);
+ virObjectEventStateQueue(driver->domainEventState, event);
+
return ret;
}
@@ -643,6 +667,7 @@ chDomainDestroyFlags(virDomainPtr dom, unsigned int flags)
{
virCHDriver *driver = dom->conn->privateData;
virDomainObj *vm;
+ virObjectEvent *event = NULL;
int ret = -1;
virCheckFlags(0, -1);
@@ -662,6 +687,9 @@ chDomainDestroyFlags(virDomainPtr dom, unsigned int flags)
if (virCHProcessStop(driver, vm, VIR_DOMAIN_SHUTOFF_DESTROYED) < 0)
goto endjob;
+ event = virDomainEventLifecycleNewFromObj(vm,
+ VIR_DOMAIN_EVENT_STOPPED,
+ VIR_DOMAIN_EVENT_STOPPED_DESTROYED);
virCHDomainRemoveInactive(driver, vm);
ret = 0;
@@ -670,6 +698,8 @@ chDomainDestroyFlags(virDomainPtr dom, unsigned int flags)
cleanup:
virDomainObjEndAPI(&vm);
+ virObjectEventStateQueue(driver->domainEventState, event);
+
return ret;
}
@@ -1365,6 +1395,7 @@ static int chStateCleanup(void)
virObjectUnref(ch_driver->xmlopt);
virObjectUnref(ch_driver->caps);
virObjectUnref(ch_driver->domains);
+ virObjectUnref(ch_driver->domainEventState);
virMutexDestroy(&ch_driver->lock);
g_clear_pointer(&ch_driver, g_free);
@@ -1414,6 +1445,9 @@ chStateInitialize(bool privileged,
if (!(ch_driver->config = virCHDriverConfigNew(privileged)))
goto cleanup;
+ if (!(ch_driver->domainEventState = virObjectEventStateNew()))
+ goto cleanup;
+
if ((rv = chExtractVersion(ch_driver)) < 0) {
if (rv == -2)
ret = VIR_DRV_STATE_INIT_SKIPPED;
@@ -2205,6 +2239,48 @@ chDomainSetNumaParameters(virDomainPtr dom,
return ret;
}
+static int
+chConnectDomainEventRegisterAny(virConnectPtr conn,
+ virDomainPtr dom,
+ int eventID,
+ virConnectDomainEventGenericCallback callback,
+ void *opaque,
+ virFreeCallback freecb)
+{
+ virCHDriver *driver = conn->privateData;
+ int ret = -1;
+
+ if (virConnectDomainEventRegisterAnyEnsureACL(conn) < 0)
+ return -1;
+
+ if (virDomainEventStateRegisterID(conn,
+ driver->domainEventState,
+ dom, eventID,
+ callback, opaque, freecb, &ret) < 0)
+ ret = -1;
+
+ return ret;
+}
+
+
+static int
+chConnectDomainEventDeregisterAny(virConnectPtr conn,
+ int callbackID)
+{
+ virCHDriver *driver = conn->privateData;
+
+ if (virConnectDomainEventDeregisterAnyEnsureACL(conn) < 0)
+ return -1;
+
+ if (virObjectEventStateDeregisterID(conn,
+ driver->domainEventState,
+ callbackID, true) < 0)
+ return -1;
+
+ return 0;
+}
+
+
/* Function Tables */
static virHypervisorDriver chHypervisorDriver = {
.name = "CH",
@@ -2262,6 +2338,8 @@ static virHypervisorDriver chHypervisorDriver = {
.domainHasManagedSaveImage = chDomainHasManagedSaveImage, /* 10.2.0 */
.domainRestore = chDomainRestore, /* 10.2.0 */
.domainRestoreFlags = chDomainRestoreFlags, /* 10.2.0 */
+ .connectDomainEventRegisterAny = chConnectDomainEventRegisterAny, /* 10.8.0 */
+ .connectDomainEventDeregisterAny = chConnectDomainEventDeregisterAny, /* 10.8.0 */
};
static virConnectDriver chConnectDriver = {
--
2.44.0
1 month, 1 week
[PATCH] ch: enable virNodeGetMemoryStats API
by Praveen K Paladugu
Enable virNodeGetMemoryStats API to return the stats of host memory.
Signed-off-by: Praveen K Paladugu <prapal(a)linux.microsoft.com>
Signed-off-by: Praveen K Paladugu <praveenkpaladugu(a)gmail.com>
---
src/ch/ch_driver.c | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/src/ch/ch_driver.c b/src/ch/ch_driver.c
index 17ae488a02..34f3a495fc 100644
--- a/src/ch/ch_driver.c
+++ b/src/ch/ch_driver.c
@@ -40,6 +40,7 @@
#include "virutil.h"
#include "viruuid.h"
#include "virnuma.h"
+#include "virhostmem.h"
#define VIR_FROM_THIS VIR_FROM_CH
@@ -2209,6 +2210,19 @@ chDomainSetNumaParameters(virDomainPtr dom,
return ret;
}
+static int
+chNodeGetMemoryStats(virConnectPtr conn,
+ int cellNum,
+ virNodeMemoryStatsPtr params,
+ int *nparams,
+ unsigned int flags)
+{
+ if (virNodeGetMemoryStatsEnsureACL(conn) < 0)
+ return -1;
+
+ return virHostMemGetStats(cellNum, params, nparams, flags);
+}
+
/* Function Tables */
static virHypervisorDriver chHypervisorDriver = {
.name = "CH",
@@ -2266,6 +2280,7 @@ static virHypervisorDriver chHypervisorDriver = {
.domainHasManagedSaveImage = chDomainHasManagedSaveImage, /* 10.2.0 */
.domainRestore = chDomainRestore, /* 10.2.0 */
.domainRestoreFlags = chDomainRestoreFlags, /* 10.2.0 */
+ .nodeGetMemoryStats = chNodeGetMemoryStats, /* 10.10.0 */
};
static virConnectDriver chConnectDriver = {
--
2.47.0
1 month, 1 week
[PATCH 00/10] PCI passthrough support for ch guests
by Praveen K Paladugu
This patch series introduces PCI passthrough support for ch guests. While
enabling this feature I refactored a bunch of methods from qemu to hypervisor
to reduce duplication of logic between the drivers.
Praveen K Paladugu (7):
hypervisor: move HostdevNeedsVFIO to hypervisor
hypervisor: move HostdevHostSupportsPassthroughVFIO
qemu: replace qemuHostdevPreparePCIDevices
ch: prepare domain definition for pci passthrough
ch: allow hostdev in domain definitions
ch: reattach PCI devices to host while stopping guest
ch: explicitly set INFILESIZE to 0
Wei Liu (3):
ch: add host device manager to driver
ch: add scaffolding for host devices management
ch: prepare host for PCI passthrough
po/POTFILES | 1 +
src/ch/ch_conf.h | 4 ++
src/ch/ch_domain.c | 2 +-
src/ch/ch_driver.c | 4 ++
src/ch/ch_hostdev.c | 115 +++++++++++++++++++++++++++++++++++
src/ch/ch_hostdev.h | 32 ++++++++++
src/ch/ch_monitor.c | 1 +
src/ch/ch_process.c | 74 +++++++++++++++++++++-
src/ch/meson.build | 2 +
src/hypervisor/virhostdev.c | 23 +++++++
src/hypervisor/virhostdev.h | 5 ++
src/libvirt_private.syms | 2 +
src/qemu/qemu_capabilities.c | 2 +-
src/qemu/qemu_cgroup.c | 5 +-
src/qemu/qemu_domain.c | 2 +-
src/qemu/qemu_driver.c | 2 +-
src/qemu/qemu_hostdev.c | 40 +-----------
src/qemu/qemu_hostdev.h | 10 ---
src/qemu/qemu_hotplug.c | 5 +-
src/qemu/qemu_namespace.c | 2 +-
tests/domaincapstest.c | 2 +-
21 files changed, 276 insertions(+), 59 deletions(-)
create mode 100644 src/ch/ch_hostdev.c
create mode 100644 src/ch/ch_hostdev.h
--
2.44.0
1 month, 1 week
[PATCH 00/19] hw/microblaze: Allow running cross-endian vCPUs
by Philippe Mathieu-Daudé
Make machines endianness-agnostic, allowing to run a big-endian vCPU
on the little-endian 'qemu-system-microblazeel' binary, and a little
endian one on the big-endian 'qemu-system-microblaze' binary.
Tests added, following combinations covered:
- little-endian vCPU using little-endian binary (in-tree)
- little-endian vCPU using big-endian binary (new)
- big-endian vCPU using little-endian binary (new)
- big-endian vCPU using big-endian binary (in-tree)
Deprecate untested big-endian machines, likely build on the big
endian binary by mistake:
- petalogix-ml605
- xlnx-zynqmp-pmu
To make a target endian-agnostic we need to remove the MO_TE uses.
In order to do that, we propagate the MemOp from earlier in the
call stack, or we extract it from the vCPU env (on MicroBlaze the
CPU endianness is exposed by the 'ENDI' bit).
Note, since vCPU can run in any endianness, the
MemoryRegionOps::endianness should not be DEVICE_NATIVE_ENDIAN
anymore, because this definition expand to the binary endianness,
swapping data regardless how the vcpu access it.
See adjust_endianness() -> devend_memop(). Something to keep in
mind, possibly requiring further work and optimizations (avoid
double-swap).
Next step: Look at unifying binaries.
Please review,
Phil.
Philippe Mathieu-Daudé (19):
target/microblaze: Rename CPU endianness property as 'little-endian'
hw/microblaze: Deprecate big-endian petalogix-ml605 & xlnx-zynqmp-pmu
hw/microblaze/s3adsp1800: Explicit CPU endianness
hw/microblaze/s3adsp1800: Rename unimplemented MMIO region as xps_gpio
hw/microblaze/s3adsp1800: Declare machine type using DEFINE_TYPES
macro
hw/microblaze: Fix MemoryRegionOps coding style
hw/microblaze: Restrict MemoryRegionOps are implemented as 32-bit
hw/microblaze: Propagate CPU endianness to microblaze_load_kernel()
hw/intc/xilinx_intc: Only expect big-endian accesses
hw/timer/xilinx_timer: Only expect big-endian accesses
hw/timer/xilinx_timer: Allow down to 8-bit memory access
hw/net/xilinx_ethlite: Only expect big-endian accesses
target/microblaze: Explode MO_TExx -> MO_TE | MO_xx
target/microblaze: Set MO_TE once in do_load() / do_store()
target/microblaze: Introduce mo_endian() helper
target/microblaze: Consider endianness while translating code
hw/microblaze: Support various endianness for s3adsp1800 machines
tests/functional: Explicit endianness of microblaze assets
tests/functional: Add microblaze cross-endianness tests
docs/about/deprecated.rst | 6 ++
.../devices/microblaze-softmmu/default.mak | 2 -
.../devices/microblazeel-softmmu/default.mak | 5 +-
hw/microblaze/boot.h | 4 +-
target/microblaze/cpu.h | 7 ++
hw/char/xilinx_uartlite.c | 8 ++-
hw/intc/xilinx_intc.c | 23 +++++--
hw/microblaze/boot.c | 8 +--
hw/microblaze/petalogix_ml605_mmu.c | 11 ++-
hw/microblaze/petalogix_s3adsp1800_mmu.c | 67 +++++++++++++++++--
hw/microblaze/xlnx-zynqmp-pmu.c | 12 ++--
hw/net/xilinx_ethlite.c | 28 ++++++--
hw/timer/xilinx_timer.c | 15 +++--
target/microblaze/cpu.c | 2 +-
target/microblaze/translate.c | 49 ++++++++------
.../functional/test_microblaze_s3adsp1800.py | 27 +++++++-
.../test_microblazeel_s3adsp1800.py | 25 ++++++-
17 files changed, 236 insertions(+), 63 deletions(-)
--
2.45.2
1 month, 1 week
[PATCH] qemu: Avoid useless tmp variable in qemuCanonicalizeMachine
by Jiri Denemark
Signed-off-by: Jiri Denemark <jdenemar(a)redhat.com>
---
src/qemu/qemu_postparse.c | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/src/qemu/qemu_postparse.c b/src/qemu/qemu_postparse.c
index 11134fb030..03b5ef825a 100644
--- a/src/qemu/qemu_postparse.c
+++ b/src/qemu/qemu_postparse.c
@@ -947,10 +947,8 @@ qemuCanonicalizeMachine(virDomainDef *def, virQEMUCaps *qemuCaps)
return 0;
if (STRNEQ(canon, def->os.machine)) {
- char *tmp;
- tmp = g_strdup(canon);
VIR_FREE(def->os.machine);
- def->os.machine = tmp;
+ def->os.machine = g_strdup(canon);
}
return 0;
--
2.47.0
1 month, 1 week
[PATCH 0/9] qemu: snapshot: Fix internal snapshot reversion with NVRAM image
by Peter Krempa
This series fixes two things:
- inactive snapshot handling with NVRAM image
- use of '-loadvm' commandline option to revert snapshots, both are
individually described
Peter Krempa (9):
qemu: Don't store path to qemu img
qemuDomainSnapshotForEachQcow2Raw: Remove 'driver' argument
qemu: Move 'qemuDomainSnapshotForEachQcow2(Raw)' to qemu_snapshot.c
qemuSnapshotForEachQcow2: Refactor
qemuSnapshotForEachQcow2: Handle also NVRAM image for internal
snapshots
qemu: monitor: Add monitor infrastructure for 'snapshot-load' QMP
command
qemu: Add enum entries for 'snapshot-load' qemu job
qemu: monitor: Extract vmstate presence for internal snapshots in
qemuBlockGetNamedNodeData
qemu: Avoid use of '-loadvm' commandline argument for internal
snapshot reversion
src/qemu/qemu_block.c | 1 +
src/qemu/qemu_blockjob.c | 2 +
src/qemu/qemu_blockjob.h | 1 +
src/qemu/qemu_command.c | 5 +-
src/qemu/qemu_conf.h | 3 -
src/qemu/qemu_domain.c | 106 +----
src/qemu/qemu_domain.h | 8 -
src/qemu/qemu_driver.c | 3 -
src/qemu/qemu_monitor.c | 16 +
src/qemu/qemu_monitor.h | 19 +-
src/qemu/qemu_monitor_json.c | 49 +-
src/qemu/qemu_monitor_json.h | 7 +
src/qemu/qemu_process.c | 7 +
src/qemu/qemu_snapshot.c | 437 ++++++++++++++++--
src/qemu/qemu_snapshot.h | 5 +
tests/qemublocktest.c | 14 +-
.../bitmap/snapshots-internal.out | 2 +-
17 files changed, 511 insertions(+), 174 deletions(-)
--
2.47.0
1 month, 1 week
[PATCH] kbase: virtiofs: Clarify migration support statement
by Peter Krempa
virtiofs 1.11 contains support for migration so update the 'Note' which
states that migration is not supported.
Additionally mention that VM snapshots don't save state of the files
shared via virtiofs so reverting is not a good idea.
Signed-off-by: Peter Krempa <pkrempa(a)redhat.com>
---
docs/kbase/virtiofs.rst | 10 +++++++---
1 file changed, 7 insertions(+), 3 deletions(-)
diff --git a/docs/kbase/virtiofs.rst b/docs/kbase/virtiofs.rst
index 457c15da7f..d0e09b078f 100644
--- a/docs/kbase/virtiofs.rst
+++ b/docs/kbase/virtiofs.rst
@@ -13,9 +13,13 @@ is designed to offer local file system semantics and performance.
See https://virtio-fs.gitlab.io/
-*Note:* virtiofs currently does not support migration so operations such as
-migration, save/managed-save, or snapshots with memory are not supported if
-a VM has a virtiofs filesystem connected.
+*Note:* Older versions of ``virtiofsd`` (prior to ``1.11``) do not not support
+migration so operations such as migration, save/managed-save, or snapshots with
+memory may not supported if a VM has a virtiofs filesystem connected.
+
+Additionally snapshot operations managed by libvirt do not snapshot the state
+of the files shared via ``virtiofs``, and thus reverting to an earlier state is
+not recommended.
Sharing a host directory with a guest
=====================================
--
2.47.0
1 month, 1 week
[PATCH] syntax-check: Suggest ways to fix internal references
by Andrea Bolognani
The rule catches incorrect attempts to use internal references,
but doesn't guide the developer hitting a failure towards the
not exactly obvious acceptable alternatives.
Signed-off-by: Andrea Bolognani <abologna(a)redhat.com>
---
build-aux/syntax-check.mk | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/build-aux/syntax-check.mk b/build-aux/syntax-check.mk
index 6ed2a61192..2c9593c28f 100644
--- a/build-aux/syntax-check.mk
+++ b/build-aux/syntax-check.mk
@@ -1135,7 +1135,7 @@ sc_prohibit_backup_files:
sc_avoid_remote_reference_to_local_file:
@prohibit='<#' \
in_vc_files='\.rst$$' \
- halt='use local reference within a file' \
+ halt='use `section`_ or `here <section_>`__ instead of `here <#section>`__' \
$(_sc_search_regexp)
# This Perl code is slightly obfuscated. Not only is each "$" doubled
--
2.47.0
1 month, 1 week
[PATCH 0/2] docs: Rework and extend NSS module page
by Andrea Bolognani
My initial goal was to write the second patch, but as usual I just
couldn't stop myself...
Andrea Bolognani (2):
docs: Rework documentation for the NSS module
docs: Document authselect to enable the NSS module
docs/nss.rst | 194 ++++++++++++++++++++-------------------------------
1 file changed, 76 insertions(+), 118 deletions(-)
--
2.47.0
1 month, 1 week
[PATCH] qemu: Move PostParse functions out of qemu_domain.c
by Michal Privoznik
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(a)redhat.com>
---
Best viewed via 'git show --color-moved'.
po/POTFILES | 1 +
src/qemu/meson.build | 1 +
src/qemu/qemu_domain.c | 1898 +-----------------------------------
src/qemu/qemu_domain.h | 16 +-
src/qemu/qemu_postparse.c | 1925 +++++++++++++++++++++++++++++++++++++
src/qemu/qemu_postparse.h | 54 ++
tests/qemublocktest.c | 1 +
7 files changed, 2001 insertions(+), 1895 deletions(-)
create mode 100644 src/qemu/qemu_postparse.c
create mode 100644 src/qemu/qemu_postparse.h
diff --git a/po/POTFILES b/po/POTFILES
index 1ed4086d2c..c20781e1a8 100644
--- a/po/POTFILES
+++ b/po/POTFILES
@@ -189,6 +189,7 @@ src/qemu/qemu_monitor_text.c
src/qemu/qemu_namespace.c
src/qemu/qemu_nbdkit.c
src/qemu/qemu_passt.c
+src/qemu/qemu_postparse.c
src/qemu/qemu_process.c
src/qemu/qemu_qapi.c
src/qemu/qemu_saveimage.c
diff --git a/src/qemu/meson.build b/src/qemu/meson.build
index 1d904bbc68..2a85e2e604 100644
--- a/src/qemu/meson.build
+++ b/src/qemu/meson.build
@@ -32,6 +32,7 @@ qemu_driver_sources = [
'qemu_namespace.c',
'qemu_nbdkit.c',
'qemu_passt.c',
+ 'qemu_postparse.c',
'qemu_process.c',
'qemu_qapi.c',
'qemu_saveimage.c',
diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c
index 562fa76a78..c798ef37fd 100644
--- a/src/qemu/qemu_domain.c
+++ b/src/qemu/qemu_domain.c
@@ -27,7 +27,6 @@
#include "qemu_cgroup.h"
#include "qemu_command.h"
#include "qemu_capabilities.h"
-#include "qemu_firmware.h"
#include "qemu_hostdev.h"
#include "qemu_migration_params.h"
#include "qemu_security.h"
@@ -38,6 +37,7 @@
#include "qemu_checkpoint.h"
#include "qemu_validate.h"
#include "qemu_namespace.h"
+#include "qemu_postparse.h"
#include "viralloc.h"
#include "virlog.h"
#include "virerror.h"
@@ -71,8 +71,6 @@
#include <sys/time.h>
#include <fcntl.h>
-#define QEMU_QXL_VGAMEM_DEFAULT 16 * 1024
-
#define VIR_FROM_THIS VIR_FROM_QEMU
VIR_LOG_INIT("qemu.qemu_domain");
@@ -2020,7 +2018,7 @@ qemuDomainObjPrivateAlloc(void *opaque)
}
-static int
+int
qemuStorageSourcePrivateDataAssignSecinfo(qemuDomainSecretInfo **secinfo,
char **alias)
{
@@ -4001,26 +3999,6 @@ virXMLNamespace virQEMUDriverDomainXMLNamespace = {
};
-static int
-qemuDomainDefAddImplicitInputDevice(virDomainDef *def,
- virQEMUCaps *qemuCaps)
-{
- if (virQEMUCapsSupportsI8042(qemuCaps, def) &&
- def->features[VIR_DOMAIN_FEATURE_PS2] != VIR_TRISTATE_SWITCH_OFF) {
- if (virDomainDefMaybeAddInput(def,
- VIR_DOMAIN_INPUT_TYPE_MOUSE,
- VIR_DOMAIN_INPUT_BUS_PS2) < 0)
- return -1;
-
- if (virDomainDefMaybeAddInput(def,
- VIR_DOMAIN_INPUT_TYPE_KBD,
- VIR_DOMAIN_INPUT_BUS_PS2) < 0)
- return -1;
- }
-
- return 0;
-}
-
static int
qemuDomainDefSuggestDefaultAudioBackend(virQEMUDriver *driver,
virDomainDef *def,
@@ -4168,7 +4146,7 @@ qemuDomainDefClearDefaultAudioBackend(virQEMUDriver *driver,
return 0;
}
-static int
+int
qemuDomainDefAddDefaultAudioBackend(virQEMUDriver *driver,
virDomainDef *def)
{
@@ -4227,7 +4205,7 @@ qemuDomainGetSCSIControllerModel(const virDomainDef *def,
}
-static virDomainPanicModel
+virDomainPanicModel
qemuDomainDefaultPanicModel(const virDomainDef *def)
{
if (qemuDomainIsPSeries(def))
@@ -4246,834 +4224,6 @@ qemuDomainDefaultPanicModel(const virDomainDef *def)
}
-static int
-qemuDomainDefAddDefaultDevices(virQEMUDriver *driver,
- virDomainDef *def,
- virQEMUCaps *qemuCaps)
-{
- bool addDefaultUSB = false;
- int usbModel = -1; /* "default for machinetype" */
- int pciRoot; /* index within def->controllers */
- bool addImplicitSATA = false;
- bool addPCIRoot = false;
- bool addPCIeRoot = false;
- bool addDefaultMemballoon = false;
- bool addDefaultUSBKBD = false;
- bool addDefaultUSBMouse = false;
- bool addPanicDevice = false;
- bool addITCOWatchdog = false;
- bool addIOMMU = false;
-
- /* add implicit input devices */
- if (qemuDomainDefAddImplicitInputDevice(def, qemuCaps) < 0)
- return -1;
-
- /* Add implicit PCI root controller if the machine has one */
- switch (def->os.arch) {
- case VIR_ARCH_I686:
- case VIR_ARCH_X86_64:
- addDefaultMemballoon = true;
-
- if (STREQ(def->os.machine, "isapc")) {
- break;
- }
-
- addDefaultUSB = true;
-
- if (qemuDomainIsQ35(def)) {
- addPCIeRoot = true;
- addImplicitSATA = true;
- addITCOWatchdog = true;
-
- if (virDomainDefGetVcpusMax(def) > QEMU_MAX_VCPUS_WITHOUT_EIM) {
- addIOMMU = true;
- }
-
- /* Prefer adding a USB3 controller if supported, fall back
- * to USB2 if there is no USB3 available, and if that's
- * unavailable don't add anything.
- */
- if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_QEMU_XHCI))
- usbModel = VIR_DOMAIN_CONTROLLER_MODEL_USB_QEMU_XHCI;
- else if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_NEC_USB_XHCI))
- usbModel = VIR_DOMAIN_CONTROLLER_MODEL_USB_NEC_XHCI;
- else if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_ICH9_USB_EHCI1))
- usbModel = VIR_DOMAIN_CONTROLLER_MODEL_USB_ICH9_EHCI1;
- else
- addDefaultUSB = false;
- break;
- }
- if (qemuDomainIsI440FX(def))
- addPCIRoot = true;
- break;
-
- case VIR_ARCH_ARMV6L:
- case VIR_ARCH_ARMV7L:
- case VIR_ARCH_ARMV7B:
- case VIR_ARCH_AARCH64:
- if (STREQ(def->os.machine, "versatilepb"))
- addPCIRoot = true;
-
- /* Add default USB for the two machine types which historically
- * supported -usb */
- if (STREQ(def->os.machine, "versatilepb") ||
- STRPREFIX(def->os.machine, "realview")) {
- addDefaultUSB = true;
- usbModel = VIR_DOMAIN_CONTROLLER_MODEL_USB_PCI_OHCI;
- }
-
- if (qemuDomainIsARMVirt(def))
- addPCIeRoot = true;
-
- break;
-
- case VIR_ARCH_PPC64:
- case VIR_ARCH_PPC64LE:
- addPCIRoot = true;
- addDefaultUSB = true;
- addDefaultUSBKBD = true;
- addDefaultUSBMouse = true;
- addDefaultMemballoon = true;
- /* For pSeries guests, the firmware provides the same
- * functionality as the pvpanic device, so automatically
- * add the definition if not already present */
- if (qemuDomainIsPSeries(def))
- addPanicDevice = true;
- break;
-
- case VIR_ARCH_ALPHA:
- case VIR_ARCH_PPC:
- case VIR_ARCH_PPCEMB:
- case VIR_ARCH_SH4:
- case VIR_ARCH_SH4EB:
- addDefaultUSB = true;
- addDefaultMemballoon = true;
- addPCIRoot = true;
- break;
-
- case VIR_ARCH_RISCV32:
- case VIR_ARCH_RISCV64:
- addDefaultMemballoon = true;
- if (qemuDomainIsRISCVVirt(def))
- addPCIeRoot = true;
- break;
-
- case VIR_ARCH_S390:
- case VIR_ARCH_S390X:
- addDefaultMemballoon = true;
- addPanicDevice = true;
- addPCIRoot = virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_ZPCI);
- break;
-
- case VIR_ARCH_SPARC64:
- addDefaultUSB = true;
- addDefaultMemballoon = true;
- addPCIRoot = true;
- break;
-
- case VIR_ARCH_MIPS:
- case VIR_ARCH_MIPSEL:
- case VIR_ARCH_MIPS64:
- case VIR_ARCH_MIPS64EL:
- addDefaultUSB = true;
- addDefaultMemballoon = true;
- if (qemuDomainIsMipsMalta(def))
- addPCIRoot = true;
- break;
-
- case VIR_ARCH_LOONGARCH64:
- addPCIeRoot = true;
- break;
-
- case VIR_ARCH_CRIS:
- case VIR_ARCH_ITANIUM:
- case VIR_ARCH_LM32:
- case VIR_ARCH_M68K:
- case VIR_ARCH_MICROBLAZE:
- case VIR_ARCH_MICROBLAZEEL:
- case VIR_ARCH_OR32:
- case VIR_ARCH_PARISC:
- case VIR_ARCH_PARISC64:
- case VIR_ARCH_PPCLE:
- case VIR_ARCH_SPARC:
- case VIR_ARCH_UNICORE32:
- case VIR_ARCH_XTENSA:
- case VIR_ARCH_XTENSAEB:
- case VIR_ARCH_NONE:
- case VIR_ARCH_LAST:
- default:
- break;
- }
-
- if (addDefaultUSB &&
- virDomainControllerFind(def, VIR_DOMAIN_CONTROLLER_TYPE_USB, 0) < 0 &&
- virDomainDefAddUSBController(def, 0, usbModel) < 0)
- return -1;
-
- if (addImplicitSATA &&
- virDomainDefMaybeAddController(
- def, VIR_DOMAIN_CONTROLLER_TYPE_SATA, 0, -1) < 0)
- return -1;
-
- pciRoot = virDomainControllerFind(def, VIR_DOMAIN_CONTROLLER_TYPE_PCI, 0);
-
- /* NB: any machine that sets addPCIRoot to true must also return
- * true from the function qemuDomainSupportsPCI().
- */
- if (addPCIRoot) {
- if (pciRoot >= 0) {
- if (def->controllers[pciRoot]->model != VIR_DOMAIN_CONTROLLER_MODEL_PCI_ROOT) {
- virReportError(VIR_ERR_XML_ERROR,
- _("The PCI controller with index='0' must be model='pci-root' for this machine type, but model='%1$s' was found instead"),
- virDomainControllerModelPCITypeToString(def->controllers[pciRoot]->model));
- return -1;
- }
- } else if (!virDomainDefAddController(def, VIR_DOMAIN_CONTROLLER_TYPE_PCI, 0,
- VIR_DOMAIN_CONTROLLER_MODEL_PCI_ROOT)) {
- return -1;
- }
- }
-
- /* When a machine has a pcie-root, make sure that there is always
- * a dmi-to-pci-bridge controller added as bus 1, and a pci-bridge
- * as bus 2, so that standard PCI devices can be connected
- *
- * NB: any machine that sets addPCIeRoot to true must also return
- * true from the function qemuDomainSupportsPCI().
- */
- if (addPCIeRoot) {
- if (pciRoot >= 0) {
- if (def->controllers[pciRoot]->model != VIR_DOMAIN_CONTROLLER_MODEL_PCIE_ROOT) {
- virReportError(VIR_ERR_XML_ERROR,
- _("The PCI controller with index='0' must be model='pcie-root' for this machine type, but model='%1$s' was found instead"),
- virDomainControllerModelPCITypeToString(def->controllers[pciRoot]->model));
- return -1;
- }
- } else if (!virDomainDefAddController(def, VIR_DOMAIN_CONTROLLER_TYPE_PCI, 0,
- VIR_DOMAIN_CONTROLLER_MODEL_PCIE_ROOT)) {
- return -1;
- }
- }
-
- if (addDefaultMemballoon && !def->memballoon) {
- virDomainMemballoonDef *memballoon;
- memballoon = g_new0(virDomainMemballoonDef, 1);
-
- memballoon->model = VIR_DOMAIN_MEMBALLOON_MODEL_VIRTIO;
- def->memballoon = memballoon;
- }
-
- if (addDefaultUSBMouse) {
- bool hasUSBTablet = false;
- size_t j;
-
- for (j = 0; j < def->ninputs; j++) {
- if (def->inputs[j]->type == VIR_DOMAIN_INPUT_TYPE_TABLET &&
- def->inputs[j]->bus == VIR_DOMAIN_INPUT_BUS_USB) {
- hasUSBTablet = true;
- break;
- }
- }
-
- /* Historically, we have automatically added USB keyboard and
- * mouse to some guests. While the former device is generally
- * safe to have, adding the latter is undesiderable if a USB
- * tablet is already present in the guest */
- if (hasUSBTablet)
- addDefaultUSBMouse = false;
- }
-
- if (addDefaultUSBKBD &&
- def->ngraphics > 0 &&
- virDomainDefMaybeAddInput(def,
- VIR_DOMAIN_INPUT_TYPE_KBD,
- VIR_DOMAIN_INPUT_BUS_USB) < 0)
- return -1;
-
- if (addDefaultUSBMouse &&
- def->ngraphics > 0 &&
- virDomainDefMaybeAddInput(def,
- VIR_DOMAIN_INPUT_TYPE_MOUSE,
- VIR_DOMAIN_INPUT_BUS_USB) < 0)
- return -1;
-
- if (addPanicDevice) {
- virDomainPanicModel defaultModel = qemuDomainDefaultPanicModel(def);
- size_t j;
-
- for (j = 0; j < def->npanics; j++) {
- if (def->panics[j]->model == VIR_DOMAIN_PANIC_MODEL_DEFAULT ||
- def->panics[j]->model == defaultModel)
- break;
- }
-
- if (j == def->npanics) {
- virDomainPanicDef *panic = g_new0(virDomainPanicDef, 1);
-
- VIR_APPEND_ELEMENT_COPY(def->panics, def->npanics, panic);
- }
- }
-
- if (addITCOWatchdog) {
- size_t i = 0;
-
- for (i = 0; i < def->nwatchdogs; i++) {
- if (def->watchdogs[i]->model == VIR_DOMAIN_WATCHDOG_MODEL_ITCO)
- break;
- }
-
- if (i == def->nwatchdogs) {
- virDomainWatchdogDef *watchdog = g_new0(virDomainWatchdogDef, 1);
-
- watchdog->model = VIR_DOMAIN_WATCHDOG_MODEL_ITCO;
- if (def->nwatchdogs)
- watchdog->action = def->watchdogs[0]->action;
- else
- watchdog->action = VIR_DOMAIN_WATCHDOG_ACTION_RESET;
-
- VIR_APPEND_ELEMENT(def->watchdogs, def->nwatchdogs, watchdog);
- }
- }
-
- if (addIOMMU && !def->iommu &&
- virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_INTEL_IOMMU) &&
- virQEMUCapsGet(qemuCaps, QEMU_CAPS_INTEL_IOMMU_INTREMAP) &&
- virQEMUCapsGet(qemuCaps, QEMU_CAPS_INTEL_IOMMU_EIM)) {
- g_autoptr(virDomainIOMMUDef) iommu = NULL;
-
- iommu = virDomainIOMMUDefNew();
- iommu->model = VIR_DOMAIN_IOMMU_MODEL_INTEL;
- /* eim requires intremap. */
- iommu->intremap = VIR_TRISTATE_SWITCH_ON;
- iommu->eim = VIR_TRISTATE_SWITCH_ON;
-
- def->iommu = g_steal_pointer(&iommu);
- }
-
- if (qemuDomainDefAddDefaultAudioBackend(driver, def) < 0)
- return -1;
-
- return 0;
-}
-
-
-/**
- * qemuDomainDefEnableDefaultFeatures:
- * @def: domain definition
- * @qemuCaps: QEMU capabilities
- *
- * Make sure that features that should be enabled by default are actually
- * enabled and configure default values related to those features.
- */
-static void
-qemuDomainDefEnableDefaultFeatures(virDomainDef *def,
- virQEMUCaps *qemuCaps)
-{
- /* The virt machine type always uses GIC: if the relevant information
- * was not included in the domain XML, we need to choose a suitable
- * GIC version ourselves */
- if ((def->features[VIR_DOMAIN_FEATURE_GIC] == VIR_TRISTATE_SWITCH_ABSENT &&
- qemuDomainIsARMVirt(def)) ||
- (def->features[VIR_DOMAIN_FEATURE_GIC] == VIR_TRISTATE_SWITCH_ON &&
- def->gic_version == VIR_GIC_VERSION_NONE)) {
- virGICVersion version;
-
- VIR_DEBUG("Looking for usable GIC version in domain capabilities");
- for (version = VIR_GIC_VERSION_LAST - 1;
- version > VIR_GIC_VERSION_NONE;
- version--) {
-
- /* We want to use the highest available GIC version for guests;
- * however, the emulated GICv3 is currently lacking a MSI controller,
- * making it unsuitable for the pure PCIe topology we aim for.
- *
- * For that reason, we skip this step entirely for TCG guests,
- * and rely on the code below to pick the default version, GICv2,
- * which supports all the features we need.
- *
- * See https://bugzilla.redhat.com/show_bug.cgi?id=1414081 */
- if (version == VIR_GIC_VERSION_3 &&
- def->virtType == VIR_DOMAIN_VIRT_QEMU) {
- continue;
- }
-
- if (virQEMUCapsSupportsGICVersion(qemuCaps,
- def->virtType,
- version)) {
- VIR_DEBUG("Using GIC version %s",
- virGICVersionTypeToString(version));
- def->gic_version = version;
- break;
- }
- }
-
- /* Use the default GIC version (GICv2) as a last-ditch attempt
- * if no match could be found above */
- if (def->gic_version == VIR_GIC_VERSION_NONE) {
- VIR_DEBUG("Using GIC version 2 (default)");
- def->gic_version = VIR_GIC_VERSION_2;
- }
-
- /* Even if we haven't found a usable GIC version in the domain
- * capabilities, we still want to enable this */
- def->features[VIR_DOMAIN_FEATURE_GIC] = VIR_TRISTATE_SWITCH_ON;
- }
-}
-
-
-static int
-qemuCanonicalizeMachine(virDomainDef *def, virQEMUCaps *qemuCaps)
-{
- const char *canon;
-
- if (!(canon = virQEMUCapsGetCanonicalMachine(qemuCaps, def->virtType,
- def->os.machine)))
- return 0;
-
- if (STRNEQ(canon, def->os.machine)) {
- char *tmp;
- tmp = g_strdup(canon);
- VIR_FREE(def->os.machine);
- def->os.machine = tmp;
- }
-
- return 0;
-}
-
-
-static int
-qemuDomainRecheckInternalPaths(virDomainDef *def,
- virQEMUDriverConfig *cfg,
- unsigned int flags)
-{
- size_t i = 0;
- size_t j = 0;
-
- for (i = 0; i < def->ngraphics; ++i) {
- virDomainGraphicsDef *graphics = def->graphics[i];
-
- for (j = 0; j < graphics->nListens; ++j) {
- virDomainGraphicsListenDef *glisten = &graphics->listens[j];
-
- /* This will happen only if we parse XML from old libvirts where
- * unix socket was available only for VNC graphics. In this
- * particular case we should follow the behavior and if we remove
- * the auto-generated socket based on config option from qemu.conf
- * we need to change the listen type to address. */
- if (graphics->type == VIR_DOMAIN_GRAPHICS_TYPE_VNC &&
- glisten->type == VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_SOCKET &&
- glisten->socket &&
- !glisten->autoGenerated &&
- STRPREFIX(glisten->socket, cfg->libDir)) {
- if (flags & VIR_DOMAIN_DEF_PARSE_INACTIVE) {
- VIR_FREE(glisten->socket);
- glisten->type = VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_ADDRESS;
- } else {
- glisten->fromConfig = true;
- }
- }
- }
- }
-
- return 0;
-}
-
-
-static int
-qemuDomainDefBootPostParse(virDomainDef *def,
- virQEMUDriver *driver,
- unsigned int parseFlags)
-{
- bool abiUpdate = !!(parseFlags & VIR_DOMAIN_DEF_PARSE_ABI_UPDATE);
-
- /* If we're loading an existing configuration from disk, we
- * should try as hard as possible to preserve historical
- * behavior. In particular, firmware autoselection being enabled
- * could never have resulted, before libvirt 9.2.0, in anything
- * but a raw firmware image being selected.
- *
- * In order to ensure that existing domains keep working even if
- * a firmware descriptor for a build with a different format is
- * given higher priority, explicitly add this requirement to the
- * definition before performing firmware selection */
- if (!abiUpdate && def->os.firmware) {
- if (!def->os.loader)
- def->os.loader = virDomainLoaderDefNew();
- if (!def->os.loader->format)
- def->os.loader->format = VIR_STORAGE_FILE_RAW;
- }
-
- /* Firmware selection can fail for a number of reasons, but the
- * most likely one is that the requested configuration contains
- * mistakes or includes constraints that are impossible to
- * satisfy on the current system.
- *
- * If that happens, we have to react differently based on the
- * situation: if we're defining a new domain or updating its ABI,
- * we should let the user know immediately so that they can
- * change the requested configuration, hopefully into one that we
- * can work with; if we're loading the configuration of an
- * existing domain from disk, however, we absolutely cannot error
- * out here, or the domain will disappear.
- *
- * To handle the second case gracefully, we clear any reported
- * errors and continue as if nothing had happened. When it's time
- * to start the domain, qemuFirmwareFillDomain() will be run
- * again, fail in the same way, and at that point we'll have a
- * chance to inform the user of any issues */
- if (qemuFirmwareFillDomain(driver, def, abiUpdate) < 0) {
- if (abiUpdate) {
- return -1;
- } else {
- virResetLastError();
- return 0;
- }
- }
-
- return 0;
-}
-
-
-static int
-qemuDomainDefMachinePostParse(virDomainDef *def,
- virQEMUCaps *qemuCaps)
-{
- if (!def->os.machine) {
- const char *machine = virQEMUCapsGetPreferredMachine(qemuCaps,
- def->virtType);
- if (!machine) {
- virReportError(VIR_ERR_INVALID_ARG,
- _("could not get preferred machine for %1$s type=%2$s"),
- def->emulator,
- virDomainVirtTypeToString(def->virtType));
- return -1;
- }
-
- def->os.machine = g_strdup(machine);
- }
-
- if (qemuCanonicalizeMachine(def, qemuCaps) < 0)
- return -1;
-
- return 0;
-}
-
-
-static int
-qemuDomainDefVcpusPostParse(virDomainDef *def)
-{
- unsigned int maxvcpus = virDomainDefGetVcpusMax(def);
- virDomainVcpuDef *vcpu;
- virDomainVcpuDef *prevvcpu;
- size_t i;
- bool has_order = false;
-
- /* vcpu 0 needs to be present, first, and non-hotpluggable */
- vcpu = virDomainDefGetVcpu(def, 0);
- if (!vcpu->online) {
- virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
- _("vcpu 0 can't be offline"));
- return -1;
- }
- if (vcpu->hotpluggable == VIR_TRISTATE_BOOL_YES) {
- virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
- _("vcpu0 can't be hotpluggable"));
- return -1;
- }
- if (vcpu->order != 0 && vcpu->order != 1) {
- virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
- _("vcpu0 must be enabled first"));
- return -1;
- }
-
- if (vcpu->order != 0)
- has_order = true;
-
- prevvcpu = vcpu;
-
- /* all online vcpus or non online vcpu need to have order set */
- for (i = 1; i < maxvcpus; i++) {
- vcpu = virDomainDefGetVcpu(def, i);
-
- if (vcpu->online &&
- (vcpu->order != 0) != has_order) {
- virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
- _("all vcpus must have either set or unset order"));
- return -1;
- }
-
- /* few conditions for non-hotpluggable (thus online) vcpus */
- if (vcpu->hotpluggable == VIR_TRISTATE_BOOL_NO) {
- /* they can be ordered only at the beginning */
- if (prevvcpu->hotpluggable == VIR_TRISTATE_BOOL_YES) {
- virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
- _("online non-hotpluggable vcpus need to be ordered prior to hotplugable vcpus"));
- return -1;
- }
-
- /* they need to be in order (qemu doesn't support any order yet).
- * Also note that multiple vcpus may share order on some platforms */
- if (prevvcpu->order > vcpu->order) {
- virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
- _("online non-hotpluggable vcpus must be ordered in ascending order"));
- return -1;
- }
- }
-
- prevvcpu = vcpu;
- }
-
- return 0;
-}
-
-
-static int
-qemuDomainDefSetDefaultCPU(virDomainDef *def,
- virArch hostarch,
- virQEMUCaps *qemuCaps)
-{
- const char *model;
-
- if (def->cpu &&
- (def->cpu->mode != VIR_CPU_MODE_CUSTOM ||
- def->cpu->model))
- return 0;
-
- if (!virCPUArchIsSupported(def->os.arch))
- return 0;
-
- /* Default CPU model info from QEMU is usable for TCG only except for
- * x86, s390, and ppc64. */
- if (!ARCH_IS_X86(def->os.arch) &&
- !ARCH_IS_S390(def->os.arch) &&
- !ARCH_IS_PPC64(def->os.arch) &&
- def->virtType != VIR_DOMAIN_VIRT_QEMU)
- return 0;
-
- model = virQEMUCapsGetMachineDefaultCPU(qemuCaps, def->os.machine, def->virtType);
- if (!model) {
- VIR_DEBUG("Unknown default CPU model for domain '%s'", def->name);
- return 0;
- }
-
- if (STREQ(model, "host") && def->virtType != VIR_DOMAIN_VIRT_KVM) {
- virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
- _("QEMU reports invalid default CPU model \"host\" for non-kvm domain virt type"));
- return -1;
- }
-
- if (!def->cpu)
- def->cpu = virCPUDefNew();
-
- def->cpu->type = VIR_CPU_TYPE_GUEST;
-
- if (STREQ(model, "host")) {
- if (ARCH_IS_S390(def->os.arch) &&
- virQEMUCapsIsCPUModeSupported(qemuCaps, hostarch, def->virtType,
- VIR_CPU_MODE_HOST_MODEL,
- def->os.machine)) {
- def->cpu->mode = VIR_CPU_MODE_HOST_MODEL;
- } else {
- def->cpu->mode = VIR_CPU_MODE_HOST_PASSTHROUGH;
- }
-
- VIR_DEBUG("Setting default CPU mode for domain '%s' to %s",
- def->name, virCPUModeTypeToString(def->cpu->mode));
- } else {
- /* We need to turn off all CPU checks when the domain is started
- * because the default CPU (e.g., qemu64) may not be runnable on any
- * host. QEMU will just disable the unavailable features and we will
- * update the CPU definition accordingly and set check to FULL when
- * starting the domain. */
- def->cpu->check = VIR_CPU_CHECK_NONE;
- def->cpu->mode = VIR_CPU_MODE_CUSTOM;
- def->cpu->match = VIR_CPU_MATCH_EXACT;
- def->cpu->fallback = VIR_CPU_FALLBACK_FORBID;
- def->cpu->model = g_strdup(model);
-
- VIR_DEBUG("Setting default CPU model for domain '%s' to %s",
- def->name, model);
- }
-
- return 0;
-}
-
-
-static int
-qemuDomainDefCPUPostParse(virDomainDef *def,
- virQEMUCaps *qemuCaps)
-{
- virCPUFeatureDef *sveFeature = NULL;
- bool sveVectorLengthsProvided = false;
- size_t i;
-
- if (!def->cpu)
- return 0;
-
- for (i = 0; i < def->cpu->nfeatures; i++) {
- virCPUFeatureDef *feature = &def->cpu->features[i];
-
- if (STREQ(feature->name, "sve")) {
- sveFeature = feature;
- } else if (STRPREFIX(feature->name, "sve")) {
- sveVectorLengthsProvided = true;
- }
- }
-
- if (sveVectorLengthsProvided) {
- if (sveFeature) {
- if (sveFeature->policy == VIR_CPU_FEATURE_DISABLE ||
- sveFeature->policy == VIR_CPU_FEATURE_FORBID) {
- virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
- _("SVE disabled, but SVE vector lengths provided"));
- return -1;
- } else {
- sveFeature->policy = VIR_CPU_FEATURE_REQUIRE;
- }
- } else {
- VIR_RESIZE_N(def->cpu->features, def->cpu->nfeatures_max,
- def->cpu->nfeatures, 1);
-
- def->cpu->features[def->cpu->nfeatures].name = g_strdup("sve");
- def->cpu->features[def->cpu->nfeatures].policy = VIR_CPU_FEATURE_REQUIRE;
-
- def->cpu->nfeatures++;
- }
- }
-
- /* Running domains were either started before QEMU_CAPS_CPU_MIGRATABLE was
- * introduced and thus we can't rely on it or they already have the
- * migratable default set. */
- if (def->id == -1 &&
- qemuCaps &&
- def->cpu->mode == VIR_CPU_MODE_HOST_PASSTHROUGH &&
- def->cpu->migratable == VIR_TRISTATE_SWITCH_ABSENT) {
- if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_CPU_MIGRATABLE))
- def->cpu->migratable = VIR_TRISTATE_SWITCH_ON;
- else if (ARCH_IS_X86(def->os.arch))
- def->cpu->migratable = VIR_TRISTATE_SWITCH_OFF;
- }
-
- /* Nothing to be done if only CPU topology is specified. */
- if (def->cpu->mode == VIR_CPU_MODE_CUSTOM &&
- !def->cpu->model)
- return 0;
-
- if (def->cpu->check != VIR_CPU_CHECK_DEFAULT)
- return 0;
-
- switch ((virCPUMode) def->cpu->mode) {
- case VIR_CPU_MODE_HOST_PASSTHROUGH:
- case VIR_CPU_MODE_MAXIMUM:
- def->cpu->check = VIR_CPU_CHECK_NONE;
- break;
-
- case VIR_CPU_MODE_HOST_MODEL:
- def->cpu->check = VIR_CPU_CHECK_PARTIAL;
- break;
-
- case VIR_CPU_MODE_CUSTOM:
- /* Custom CPUs in TCG mode are not compared to host CPU by default. */
- if (def->virtType == VIR_DOMAIN_VIRT_QEMU)
- def->cpu->check = VIR_CPU_CHECK_NONE;
- else
- def->cpu->check = VIR_CPU_CHECK_PARTIAL;
- break;
-
- case VIR_CPU_MODE_LAST:
- break;
- }
-
- return 0;
-}
-
-
-static int
-qemuDomainDefTsegPostParse(virDomainDef *def,
- virQEMUCaps *qemuCaps)
-{
- if (def->features[VIR_DOMAIN_FEATURE_SMM] != VIR_TRISTATE_SWITCH_ON)
- return 0;
-
- if (!def->tseg_specified)
- return 0;
-
- if (!qemuDomainIsQ35(def)) {
- virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
- _("SMM TSEG is only supported with q35 machine type"));
- return -1;
- }
-
- if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_MCH_EXTENDED_TSEG_MBYTES)) {
- virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
- _("Setting TSEG size is not supported with this QEMU binary"));
- return -1;
- }
-
- if (def->tseg_size & ((1 << 20) - 1)) {
- virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
- _("SMM TSEG size must be divisible by 1 MiB"));
- return -1;
- }
-
- return 0;
-}
-
-
-static int
-qemuDomainDefNumaAutoAdd(virDomainDef *def,
- unsigned int parseFlags)
-{
- bool abiUpdate = !!(parseFlags & VIR_DOMAIN_DEF_PARSE_ABI_UPDATE);
- unsigned long long nodeMem;
- size_t i;
-
- if (!abiUpdate ||
- !virDomainDefHasMemoryHotplug(def) ||
- virDomainNumaGetNodeCount(def->numa) > 0) {
- return 0;
- }
-
- nodeMem = virDomainDefGetMemoryTotal(def);
-
- if (!def->numa)
- def->numa = virDomainNumaNew();
-
- virDomainNumaSetNodeCount(def->numa, 1);
-
- for (i = 0; i < def->nmems; i++) {
- virDomainMemoryDef *mem = def->mems[i];
-
- if (mem->size > nodeMem) {
- virReportError(VIR_ERR_XML_ERROR, "%s",
- _("Total size of memory devices exceeds the total memory size"));
- return -1;
- }
-
- nodeMem -= mem->size;
-
- switch (mem->model) {
- case VIR_DOMAIN_MEMORY_MODEL_DIMM:
- case VIR_DOMAIN_MEMORY_MODEL_NVDIMM:
- case VIR_DOMAIN_MEMORY_MODEL_VIRTIO_MEM:
- if (mem->targetNode == -1)
- mem->targetNode = 0;
- break;
-
- case VIR_DOMAIN_MEMORY_MODEL_NONE:
- case VIR_DOMAIN_MEMORY_MODEL_VIRTIO_PMEM:
- case VIR_DOMAIN_MEMORY_MODEL_SGX_EPC:
- case VIR_DOMAIN_MEMORY_MODEL_LAST:
- break;
- }
- }
-
- virDomainNumaSetNodeMemorySize(def->numa, 0, nodeMem);
-
- return 0;
-}
-
-
/**
* qemuDomainDefNumaCPUsRectify:
* @numa: pointer to numa definition
@@ -5106,141 +4256,6 @@ qemuDomainDefNumaCPUsRectify(virDomainDef *def,
}
-static int
-qemuDomainDefNumaCPUsPostParse(virDomainDef *def,
- virQEMUCaps *qemuCaps,
- unsigned int parseFlags)
-{
- if (qemuDomainDefNumaAutoAdd(def, parseFlags) < 0)
- return -1;
-
- return qemuDomainDefNumaCPUsRectify(def, qemuCaps);
-}
-
-
-static int
-qemuDomainDefPostParseBasic(virDomainDef *def,
- void *opaque G_GNUC_UNUSED)
-{
- virQEMUDriver *driver = opaque;
-
- /* check for emulator and create a default one if needed */
- if (!def->emulator) {
- if (!(def->emulator = virQEMUCapsGetDefaultEmulator(
- driver->hostarch, def->os.arch))) {
- virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
- _("No emulator found for arch '%1$s'"),
- virArchToString(def->os.arch));
- return 1;
- }
- }
-
- return 0;
-}
-
-
-/**
- * qemuDomainDefACPIPostParse:
- * @def: domain definition
- * @qemuCaps: qemu capabilities object
- *
- * Fixup the use of ACPI flag on certain architectures that never supported it
- * and users for some reason used it, which would break migration to newer
- * libvirt versions which check whether given machine type supports ACPI.
- *
- * The fixup is done in post-parse as it's hard to update the ABI stability
- * check on source of the migration.
- */
-static void
-qemuDomainDefACPIPostParse(virDomainDef *def,
- virQEMUCaps *qemuCaps,
- unsigned int parseFlags)
-{
- /* Only cases when ACPI is enabled need to be fixed up */
- if (def->features[VIR_DOMAIN_FEATURE_ACPI] != VIR_TRISTATE_SWITCH_ON)
- return;
-
- /* Strip the <acpi/> feature only for non-fresh configs, in order to still
- * produce an error if the feature is present in a newly defined one.
- *
- * The use of the VIR_DOMAIN_DEF_PARSE_ABI_UPDATE looks counter-intuitive,
- * but it's used only in qemuDomainCreateXML/qemuDomainDefineXMLFlags APIs
- * */
- if (parseFlags & VIR_DOMAIN_DEF_PARSE_ABI_UPDATE)
- return;
-
- /* This fixup is applicable _only_ on architectures which were present as of
- * libvirt-9.2 and *never* supported ACPI. The fixup is currently done only
- * for existing users of s390(x) to fix migration for configs which had
- * <acpi/> despite being ignored.
- */
- if (def->os.arch != VIR_ARCH_S390 &&
- def->os.arch != VIR_ARCH_S390X)
- return;
-
- /* To be sure, we only strip ACPI if given machine type doesn't support it */
- if (virQEMUCapsMachineSupportsACPI(qemuCaps, def->virtType, def->os.machine) != VIR_TRISTATE_BOOL_NO)
- return;
-
- def->features[VIR_DOMAIN_FEATURE_ACPI] = VIR_TRISTATE_SWITCH_ABSENT;
-}
-
-
-static int
-qemuDomainDefPostParse(virDomainDef *def,
- unsigned int parseFlags,
- void *opaque,
- void *parseOpaque)
-{
- virQEMUDriver *driver = opaque;
- g_autoptr(virQEMUDriverConfig) cfg = virQEMUDriverGetConfig(driver);
- virQEMUCaps *qemuCaps = parseOpaque;
-
- /* Note that qemuCaps may be NULL when this function is called. This
- * function shall not fail in that case. It will be re-run on VM startup
- * with the capabilities populated.
- */
- if (!qemuCaps)
- return 1;
-
- if (qemuDomainDefMachinePostParse(def, qemuCaps) < 0)
- return -1;
-
- qemuDomainDefACPIPostParse(def, qemuCaps, parseFlags);
-
- if (qemuDomainDefBootPostParse(def, driver, parseFlags) < 0)
- return -1;
-
- if (qemuDomainDefAddDefaultDevices(driver, def, qemuCaps) < 0)
- return -1;
-
- if (qemuDomainDefSetDefaultCPU(def, driver->hostarch, qemuCaps) < 0)
- return -1;
-
- qemuDomainDefEnableDefaultFeatures(def, qemuCaps);
-
- if (qemuDomainRecheckInternalPaths(def, cfg, parseFlags) < 0)
- return -1;
-
- if (qemuSecurityVerify(driver->securityManager, def) < 0)
- return -1;
-
- if (qemuDomainDefVcpusPostParse(def) < 0)
- return -1;
-
- if (qemuDomainDefCPUPostParse(def, qemuCaps) < 0)
- return -1;
-
- if (qemuDomainDefTsegPostParse(def, qemuCaps) < 0)
- return -1;
-
- if (qemuDomainDefNumaCPUsPostParse(def, qemuCaps, parseFlags) < 0)
- return -1;
-
- return 0;
-}
-
-
int
qemuDomainValidateActualNetDef(const virDomainNetDef *net,
virQEMUCaps *qemuCaps G_GNUC_UNUSED)
@@ -5605,63 +4620,6 @@ qemuDomainValidateStorageSource(virStorageSource *src,
}
-/**
- * qemuDomainDefaultNetModel:
- * @def: domain definition
- * @qemuCaps: qemu capabilities
- *
- * Returns the default network model for a given domain. Note that if @qemuCaps
- * is NULL this function may return NULL if the default model depends on the
- * capabilities.
- */
-static int
-qemuDomainDefaultNetModel(const virDomainDef *def,
- virQEMUCaps *qemuCaps)
-{
- /* When there are no backwards compatibility concerns getting in
- * the way, virtio is a good default */
- if (ARCH_IS_S390(def->os.arch) ||
- qemuDomainIsLoongArchVirt(def) ||
- qemuDomainIsRISCVVirt(def)) {
- return VIR_DOMAIN_NET_MODEL_VIRTIO;
- }
-
- if (ARCH_IS_ARM(def->os.arch)) {
- if (STREQ(def->os.machine, "versatilepb"))
- return VIR_DOMAIN_NET_MODEL_SMC91C111;
-
- if (qemuDomainIsARMVirt(def))
- return VIR_DOMAIN_NET_MODEL_VIRTIO;
-
- /* Incomplete. vexpress (and a few others) use this, but not all
- * arm boards */
- return VIR_DOMAIN_NET_MODEL_LAN9118;
- }
-
- /* In all other cases the model depends on the capabilities. If they were
- * not provided don't report any default. */
- if (!qemuCaps)
- return VIR_DOMAIN_NET_MODEL_UNKNOWN;
-
- /* Try several network devices in turn; each of these devices is
- * less likely be supported out-of-the-box by the guest operating
- * system than the previous one */
- if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_RTL8139))
- return VIR_DOMAIN_NET_MODEL_RTL8139;
-
- if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_E1000))
- return VIR_DOMAIN_NET_MODEL_E1000;
-
- if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_VIRTIO_NET))
- return VIR_DOMAIN_NET_MODEL_VIRTIO;
-
- /* We've had no luck detecting support for any network device,
- * but we have to return something: might as well be rtl8139 */
- return VIR_DOMAIN_NET_MODEL_RTL8139;
-}
-
-
-
static bool
qemuDomainChrMatchDefaultPath(const char *prefix,
const char *infix,
@@ -5707,7 +4665,7 @@ qemuDomainChrMatchDefaultPath(const char *prefix,
* Please note, as of libvirt 9.7.0 the channelTargetDir is no longer derived
* from cfg->libDir but rather cfg->stateDir.
*/
-static void
+void
qemuDomainChrDefDropDefaultPath(virDomainChrDef *chr,
virQEMUDriver *driver)
{
@@ -5750,640 +4708,7 @@ qemuDomainChrDefDropDefaultPath(virDomainChrDef *chr,
}
-static int
-qemuDomainShmemDefPostParse(virDomainShmemDef *shm)
-{
- /* This was the default since the introduction of this device. */
- if (shm->model != VIR_DOMAIN_SHMEM_MODEL_IVSHMEM_DOORBELL && !shm->size)
- shm->size = 4 << 20;
-
- /* Nothing more to check/change for IVSHMEM */
- if (shm->model == VIR_DOMAIN_SHMEM_MODEL_IVSHMEM)
- return 0;
-
- if (!shm->server.enabled) {
- if (shm->model == VIR_DOMAIN_SHMEM_MODEL_IVSHMEM_DOORBELL) {
- virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
- _("shmem model '%1$s' is supported only with server option enabled"),
- virDomainShmemModelTypeToString(shm->model));
- return -1;
- }
-
- if (shm->msi.enabled) {
- virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
- _("shmem model '%1$s' doesn't support msi"),
- virDomainShmemModelTypeToString(shm->model));
- }
- } else {
- if (shm->model == VIR_DOMAIN_SHMEM_MODEL_IVSHMEM_PLAIN) {
- virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
- _("shmem model '%1$s' is supported only with server option disabled"),
- virDomainShmemModelTypeToString(shm->model));
- return -1;
- }
-
- if (shm->size) {
- virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
- _("shmem model '%1$s' does not support size setting"),
- virDomainShmemModelTypeToString(shm->model));
- return -1;
- }
- shm->msi.enabled = true;
- if (!shm->msi.ioeventfd)
- shm->msi.ioeventfd = VIR_TRISTATE_SWITCH_ON;
- }
-
- return 0;
-}
-
-
-#define QEMU_USB_XHCI_MAXPORTS 15
-
-
-static int
-qemuDomainControllerDefPostParse(virDomainControllerDef *cont,
- const virDomainDef *def,
- virQEMUCaps *qemuCaps,
- unsigned int parseFlags)
-{
- switch (cont->type) {
- case VIR_DOMAIN_CONTROLLER_TYPE_SCSI:
- /* Set the default SCSI controller model if not already set */
- cont->model = qemuDomainGetSCSIControllerModel(def, cont, qemuCaps);
-
- if (cont->model < 0)
- return -1;
- break;
-
- case VIR_DOMAIN_CONTROLLER_TYPE_USB:
- if (cont->model == VIR_DOMAIN_CONTROLLER_MODEL_USB_DEFAULT && qemuCaps) {
- /* Pick a suitable default model for the USB controller if none
- * has been selected by the user and we have the qemuCaps for
- * figuring out which controllers are supported.
- *
- * We rely on device availability instead of setting the model
- * unconditionally because, for some machine types, there's a
- * chance we will get away with using the legacy USB controller
- * when the relevant device is not available.
- *
- * See qemuBuildControllersCommandLine() */
-
- /* Default USB controller is piix3-uhci if available. Fall back to
- * 'pci-ohci' otherwise which is the default for non-x86 machines
- * which honour -usb */
- if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_PIIX3_USB_UHCI))
- cont->model = VIR_DOMAIN_CONTROLLER_MODEL_USB_PIIX3_UHCI;
- else if (!ARCH_IS_X86(def->os.arch) &&
- virQEMUCapsGet(qemuCaps, QEMU_CAPS_PCI_OHCI))
- cont->model = VIR_DOMAIN_CONTROLLER_MODEL_USB_PCI_OHCI;
-
- if (ARCH_IS_S390(def->os.arch)) {
- if (cont->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) {
- /* set the default USB model to none for s390 unless an
- * address is found */
- cont->model = VIR_DOMAIN_CONTROLLER_MODEL_USB_NONE;
- }
- } else if (ARCH_IS_PPC64(def->os.arch)) {
- /* To not break migration we need to set default USB controller
- * for ppc64 to pci-ohci if we cannot change ABI of the VM.
- * The nec-usb-xhci or qemu-xhci controller is used as default
- * only for newly defined domains or devices. */
- if ((parseFlags & VIR_DOMAIN_DEF_PARSE_ABI_UPDATE) &&
- virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_QEMU_XHCI)) {
- cont->model = VIR_DOMAIN_CONTROLLER_MODEL_USB_QEMU_XHCI;
- } else if ((parseFlags & VIR_DOMAIN_DEF_PARSE_ABI_UPDATE) &&
- virQEMUCapsGet(qemuCaps, QEMU_CAPS_NEC_USB_XHCI)) {
- cont->model = VIR_DOMAIN_CONTROLLER_MODEL_USB_NEC_XHCI;
- } else if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_PCI_OHCI)) {
- cont->model = VIR_DOMAIN_CONTROLLER_MODEL_USB_PCI_OHCI;
- } else {
- /* Explicitly fallback to legacy USB controller for PPC64. */
- cont->model = VIR_DOMAIN_CONTROLLER_MODEL_USB_DEFAULT;
- }
- } else if (def->os.arch == VIR_ARCH_AARCH64) {
- if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_QEMU_XHCI))
- cont->model = VIR_DOMAIN_CONTROLLER_MODEL_USB_QEMU_XHCI;
- else if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_NEC_USB_XHCI))
- cont->model = VIR_DOMAIN_CONTROLLER_MODEL_USB_NEC_XHCI;
- } else if (ARCH_IS_LOONGARCH(def->os.arch)) {
- if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_QEMU_XHCI))
- cont->model = VIR_DOMAIN_CONTROLLER_MODEL_USB_QEMU_XHCI;
- }
- }
- /* forbid usb model 'qusb1' and 'qusb2' in this kind of hyperviosr */
- if (cont->model == VIR_DOMAIN_CONTROLLER_MODEL_USB_QUSB1 ||
- cont->model == VIR_DOMAIN_CONTROLLER_MODEL_USB_QUSB2) {
- virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
- _("USB controller model type 'qusb1' or 'qusb2' is not supported in %1$s"),
- virDomainVirtTypeToString(def->virtType));
- return -1;
- }
- if ((cont->model == VIR_DOMAIN_CONTROLLER_MODEL_USB_NEC_XHCI ||
- cont->model == VIR_DOMAIN_CONTROLLER_MODEL_USB_QEMU_XHCI) &&
- cont->opts.usbopts.ports > QEMU_USB_XHCI_MAXPORTS) {
- virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
- _("'%1$s' controller only supports up to '%2$u' ports"),
- virDomainControllerModelUSBTypeToString(cont->model),
- QEMU_USB_XHCI_MAXPORTS);
- return -1;
- }
- break;
-
- case VIR_DOMAIN_CONTROLLER_TYPE_PCI:
-
- /* pSeries guests can have multiple pci-root controllers,
- * but other machine types only support a single one */
- if (!qemuDomainIsPSeries(def) &&
- (cont->model == VIR_DOMAIN_CONTROLLER_MODEL_PCI_ROOT ||
- cont->model == VIR_DOMAIN_CONTROLLER_MODEL_PCIE_ROOT) &&
- cont->idx != 0) {
- virReportError(VIR_ERR_XML_ERROR, "%s",
- _("pci-root and pcie-root controllers should have index 0"));
- return -1;
- }
-
- if (cont->model == VIR_DOMAIN_CONTROLLER_MODEL_PCI_EXPANDER_BUS &&
- !qemuDomainIsI440FX(def)) {
- virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
- _("pci-expander-bus controllers are only supported on 440fx-based machinetypes"));
- return -1;
- }
- if (cont->model == VIR_DOMAIN_CONTROLLER_MODEL_PCIE_EXPANDER_BUS &&
- !(qemuDomainIsQ35(def) || qemuDomainIsARMVirt(def))) {
- virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
- _("pcie-expander-bus controllers are not supported with this machine type"));
- return -1;
- }
-
- /* if a PCI expander bus or pci-root on Pseries has a NUMA node
- * set, make sure that NUMA node is configured in the guest
- * <cpu><numa> array. NUMA cell id's in this array are numbered
- * from 0 .. size-1.
- */
- if (cont->opts.pciopts.numaNode >= 0 &&
- cont->opts.pciopts.numaNode >=
- (int)virDomainNumaGetNodeCount(def->numa)) {
- virReportError(VIR_ERR_XML_ERROR,
- _("%1$s with index %2$d is configured for a NUMA node (%3$d) not present in the domain's <cpu><numa> array (%4$zu)"),
- virDomainControllerModelPCITypeToString(cont->model),
- cont->idx, cont->opts.pciopts.numaNode,
- virDomainNumaGetNodeCount(def->numa));
- return -1;
- }
- break;
-
- case VIR_DOMAIN_CONTROLLER_TYPE_SATA:
- case VIR_DOMAIN_CONTROLLER_TYPE_VIRTIO_SERIAL:
- case VIR_DOMAIN_CONTROLLER_TYPE_CCID:
- case VIR_DOMAIN_CONTROLLER_TYPE_IDE:
- case VIR_DOMAIN_CONTROLLER_TYPE_FDC:
- case VIR_DOMAIN_CONTROLLER_TYPE_XENBUS:
- case VIR_DOMAIN_CONTROLLER_TYPE_ISA:
- case VIR_DOMAIN_CONTROLLER_TYPE_LAST:
- break;
- }
-
- return 0;
-}
-
-static int
-qemuDomainChrDefPostParse(virDomainChrDef *chr,
- const virDomainDef *def,
- virQEMUDriver *driver,
- unsigned int parseFlags)
-{
- /* Historically, isa-serial and the default matched, so in order to
- * maintain backwards compatibility we map them here. The actual default
- * will be picked below based on the architecture and machine type. */
- if (chr->deviceType == VIR_DOMAIN_CHR_DEVICE_TYPE_SERIAL &&
- chr->targetType == VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_ISA) {
- chr->targetType = VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_NONE;
- }
-
- /* Set the default serial type */
- if (chr->deviceType == VIR_DOMAIN_CHR_DEVICE_TYPE_SERIAL &&
- chr->targetType == VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_NONE) {
- if (ARCH_IS_X86(def->os.arch)) {
- chr->targetType = VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_ISA;
- } else if (qemuDomainIsPSeries(def)) {
- chr->targetType = VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_SPAPR_VIO;
- } else if (qemuDomainIsARMVirt(def) ||
- qemuDomainIsLoongArchVirt(def) ||
- qemuDomainIsRISCVVirt(def)) {
- chr->targetType = VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_SYSTEM;
- } else if (ARCH_IS_S390(def->os.arch)) {
- chr->targetType = VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_SCLP;
- }
- }
-
- /* Set the default target model */
- if (chr->deviceType == VIR_DOMAIN_CHR_DEVICE_TYPE_SERIAL &&
- chr->targetModel == VIR_DOMAIN_CHR_SERIAL_TARGET_MODEL_NONE) {
- switch ((virDomainChrSerialTargetType)chr->targetType) {
- case VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_ISA:
- chr->targetModel = VIR_DOMAIN_CHR_SERIAL_TARGET_MODEL_ISA_SERIAL;
- break;
- case VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_USB:
- chr->targetModel = VIR_DOMAIN_CHR_SERIAL_TARGET_MODEL_USB_SERIAL;
- break;
- case VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_PCI:
- chr->targetModel = VIR_DOMAIN_CHR_SERIAL_TARGET_MODEL_PCI_SERIAL;
- break;
- case VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_SPAPR_VIO:
- chr->targetModel = VIR_DOMAIN_CHR_SERIAL_TARGET_MODEL_SPAPR_VTY;
- break;
- case VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_SYSTEM:
- if (qemuDomainIsARMVirt(def)) {
- chr->targetModel = VIR_DOMAIN_CHR_SERIAL_TARGET_MODEL_PL011;
- } else if (qemuDomainIsLoongArchVirt(def) ||
- qemuDomainIsRISCVVirt(def)) {
- chr->targetModel = VIR_DOMAIN_CHR_SERIAL_TARGET_MODEL_16550A;
- }
- break;
- case VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_SCLP:
- chr->targetModel = VIR_DOMAIN_CHR_SERIAL_TARGET_MODEL_SCLPCONSOLE;
- break;
- case VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_ISA_DEBUG:
- chr->targetModel = VIR_DOMAIN_CHR_SERIAL_TARGET_MODEL_ISA_DEBUGCON;
- break;
- case VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_NONE:
- case VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_LAST:
- /* Nothing to do */
- break;
- }
- }
-
- /* clear auto generated unix socket path for inactive definitions */
- if (parseFlags & VIR_DOMAIN_DEF_PARSE_INACTIVE) {
- qemuDomainChrDefDropDefaultPath(chr, driver);
-
- /* For UNIX chardev if no path is provided we generate one.
- * This also implies that the mode is 'bind'. */
- if (chr->source &&
- chr->source->type == VIR_DOMAIN_CHR_TYPE_UNIX &&
- !chr->source->data.nix.path) {
- chr->source->data.nix.listen = true;
- }
- }
-
- return 0;
-}
-
-
-/**
- * qemuDomainDeviceDiskDefPostParseRestoreSecAlias:
- *
- * Re-generate aliases for objects related to the storage source if they
- * were not stored in the status XML by an older libvirt.
- *
- * Note that qemuCaps should be always present for a status XML.
- */
-static int
-qemuDomainDeviceDiskDefPostParseRestoreSecAlias(virDomainDiskDef *disk,
- unsigned int parseFlags)
-{
- qemuDomainStorageSourcePrivate *priv = QEMU_DOMAIN_STORAGE_SOURCE_PRIVATE(disk->src);
- bool restoreAuthSecret = false;
- bool restoreEncSecret = false;
- g_autofree char *authalias = NULL;
- g_autofree char *encalias = NULL;
-
- if (!(parseFlags & VIR_DOMAIN_DEF_PARSE_STATUS) ||
- virStorageSourceIsEmpty(disk->src))
- return 0;
-
- /* network storage authentication secret */
- if (disk->src->auth &&
- (!priv || !priv->secinfo)) {
-
- /* only RBD and iSCSI (with capability) were supporting authentication
- * using secret object at the time we did not format the alias into the
- * status XML */
- if (virStorageSourceGetActualType(disk->src) == VIR_STORAGE_TYPE_NETWORK &&
- (disk->src->protocol == VIR_STORAGE_NET_PROTOCOL_RBD ||
- disk->src->protocol == VIR_STORAGE_NET_PROTOCOL_ISCSI))
- restoreAuthSecret = true;
- }
-
- /* disk encryption secret */
- if (disk->src->encryption &&
- disk->src->encryption->format == VIR_STORAGE_ENCRYPTION_FORMAT_LUKS &&
- (!priv || !priv->encinfo))
- restoreEncSecret = true;
-
- if (!restoreAuthSecret && !restoreEncSecret)
- return 0;
-
- if (!priv) {
- if (!(disk->src->privateData = qemuDomainStorageSourcePrivateNew()))
- return -1;
-
- priv = QEMU_DOMAIN_STORAGE_SOURCE_PRIVATE(disk->src);
- }
-
- if (restoreAuthSecret) {
- authalias = g_strdup_printf("%s-secret0", disk->info.alias);
-
- if (qemuStorageSourcePrivateDataAssignSecinfo(&priv->secinfo, &authalias) < 0)
- return -1;
- }
-
- if (restoreEncSecret) {
- if (!priv->encinfo) {
- priv->enccount = 1;
- priv->encinfo = g_new0(qemuDomainSecretInfo *, 1);
- }
-
- encalias = g_strdup_printf("%s-luks-secret0", disk->info.alias);
-
- if (qemuStorageSourcePrivateDataAssignSecinfo(&priv->encinfo[0], &encalias) < 0)
- return -1;
- }
-
- return 0;
-}
-
-
int
-qemuDomainDeviceDiskDefPostParse(virDomainDiskDef *disk,
- unsigned int parseFlags)
-{
- virStorageSource *n;
-
- /* set default disk types and drivers */
- if (!virDomainDiskGetDriver(disk))
- virDomainDiskSetDriver(disk, "qemu");
-
- /* default disk format for drives */
- if (virDomainDiskGetFormat(disk) == VIR_STORAGE_FILE_NONE &&
- virDomainDiskGetType(disk) != VIR_STORAGE_TYPE_VOLUME)
- virDomainDiskSetFormat(disk, VIR_STORAGE_FILE_RAW);
-
- /* default disk format for mirrored drive */
- if (disk->mirror &&
- disk->mirror->format == VIR_STORAGE_FILE_NONE)
- disk->mirror->format = VIR_STORAGE_FILE_RAW;
-
- /* default disk encryption engine */
- for (n = disk->src; virStorageSourceIsBacking(n); n = n->backingStore) {
- if (n->encryption && n->encryption->engine == VIR_STORAGE_ENCRYPTION_ENGINE_DEFAULT)
- n->encryption->engine = VIR_STORAGE_ENCRYPTION_ENGINE_QEMU;
- }
-
- if (qemuDomainDeviceDiskDefPostParseRestoreSecAlias(disk, parseFlags) < 0)
- return -1;
-
- /* regenerate TLS alias for old status XMLs */
- if (parseFlags & VIR_DOMAIN_DEF_PARSE_STATUS &&
- disk->src->haveTLS == VIR_TRISTATE_BOOL_YES &&
- !disk->src->tlsAlias &&
- !(disk->src->tlsAlias = qemuAliasTLSObjFromSrcAlias(disk->info.alias)))
- return -1;
-
- return 0;
-}
-
-
-static int
-qemuDomainDeviceNetDefPostParse(virDomainNetDef *net,
- const virDomainDef *def,
- virQEMUCaps *qemuCaps)
-{
- if (net->type == VIR_DOMAIN_NET_TYPE_VDPA &&
- !virDomainNetGetModelString(net)) {
- net->model = VIR_DOMAIN_NET_MODEL_VIRTIO;
- } else if (net->type != VIR_DOMAIN_NET_TYPE_HOSTDEV &&
- !virDomainNetGetModelString(net) &&
- virDomainNetResolveActualType(net) != VIR_DOMAIN_NET_TYPE_HOSTDEV) {
- net->model = qemuDomainDefaultNetModel(def, qemuCaps);
- }
-
- if (net->type == VIR_DOMAIN_NET_TYPE_USER &&
- net->backend.type == VIR_DOMAIN_NET_BACKEND_DEFAULT) {
- virDomainCapsDeviceNet netCaps = { };
-
- virQEMUCapsFillDomainDeviceNetCaps(qemuCaps, &netCaps);
-
- if (!VIR_DOMAIN_CAPS_ENUM_IS_SET(netCaps.backendType, VIR_DOMAIN_NET_BACKEND_DEFAULT) &&
- VIR_DOMAIN_CAPS_ENUM_IS_SET(netCaps.backendType, VIR_DOMAIN_NET_BACKEND_PASST)) {
- net->backend.type = VIR_DOMAIN_NET_BACKEND_PASST;
- }
- }
-
- return 0;
-}
-
-
-static int
-qemuDomainDefaultVideoDevice(const virDomainDef *def,
- virQEMUCaps *qemuCaps)
-{
- if (ARCH_IS_PPC64(def->os.arch))
- return VIR_DOMAIN_VIDEO_TYPE_VGA;
- if (qemuDomainIsARMVirt(def) ||
- qemuDomainIsLoongArchVirt(def) ||
- qemuDomainIsRISCVVirt(def) ||
- ARCH_IS_S390(def->os.arch)) {
- return VIR_DOMAIN_VIDEO_TYPE_VIRTIO;
- }
- if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_CIRRUS_VGA))
- return VIR_DOMAIN_VIDEO_TYPE_CIRRUS;
- if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_VGA))
- return VIR_DOMAIN_VIDEO_TYPE_VGA;
- return VIR_DOMAIN_VIDEO_TYPE_DEFAULT;
-}
-
-
-static int
-qemuDomainDeviceVideoDefPostParse(virDomainVideoDef *video,
- const virDomainDef *def,
- virQEMUCaps *qemuCaps)
-{
- if (video->type == VIR_DOMAIN_VIDEO_TYPE_DEFAULT)
- video->type = qemuDomainDefaultVideoDevice(def, qemuCaps);
-
- if (video->type == VIR_DOMAIN_VIDEO_TYPE_QXL &&
- !video->vgamem) {
- video->vgamem = QEMU_QXL_VGAMEM_DEFAULT;
- }
-
- return 0;
-}
-
-
-static int
-qemuDomainDevicePanicDefPostParse(virDomainPanicDef *panic,
- const virDomainDef *def)
-{
- if (panic->model == VIR_DOMAIN_PANIC_MODEL_DEFAULT)
- panic->model = qemuDomainDefaultPanicModel(def);
-
- return 0;
-}
-
-
-static int
-qemuDomainVsockDefPostParse(virDomainVsockDef *vsock)
-{
- if (vsock->model == VIR_DOMAIN_VSOCK_MODEL_DEFAULT)
- vsock->model = VIR_DOMAIN_VSOCK_MODEL_VIRTIO;
-
- return 0;
-}
-
-
-/**
- * qemuDomainDeviceHostdevDefPostParseRestoreSecAlias:
- *
- * Re-generate aliases for objects related to the storage source if they
- * were not stored in the status XML by an older libvirt.
- *
- * Note that qemuCaps should be always present for a status XML.
- */
-static int
-qemuDomainDeviceHostdevDefPostParseRestoreSecAlias(virDomainHostdevDef *hostdev,
- unsigned int parseFlags)
-{
- qemuDomainStorageSourcePrivate *priv;
- virDomainHostdevSubsysSCSI *scsisrc = &hostdev->source.subsys.u.scsi;
- virDomainHostdevSubsysSCSIiSCSI *iscsisrc = &scsisrc->u.iscsi;
- g_autofree char *authalias = NULL;
-
- if (!(parseFlags & VIR_DOMAIN_DEF_PARSE_STATUS))
- return 0;
-
- if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS ||
- hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI ||
- scsisrc->protocol != VIR_DOMAIN_HOSTDEV_SCSI_PROTOCOL_TYPE_ISCSI ||
- !iscsisrc->src->auth)
- return 0;
-
- if (!(priv = qemuDomainStorageSourcePrivateFetch(iscsisrc->src)))
- return -1;
-
- if (priv->secinfo)
- return 0;
-
- authalias = g_strdup_printf("%s-secret0", hostdev->info->alias);
-
- if (qemuStorageSourcePrivateDataAssignSecinfo(&priv->secinfo, &authalias) < 0)
- return -1;
-
- return 0;
-}
-
-
-/**
- * qemuDomainDeviceHostdevDefPostParseRestoreBackendAlias:
- *
- * Re-generate backend alias if it wasn't stored in the status XML by an older
- * libvirtd.
- *
- * Note that qemuCaps should be always present for a status XML.
- */
-static int
-qemuDomainDeviceHostdevDefPostParseRestoreBackendAlias(virDomainHostdevDef *hostdev,
- unsigned int parseFlags)
-{
- virDomainHostdevSubsysSCSI *scsisrc = &hostdev->source.subsys.u.scsi;
- virStorageSource *src;
-
- if (!(parseFlags & VIR_DOMAIN_DEF_PARSE_STATUS))
- return 0;
-
- if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS ||
- hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI)
- return 0;
-
- switch (scsisrc->protocol) {
- case VIR_DOMAIN_HOSTDEV_SCSI_PROTOCOL_TYPE_NONE:
- if (!scsisrc->u.host.src)
- scsisrc->u.host.src = virStorageSourceNew();
-
- src = scsisrc->u.host.src;
- break;
-
- case VIR_DOMAIN_HOSTDEV_SCSI_PROTOCOL_TYPE_ISCSI:
- src = scsisrc->u.iscsi.src;
- break;
-
- case VIR_DOMAIN_HOSTDEV_SCSI_PROTOCOL_TYPE_LAST:
- default:
- virReportEnumRangeError(virDomainHostdevSCSIProtocolType, scsisrc->protocol);
- return -1;
- }
-
- if (!qemuBlockStorageSourceGetStorageNodename(src))
- qemuBlockStorageSourceSetStorageNodename(src, g_strdup_printf("libvirt-%s-backend", hostdev->info->alias));
-
- return 0;
-}
-
-
-static int
-qemuDomainHostdevDefMdevPostParse(virDomainHostdevSubsysMediatedDev *mdevsrc)
-{
- /* QEMU 2.12 added support for vfio-pci display type, we default to
- * 'display=off' to stay safe from future changes */
- if (mdevsrc->model == VIR_MDEV_MODEL_TYPE_VFIO_PCI &&
- mdevsrc->display == VIR_TRISTATE_SWITCH_ABSENT)
- mdevsrc->display = VIR_TRISTATE_SWITCH_OFF;
-
- return 0;
-}
-
-
-static int
-qemuDomainHostdevDefPostParse(virDomainHostdevDef *hostdev,
- unsigned int parseFlags)
-{
- virDomainHostdevSubsys *subsys = &hostdev->source.subsys;
-
- if (qemuDomainDeviceHostdevDefPostParseRestoreSecAlias(hostdev, parseFlags) < 0)
- return -1;
-
- if (qemuDomainDeviceHostdevDefPostParseRestoreBackendAlias(hostdev, parseFlags) < 0)
- return -1;
-
- if (hostdev->mode == VIR_DOMAIN_HOSTDEV_MODE_SUBSYS &&
- hostdev->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_MDEV &&
- qemuDomainHostdevDefMdevPostParse(&subsys->u.mdev) < 0)
- return -1;
-
- return 0;
-}
-
-
-static int
-qemuDomainTPMDefPostParse(virDomainTPMDef *tpm,
- const virDomainDef *def)
-{
- if (tpm->model == VIR_DOMAIN_TPM_MODEL_DEFAULT) {
- if (ARCH_IS_PPC64(def->os.arch))
- tpm->model = VIR_DOMAIN_TPM_MODEL_SPAPR;
- else
- tpm->model = VIR_DOMAIN_TPM_MODEL_TIS;
- }
-
- /* TPM 1.2 and 2 are not compatible, so we choose a specific version here */
- if (tpm->type == VIR_DOMAIN_TPM_TYPE_EMULATOR &&
- tpm->data.emulator.version == VIR_DOMAIN_TPM_VERSION_DEFAULT) {
- /* tpm-tis on x86 defaults to TPM 1.2 to preserve the
- * historical behavior, but in all other scenarios we want
- * TPM 2.0 instead */
- if (tpm->model == VIR_DOMAIN_TPM_MODEL_TIS &&
- ARCH_IS_X86(def->os.arch)) {
- tpm->data.emulator.version = VIR_DOMAIN_TPM_VERSION_1_2;
- } else {
- tpm->data.emulator.version = VIR_DOMAIN_TPM_VERSION_2_0;
- }
- }
-
- return 0;
-}
-
-
-static int
qemuDomainNVDimmAlignSizePseries(virDomainMemoryDef *mem)
{
/* For NVDIMMs in ppc64 in we want to align down the guest
@@ -6418,194 +4743,6 @@ qemuDomainNVDimmAlignSizePseries(virDomainMemoryDef *mem)
}
-static int
-qemuDomainMemoryDefPostParse(virDomainMemoryDef *mem, virArch arch,
- unsigned int parseFlags)
-{
- /* Memory alignment can't be done for migration or snapshot
- * scenarios. This logic was defined by commit c7d7ba85a624.
- *
- * There is no easy way to replicate at this point the same conditions
- * used to call qemuDomainAlignMemorySizes(), which means checking if
- * we're not migrating and not in a snapshot.
- *
- * We can use the PARSE_ABI_UPDATE flag, which is more strict -
- * existing guests will not activate the flag to avoid breaking
- * boot ABI. This means that any alignment done here will be replicated
- * later on by qemuDomainAlignMemorySizes() to contemplate existing
- * guests as well. */
- if (parseFlags & VIR_DOMAIN_DEF_PARSE_ABI_UPDATE) {
- if (ARCH_IS_PPC64(arch)) {
- unsigned long long ppc64MemModuleAlign = 256 * 1024;
-
- if (mem->model == VIR_DOMAIN_MEMORY_MODEL_NVDIMM) {
- if (qemuDomainNVDimmAlignSizePseries(mem) < 0)
- return -1;
- } else {
- mem->size = VIR_ROUND_UP(mem->size, ppc64MemModuleAlign);
- }
- }
- }
-
- return 0;
-}
-
-
-static int
-qemuDomainPstoreDefPostParse(virDomainPstoreDef *pstore,
- const virDomainDef *def,
- virQEMUDriver *driver)
-{
- g_autoptr(virQEMUDriverConfig) cfg = virQEMUDriverGetConfig(driver);
-
- switch (pstore->backend) {
- case VIR_DOMAIN_PSTORE_BACKEND_ACPI_ERST:
- if (!pstore->path)
- pstore->path = g_strdup_printf("%s/%s_PSTORE.raw",
- cfg->nvramDir, def->name);
- break;
-
- case VIR_DOMAIN_PSTORE_BACKEND_LAST:
- break;
- }
-
- return 0;
-}
-
-
-static int
-qemuDomainIOMMUDefPostParse(virDomainIOMMUDef *iommu,
- const virDomainDef *def,
- virQEMUCaps *qemuCaps,
- unsigned int parseFlags)
-{
- /* In case domain has huge number of vCPUS and Extended Interrupt Mode
- * (EIM) is not explicitly turned off, let's enable it. If we didn't then
- * guest will have troubles with interrupts. */
- if (parseFlags & VIR_DOMAIN_DEF_PARSE_ABI_UPDATE &&
- ARCH_IS_X86(def->os.arch) &&
- virDomainDefGetVcpusMax(def) > QEMU_MAX_VCPUS_WITHOUT_EIM &&
- qemuDomainIsQ35(def) &&
- iommu && iommu->model == VIR_DOMAIN_IOMMU_MODEL_INTEL) {
-
- /* eim requires intremap. */
- if (iommu->intremap == VIR_TRISTATE_SWITCH_ABSENT &&
- virQEMUCapsGet(qemuCaps, QEMU_CAPS_INTEL_IOMMU_INTREMAP)) {
- iommu->intremap = VIR_TRISTATE_SWITCH_ON;
- }
-
- if (iommu->eim == VIR_TRISTATE_SWITCH_ABSENT &&
- virQEMUCapsGet(qemuCaps, QEMU_CAPS_INTEL_IOMMU_EIM)) {
- iommu->eim = VIR_TRISTATE_SWITCH_ON;
- }
- }
-
- return 0;
-}
-
-
-static int
-qemuDomainDeviceDefPostParse(virDomainDeviceDef *dev,
- const virDomainDef *def,
- unsigned int parseFlags,
- void *opaque,
- void *parseOpaque)
-{
- virQEMUDriver *driver = opaque;
- /* Note that qemuCaps may be NULL when this function is called. This
- * function shall not fail in that case. It will be re-run on VM startup
- * with the capabilities populated. */
- virQEMUCaps *qemuCaps = parseOpaque;
- int ret = -1;
-
- switch (dev->type) {
- case VIR_DOMAIN_DEVICE_NET:
- ret = qemuDomainDeviceNetDefPostParse(dev->data.net, def, qemuCaps);
- break;
-
- case VIR_DOMAIN_DEVICE_DISK:
- ret = qemuDomainDeviceDiskDefPostParse(dev->data.disk, parseFlags);
- break;
-
- case VIR_DOMAIN_DEVICE_VIDEO:
- ret = qemuDomainDeviceVideoDefPostParse(dev->data.video, def, qemuCaps);
- break;
-
- case VIR_DOMAIN_DEVICE_PANIC:
- ret = qemuDomainDevicePanicDefPostParse(dev->data.panic, def);
- break;
-
- case VIR_DOMAIN_DEVICE_CONTROLLER:
- ret = qemuDomainControllerDefPostParse(dev->data.controller, def,
- qemuCaps, parseFlags);
- break;
-
- case VIR_DOMAIN_DEVICE_SHMEM:
- ret = qemuDomainShmemDefPostParse(dev->data.shmem);
- break;
-
- case VIR_DOMAIN_DEVICE_CHR:
- ret = qemuDomainChrDefPostParse(dev->data.chr, def, driver, parseFlags);
- break;
-
- case VIR_DOMAIN_DEVICE_VSOCK:
- ret = qemuDomainVsockDefPostParse(dev->data.vsock);
- break;
-
- case VIR_DOMAIN_DEVICE_HOSTDEV:
- ret = qemuDomainHostdevDefPostParse(dev->data.hostdev, parseFlags);
- break;
-
- case VIR_DOMAIN_DEVICE_TPM:
- ret = qemuDomainTPMDefPostParse(dev->data.tpm, def);
- break;
-
- case VIR_DOMAIN_DEVICE_MEMORY:
- ret = qemuDomainMemoryDefPostParse(dev->data.memory, def->os.arch,
- parseFlags);
- break;
-
- case VIR_DOMAIN_DEVICE_PSTORE:
- ret = qemuDomainPstoreDefPostParse(dev->data.pstore, def, driver);
- break;
-
- case VIR_DOMAIN_DEVICE_IOMMU:
- ret = qemuDomainIOMMUDefPostParse(dev->data.iommu, def,
- qemuCaps, parseFlags);
- break;
-
- case VIR_DOMAIN_DEVICE_LEASE:
- case VIR_DOMAIN_DEVICE_FS:
- case VIR_DOMAIN_DEVICE_INPUT:
- case VIR_DOMAIN_DEVICE_SOUND:
- case VIR_DOMAIN_DEVICE_WATCHDOG:
- case VIR_DOMAIN_DEVICE_GRAPHICS:
- case VIR_DOMAIN_DEVICE_HUB:
- case VIR_DOMAIN_DEVICE_REDIRDEV:
- case VIR_DOMAIN_DEVICE_SMARTCARD:
- case VIR_DOMAIN_DEVICE_MEMBALLOON:
- case VIR_DOMAIN_DEVICE_NVRAM:
- case VIR_DOMAIN_DEVICE_RNG:
- case VIR_DOMAIN_DEVICE_AUDIO:
- case VIR_DOMAIN_DEVICE_CRYPTO:
- ret = 0;
- break;
-
- case VIR_DOMAIN_DEVICE_NONE:
- virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
- _("unexpected VIR_DOMAIN_DEVICE_NONE"));
- break;
-
- case VIR_DOMAIN_DEVICE_LAST:
- default:
- virReportEnumRangeError(virDomainDeviceType, dev->type);
- break;
- }
-
- return ret;
-}
-
-
static int
qemuDomainDefAssignAddresses(virDomainDef *def,
unsigned int parseFlags G_GNUC_UNUSED,
@@ -6630,31 +4767,6 @@ qemuDomainDefAssignAddresses(virDomainDef *def,
}
-static int
-qemuDomainPostParseDataAlloc(const virDomainDef *def,
- unsigned int parseFlags G_GNUC_UNUSED,
- void *opaque,
- void **parseOpaque)
-{
- virQEMUDriver *driver = opaque;
-
- if (!(*parseOpaque = virQEMUCapsCacheLookup(driver->qemuCapsCache,
- def->emulator)))
- return 1;
-
- return 0;
-}
-
-
-static void
-qemuDomainPostParseDataFree(void *parseOpaque)
-{
- virQEMUCaps *qemuCaps = parseOpaque;
-
- virObjectUnref(qemuCaps);
-}
-
-
virDomainDefParserConfig virQEMUDriverDomainDefParserConfig = {
.domainPostParseBasicCallback = qemuDomainDefPostParseBasic,
.domainPostParseDataAlloc = qemuDomainPostParseDataAlloc,
diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h
index 047a11b7fe..1ae421e5f2 100644
--- a/src/qemu/qemu_domain.h
+++ b/src/qemu/qemu_domain.h
@@ -780,6 +780,9 @@ void qemuDomainCleanupRun(virQEMUDriver *driver,
void qemuDomainObjPrivateDataClear(qemuDomainObjPrivate *priv);
+int qemuStorageSourcePrivateDataAssignSecinfo(qemuDomainSecretInfo **secinfo,
+ char **alias);
+
extern virDomainXMLPrivateDataCallbacks virQEMUDriverPrivateDataCallbacks;
extern virXMLNamespace virQEMUDriverDomainXMLNamespace;
extern virDomainDefParserConfig virQEMUDriverDomainDefParserConfig;
@@ -849,6 +852,11 @@ int qemuDomainGetSCSIControllerModel(const virDomainDef *def,
const virDomainControllerDef *cont,
virQEMUCaps *qemuCaps);
+int qemuDomainDefAddDefaultAudioBackend(virQEMUDriver *driver,
+ virDomainDef *def);
+
+virDomainPanicModel qemuDomainDefaultPanicModel(const virDomainDef *def);
+
void qemuDomainUpdateCurrentMemorySize(virDomainObj *vm);
unsigned long long qemuDomainGetMemLockLimitBytes(virDomainDef *def);
@@ -938,8 +946,12 @@ int qemuDomainSecretPrepare(virQEMUDriver *driver,
virDomainObj *vm)
ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2);
-int qemuDomainDeviceDiskDefPostParse(virDomainDiskDef *disk,
- unsigned int parseFlags);
+void
+qemuDomainChrDefDropDefaultPath(virDomainChrDef *chr,
+ virQEMUDriver *driver);
+
+int
+qemuDomainNVDimmAlignSizePseries(virDomainMemoryDef *mem);
int qemuDomainPrepareChannel(virDomainChrDef *chr,
const char *domainChannelTargetDir)
diff --git a/src/qemu/qemu_postparse.c b/src/qemu/qemu_postparse.c
new file mode 100644
index 0000000000..11134fb030
--- /dev/null
+++ b/src/qemu/qemu_postparse.c
@@ -0,0 +1,1925 @@
+/*
+ * qemu_postparse.c: QEMU domain PostParse functions
+ *
+ * Copyright (C) 2006-2024 Red Hat, Inc.
+ * Copyright (C) 2006 Daniel P. Berrange
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+
+#include "qemu_postparse.h"
+#include "qemu_alias.h"
+#include "qemu_block.h"
+#include "qemu_domain.h"
+#include "qemu_firmware.h"
+#include "qemu_security.h"
+#include "qemu_validate.h"
+#include "domain_conf.h"
+#include "viralloc.h"
+#include "virlog.h"
+
+#define QEMU_QXL_VGAMEM_DEFAULT 16 * 1024
+
+#define VIR_FROM_THIS VIR_FROM_QEMU
+
+VIR_LOG_INIT("qemu.qemu_postparse");
+
+
+/**
+ * qemuDomainDefaultNetModel:
+ * @def: domain definition
+ * @qemuCaps: qemu capabilities
+ *
+ * Returns the default network model for a given domain. Note that if @qemuCaps
+ * is NULL this function may return NULL if the default model depends on the
+ * capabilities.
+ */
+static int
+qemuDomainDefaultNetModel(const virDomainDef *def,
+ virQEMUCaps *qemuCaps)
+{
+ /* When there are no backwards compatibility concerns getting in
+ * the way, virtio is a good default */
+ if (ARCH_IS_S390(def->os.arch) ||
+ qemuDomainIsLoongArchVirt(def) ||
+ qemuDomainIsRISCVVirt(def)) {
+ return VIR_DOMAIN_NET_MODEL_VIRTIO;
+ }
+
+ if (ARCH_IS_ARM(def->os.arch)) {
+ if (STREQ(def->os.machine, "versatilepb"))
+ return VIR_DOMAIN_NET_MODEL_SMC91C111;
+
+ if (qemuDomainIsARMVirt(def))
+ return VIR_DOMAIN_NET_MODEL_VIRTIO;
+
+ /* Incomplete. vexpress (and a few others) use this, but not all
+ * arm boards */
+ return VIR_DOMAIN_NET_MODEL_LAN9118;
+ }
+
+ /* In all other cases the model depends on the capabilities. If they were
+ * not provided don't report any default. */
+ if (!qemuCaps)
+ return VIR_DOMAIN_NET_MODEL_UNKNOWN;
+
+ /* Try several network devices in turn; each of these devices is
+ * less likely be supported out-of-the-box by the guest operating
+ * system than the previous one */
+ if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_RTL8139))
+ return VIR_DOMAIN_NET_MODEL_RTL8139;
+
+ if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_E1000))
+ return VIR_DOMAIN_NET_MODEL_E1000;
+
+ if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_VIRTIO_NET))
+ return VIR_DOMAIN_NET_MODEL_VIRTIO;
+
+ /* We've had no luck detecting support for any network device,
+ * but we have to return something: might as well be rtl8139 */
+ return VIR_DOMAIN_NET_MODEL_RTL8139;
+}
+
+
+static int
+qemuDomainDeviceNetDefPostParse(virDomainNetDef *net,
+ const virDomainDef *def,
+ virQEMUCaps *qemuCaps)
+{
+ if (net->type == VIR_DOMAIN_NET_TYPE_VDPA &&
+ !virDomainNetGetModelString(net)) {
+ net->model = VIR_DOMAIN_NET_MODEL_VIRTIO;
+ } else if (net->type != VIR_DOMAIN_NET_TYPE_HOSTDEV &&
+ !virDomainNetGetModelString(net) &&
+ virDomainNetResolveActualType(net) != VIR_DOMAIN_NET_TYPE_HOSTDEV) {
+ net->model = qemuDomainDefaultNetModel(def, qemuCaps);
+ }
+
+ if (net->type == VIR_DOMAIN_NET_TYPE_USER &&
+ net->backend.type == VIR_DOMAIN_NET_BACKEND_DEFAULT) {
+ virDomainCapsDeviceNet netCaps = { };
+
+ virQEMUCapsFillDomainDeviceNetCaps(qemuCaps, &netCaps);
+
+ if (!VIR_DOMAIN_CAPS_ENUM_IS_SET(netCaps.backendType, VIR_DOMAIN_NET_BACKEND_DEFAULT) &&
+ VIR_DOMAIN_CAPS_ENUM_IS_SET(netCaps.backendType, VIR_DOMAIN_NET_BACKEND_PASST)) {
+ net->backend.type = VIR_DOMAIN_NET_BACKEND_PASST;
+ }
+ }
+
+ return 0;
+}
+
+
+/**
+ * qemuDomainDeviceDiskDefPostParseRestoreSecAlias:
+ *
+ * Re-generate aliases for objects related to the storage source if they
+ * were not stored in the status XML by an older libvirt.
+ *
+ * Note that qemuCaps should be always present for a status XML.
+ */
+static int
+qemuDomainDeviceDiskDefPostParseRestoreSecAlias(virDomainDiskDef *disk,
+ unsigned int parseFlags)
+{
+ qemuDomainStorageSourcePrivate *priv = QEMU_DOMAIN_STORAGE_SOURCE_PRIVATE(disk->src);
+ bool restoreAuthSecret = false;
+ bool restoreEncSecret = false;
+ g_autofree char *authalias = NULL;
+ g_autofree char *encalias = NULL;
+
+ if (!(parseFlags & VIR_DOMAIN_DEF_PARSE_STATUS) ||
+ virStorageSourceIsEmpty(disk->src))
+ return 0;
+
+ /* network storage authentication secret */
+ if (disk->src->auth &&
+ (!priv || !priv->secinfo)) {
+
+ /* only RBD and iSCSI (with capability) were supporting authentication
+ * using secret object at the time we did not format the alias into the
+ * status XML */
+ if (virStorageSourceGetActualType(disk->src) == VIR_STORAGE_TYPE_NETWORK &&
+ (disk->src->protocol == VIR_STORAGE_NET_PROTOCOL_RBD ||
+ disk->src->protocol == VIR_STORAGE_NET_PROTOCOL_ISCSI))
+ restoreAuthSecret = true;
+ }
+
+ /* disk encryption secret */
+ if (disk->src->encryption &&
+ disk->src->encryption->format == VIR_STORAGE_ENCRYPTION_FORMAT_LUKS &&
+ (!priv || !priv->encinfo))
+ restoreEncSecret = true;
+
+ if (!restoreAuthSecret && !restoreEncSecret)
+ return 0;
+
+ if (!priv) {
+ if (!(disk->src->privateData = qemuDomainStorageSourcePrivateNew()))
+ return -1;
+
+ priv = QEMU_DOMAIN_STORAGE_SOURCE_PRIVATE(disk->src);
+ }
+
+ if (restoreAuthSecret) {
+ authalias = g_strdup_printf("%s-secret0", disk->info.alias);
+
+ if (qemuStorageSourcePrivateDataAssignSecinfo(&priv->secinfo, &authalias) < 0)
+ return -1;
+ }
+
+ if (restoreEncSecret) {
+ if (!priv->encinfo) {
+ priv->enccount = 1;
+ priv->encinfo = g_new0(qemuDomainSecretInfo *, 1);
+ }
+
+ encalias = g_strdup_printf("%s-luks-secret0", disk->info.alias);
+
+ if (qemuStorageSourcePrivateDataAssignSecinfo(&priv->encinfo[0], &encalias) < 0)
+ return -1;
+ }
+
+ return 0;
+}
+
+
+int
+qemuDomainDeviceDiskDefPostParse(virDomainDiskDef *disk,
+ unsigned int parseFlags)
+{
+ virStorageSource *n;
+
+ /* set default disk types and drivers */
+ if (!virDomainDiskGetDriver(disk))
+ virDomainDiskSetDriver(disk, "qemu");
+
+ /* default disk format for drives */
+ if (virDomainDiskGetFormat(disk) == VIR_STORAGE_FILE_NONE &&
+ virDomainDiskGetType(disk) != VIR_STORAGE_TYPE_VOLUME)
+ virDomainDiskSetFormat(disk, VIR_STORAGE_FILE_RAW);
+
+ /* default disk format for mirrored drive */
+ if (disk->mirror &&
+ disk->mirror->format == VIR_STORAGE_FILE_NONE)
+ disk->mirror->format = VIR_STORAGE_FILE_RAW;
+
+ /* default disk encryption engine */
+ for (n = disk->src; virStorageSourceIsBacking(n); n = n->backingStore) {
+ if (n->encryption && n->encryption->engine == VIR_STORAGE_ENCRYPTION_ENGINE_DEFAULT)
+ n->encryption->engine = VIR_STORAGE_ENCRYPTION_ENGINE_QEMU;
+ }
+
+ if (qemuDomainDeviceDiskDefPostParseRestoreSecAlias(disk, parseFlags) < 0)
+ return -1;
+
+ /* regenerate TLS alias for old status XMLs */
+ if (parseFlags & VIR_DOMAIN_DEF_PARSE_STATUS &&
+ disk->src->haveTLS == VIR_TRISTATE_BOOL_YES &&
+ !disk->src->tlsAlias &&
+ !(disk->src->tlsAlias = qemuAliasTLSObjFromSrcAlias(disk->info.alias)))
+ return -1;
+
+ return 0;
+}
+
+
+static int
+qemuDomainDefaultVideoDevice(const virDomainDef *def,
+ virQEMUCaps *qemuCaps)
+{
+ if (ARCH_IS_PPC64(def->os.arch))
+ return VIR_DOMAIN_VIDEO_TYPE_VGA;
+ if (qemuDomainIsARMVirt(def) ||
+ qemuDomainIsLoongArchVirt(def) ||
+ qemuDomainIsRISCVVirt(def) ||
+ ARCH_IS_S390(def->os.arch)) {
+ return VIR_DOMAIN_VIDEO_TYPE_VIRTIO;
+ }
+ if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_CIRRUS_VGA))
+ return VIR_DOMAIN_VIDEO_TYPE_CIRRUS;
+ if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_VGA))
+ return VIR_DOMAIN_VIDEO_TYPE_VGA;
+ return VIR_DOMAIN_VIDEO_TYPE_DEFAULT;
+}
+
+
+static int
+qemuDomainDeviceVideoDefPostParse(virDomainVideoDef *video,
+ const virDomainDef *def,
+ virQEMUCaps *qemuCaps)
+{
+ if (video->type == VIR_DOMAIN_VIDEO_TYPE_DEFAULT)
+ video->type = qemuDomainDefaultVideoDevice(def, qemuCaps);
+
+ if (video->type == VIR_DOMAIN_VIDEO_TYPE_QXL &&
+ !video->vgamem) {
+ video->vgamem = QEMU_QXL_VGAMEM_DEFAULT;
+ }
+
+ return 0;
+}
+
+
+static int
+qemuDomainDevicePanicDefPostParse(virDomainPanicDef *panic,
+ const virDomainDef *def)
+{
+ if (panic->model == VIR_DOMAIN_PANIC_MODEL_DEFAULT)
+ panic->model = qemuDomainDefaultPanicModel(def);
+
+ return 0;
+}
+
+
+#define QEMU_USB_XHCI_MAXPORTS 15
+
+static int
+qemuDomainControllerDefPostParse(virDomainControllerDef *cont,
+ const virDomainDef *def,
+ virQEMUCaps *qemuCaps,
+ unsigned int parseFlags)
+{
+ switch (cont->type) {
+ case VIR_DOMAIN_CONTROLLER_TYPE_SCSI:
+ /* Set the default SCSI controller model if not already set */
+ cont->model = qemuDomainGetSCSIControllerModel(def, cont, qemuCaps);
+
+ if (cont->model < 0)
+ return -1;
+ break;
+
+ case VIR_DOMAIN_CONTROLLER_TYPE_USB:
+ if (cont->model == VIR_DOMAIN_CONTROLLER_MODEL_USB_DEFAULT && qemuCaps) {
+ /* Pick a suitable default model for the USB controller if none
+ * has been selected by the user and we have the qemuCaps for
+ * figuring out which controllers are supported.
+ *
+ * We rely on device availability instead of setting the model
+ * unconditionally because, for some machine types, there's a
+ * chance we will get away with using the legacy USB controller
+ * when the relevant device is not available.
+ *
+ * See qemuBuildControllersCommandLine() */
+
+ /* Default USB controller is piix3-uhci if available. Fall back to
+ * 'pci-ohci' otherwise which is the default for non-x86 machines
+ * which honour -usb */
+ if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_PIIX3_USB_UHCI))
+ cont->model = VIR_DOMAIN_CONTROLLER_MODEL_USB_PIIX3_UHCI;
+ else if (!ARCH_IS_X86(def->os.arch) &&
+ virQEMUCapsGet(qemuCaps, QEMU_CAPS_PCI_OHCI))
+ cont->model = VIR_DOMAIN_CONTROLLER_MODEL_USB_PCI_OHCI;
+
+ if (ARCH_IS_S390(def->os.arch)) {
+ if (cont->info.type == VIR_DOMAIN_DEVICE_ADDRESS_TYPE_NONE) {
+ /* set the default USB model to none for s390 unless an
+ * address is found */
+ cont->model = VIR_DOMAIN_CONTROLLER_MODEL_USB_NONE;
+ }
+ } else if (ARCH_IS_PPC64(def->os.arch)) {
+ /* To not break migration we need to set default USB controller
+ * for ppc64 to pci-ohci if we cannot change ABI of the VM.
+ * The nec-usb-xhci or qemu-xhci controller is used as default
+ * only for newly defined domains or devices. */
+ if ((parseFlags & VIR_DOMAIN_DEF_PARSE_ABI_UPDATE) &&
+ virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_QEMU_XHCI)) {
+ cont->model = VIR_DOMAIN_CONTROLLER_MODEL_USB_QEMU_XHCI;
+ } else if ((parseFlags & VIR_DOMAIN_DEF_PARSE_ABI_UPDATE) &&
+ virQEMUCapsGet(qemuCaps, QEMU_CAPS_NEC_USB_XHCI)) {
+ cont->model = VIR_DOMAIN_CONTROLLER_MODEL_USB_NEC_XHCI;
+ } else if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_PCI_OHCI)) {
+ cont->model = VIR_DOMAIN_CONTROLLER_MODEL_USB_PCI_OHCI;
+ } else {
+ /* Explicitly fallback to legacy USB controller for PPC64. */
+ cont->model = VIR_DOMAIN_CONTROLLER_MODEL_USB_DEFAULT;
+ }
+ } else if (def->os.arch == VIR_ARCH_AARCH64) {
+ if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_QEMU_XHCI))
+ cont->model = VIR_DOMAIN_CONTROLLER_MODEL_USB_QEMU_XHCI;
+ else if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_NEC_USB_XHCI))
+ cont->model = VIR_DOMAIN_CONTROLLER_MODEL_USB_NEC_XHCI;
+ } else if (ARCH_IS_LOONGARCH(def->os.arch)) {
+ if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_QEMU_XHCI))
+ cont->model = VIR_DOMAIN_CONTROLLER_MODEL_USB_QEMU_XHCI;
+ }
+ }
+ /* forbid usb model 'qusb1' and 'qusb2' in this kind of hyperviosr */
+ if (cont->model == VIR_DOMAIN_CONTROLLER_MODEL_USB_QUSB1 ||
+ cont->model == VIR_DOMAIN_CONTROLLER_MODEL_USB_QUSB2) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+ _("USB controller model type 'qusb1' or 'qusb2' is not supported in %1$s"),
+ virDomainVirtTypeToString(def->virtType));
+ return -1;
+ }
+ if ((cont->model == VIR_DOMAIN_CONTROLLER_MODEL_USB_NEC_XHCI ||
+ cont->model == VIR_DOMAIN_CONTROLLER_MODEL_USB_QEMU_XHCI) &&
+ cont->opts.usbopts.ports > QEMU_USB_XHCI_MAXPORTS) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+ _("'%1$s' controller only supports up to '%2$u' ports"),
+ virDomainControllerModelUSBTypeToString(cont->model),
+ QEMU_USB_XHCI_MAXPORTS);
+ return -1;
+ }
+ break;
+
+ case VIR_DOMAIN_CONTROLLER_TYPE_PCI:
+
+ /* pSeries guests can have multiple pci-root controllers,
+ * but other machine types only support a single one */
+ if (!qemuDomainIsPSeries(def) &&
+ (cont->model == VIR_DOMAIN_CONTROLLER_MODEL_PCI_ROOT ||
+ cont->model == VIR_DOMAIN_CONTROLLER_MODEL_PCIE_ROOT) &&
+ cont->idx != 0) {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("pci-root and pcie-root controllers should have index 0"));
+ return -1;
+ }
+
+ if (cont->model == VIR_DOMAIN_CONTROLLER_MODEL_PCI_EXPANDER_BUS &&
+ !qemuDomainIsI440FX(def)) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+ _("pci-expander-bus controllers are only supported on 440fx-based machinetypes"));
+ return -1;
+ }
+ if (cont->model == VIR_DOMAIN_CONTROLLER_MODEL_PCIE_EXPANDER_BUS &&
+ !(qemuDomainIsQ35(def) || qemuDomainIsARMVirt(def))) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+ _("pcie-expander-bus controllers are not supported with this machine type"));
+ return -1;
+ }
+
+ /* if a PCI expander bus or pci-root on Pseries has a NUMA node
+ * set, make sure that NUMA node is configured in the guest
+ * <cpu><numa> array. NUMA cell id's in this array are numbered
+ * from 0 .. size-1.
+ */
+ if (cont->opts.pciopts.numaNode >= 0 &&
+ cont->opts.pciopts.numaNode >=
+ (int)virDomainNumaGetNodeCount(def->numa)) {
+ virReportError(VIR_ERR_XML_ERROR,
+ _("%1$s with index %2$d is configured for a NUMA node (%3$d) not present in the domain's <cpu><numa> array (%4$zu)"),
+ virDomainControllerModelPCITypeToString(cont->model),
+ cont->idx, cont->opts.pciopts.numaNode,
+ virDomainNumaGetNodeCount(def->numa));
+ return -1;
+ }
+ break;
+
+ case VIR_DOMAIN_CONTROLLER_TYPE_SATA:
+ case VIR_DOMAIN_CONTROLLER_TYPE_VIRTIO_SERIAL:
+ case VIR_DOMAIN_CONTROLLER_TYPE_CCID:
+ case VIR_DOMAIN_CONTROLLER_TYPE_IDE:
+ case VIR_DOMAIN_CONTROLLER_TYPE_FDC:
+ case VIR_DOMAIN_CONTROLLER_TYPE_XENBUS:
+ case VIR_DOMAIN_CONTROLLER_TYPE_ISA:
+ case VIR_DOMAIN_CONTROLLER_TYPE_LAST:
+ break;
+ }
+
+ return 0;
+}
+
+
+static int
+qemuDomainShmemDefPostParse(virDomainShmemDef *shm)
+{
+ /* This was the default since the introduction of this device. */
+ if (shm->model != VIR_DOMAIN_SHMEM_MODEL_IVSHMEM_DOORBELL && !shm->size)
+ shm->size = 4 << 20;
+
+ /* Nothing more to check/change for IVSHMEM */
+ if (shm->model == VIR_DOMAIN_SHMEM_MODEL_IVSHMEM)
+ return 0;
+
+ if (!shm->server.enabled) {
+ if (shm->model == VIR_DOMAIN_SHMEM_MODEL_IVSHMEM_DOORBELL) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+ _("shmem model '%1$s' is supported only with server option enabled"),
+ virDomainShmemModelTypeToString(shm->model));
+ return -1;
+ }
+
+ if (shm->msi.enabled) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+ _("shmem model '%1$s' doesn't support msi"),
+ virDomainShmemModelTypeToString(shm->model));
+ }
+ } else {
+ if (shm->model == VIR_DOMAIN_SHMEM_MODEL_IVSHMEM_PLAIN) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+ _("shmem model '%1$s' is supported only with server option disabled"),
+ virDomainShmemModelTypeToString(shm->model));
+ return -1;
+ }
+
+ if (shm->size) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+ _("shmem model '%1$s' does not support size setting"),
+ virDomainShmemModelTypeToString(shm->model));
+ return -1;
+ }
+ shm->msi.enabled = true;
+ if (!shm->msi.ioeventfd)
+ shm->msi.ioeventfd = VIR_TRISTATE_SWITCH_ON;
+ }
+
+ return 0;
+}
+
+
+static int
+qemuDomainChrDefPostParse(virDomainChrDef *chr,
+ const virDomainDef *def,
+ virQEMUDriver *driver,
+ unsigned int parseFlags)
+{
+ /* Historically, isa-serial and the default matched, so in order to
+ * maintain backwards compatibility we map them here. The actual default
+ * will be picked below based on the architecture and machine type. */
+ if (chr->deviceType == VIR_DOMAIN_CHR_DEVICE_TYPE_SERIAL &&
+ chr->targetType == VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_ISA) {
+ chr->targetType = VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_NONE;
+ }
+
+ /* Set the default serial type */
+ if (chr->deviceType == VIR_DOMAIN_CHR_DEVICE_TYPE_SERIAL &&
+ chr->targetType == VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_NONE) {
+ if (ARCH_IS_X86(def->os.arch)) {
+ chr->targetType = VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_ISA;
+ } else if (qemuDomainIsPSeries(def)) {
+ chr->targetType = VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_SPAPR_VIO;
+ } else if (qemuDomainIsARMVirt(def) ||
+ qemuDomainIsLoongArchVirt(def) ||
+ qemuDomainIsRISCVVirt(def)) {
+ chr->targetType = VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_SYSTEM;
+ } else if (ARCH_IS_S390(def->os.arch)) {
+ chr->targetType = VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_SCLP;
+ }
+ }
+
+ /* Set the default target model */
+ if (chr->deviceType == VIR_DOMAIN_CHR_DEVICE_TYPE_SERIAL &&
+ chr->targetModel == VIR_DOMAIN_CHR_SERIAL_TARGET_MODEL_NONE) {
+ switch ((virDomainChrSerialTargetType)chr->targetType) {
+ case VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_ISA:
+ chr->targetModel = VIR_DOMAIN_CHR_SERIAL_TARGET_MODEL_ISA_SERIAL;
+ break;
+ case VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_USB:
+ chr->targetModel = VIR_DOMAIN_CHR_SERIAL_TARGET_MODEL_USB_SERIAL;
+ break;
+ case VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_PCI:
+ chr->targetModel = VIR_DOMAIN_CHR_SERIAL_TARGET_MODEL_PCI_SERIAL;
+ break;
+ case VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_SPAPR_VIO:
+ chr->targetModel = VIR_DOMAIN_CHR_SERIAL_TARGET_MODEL_SPAPR_VTY;
+ break;
+ case VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_SYSTEM:
+ if (qemuDomainIsARMVirt(def)) {
+ chr->targetModel = VIR_DOMAIN_CHR_SERIAL_TARGET_MODEL_PL011;
+ } else if (qemuDomainIsLoongArchVirt(def) ||
+ qemuDomainIsRISCVVirt(def)) {
+ chr->targetModel = VIR_DOMAIN_CHR_SERIAL_TARGET_MODEL_16550A;
+ }
+ break;
+ case VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_SCLP:
+ chr->targetModel = VIR_DOMAIN_CHR_SERIAL_TARGET_MODEL_SCLPCONSOLE;
+ break;
+ case VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_ISA_DEBUG:
+ chr->targetModel = VIR_DOMAIN_CHR_SERIAL_TARGET_MODEL_ISA_DEBUGCON;
+ break;
+ case VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_NONE:
+ case VIR_DOMAIN_CHR_SERIAL_TARGET_TYPE_LAST:
+ /* Nothing to do */
+ break;
+ }
+ }
+
+ /* clear auto generated unix socket path for inactive definitions */
+ if (parseFlags & VIR_DOMAIN_DEF_PARSE_INACTIVE) {
+ qemuDomainChrDefDropDefaultPath(chr, driver);
+
+ /* For UNIX chardev if no path is provided we generate one.
+ * This also implies that the mode is 'bind'. */
+ if (chr->source &&
+ chr->source->type == VIR_DOMAIN_CHR_TYPE_UNIX &&
+ !chr->source->data.nix.path) {
+ chr->source->data.nix.listen = true;
+ }
+ }
+
+ return 0;
+}
+
+
+static int
+qemuDomainVsockDefPostParse(virDomainVsockDef *vsock)
+{
+ if (vsock->model == VIR_DOMAIN_VSOCK_MODEL_DEFAULT)
+ vsock->model = VIR_DOMAIN_VSOCK_MODEL_VIRTIO;
+
+ return 0;
+}
+
+
+/**
+ * qemuDomainDeviceHostdevDefPostParseRestoreSecAlias:
+ *
+ * Re-generate aliases for objects related to the storage source if they
+ * were not stored in the status XML by an older libvirt.
+ *
+ * Note that qemuCaps should be always present for a status XML.
+ */
+static int
+qemuDomainDeviceHostdevDefPostParseRestoreSecAlias(virDomainHostdevDef *hostdev,
+ unsigned int parseFlags)
+{
+ qemuDomainStorageSourcePrivate *priv;
+ virDomainHostdevSubsysSCSI *scsisrc = &hostdev->source.subsys.u.scsi;
+ virDomainHostdevSubsysSCSIiSCSI *iscsisrc = &scsisrc->u.iscsi;
+ g_autofree char *authalias = NULL;
+
+ if (!(parseFlags & VIR_DOMAIN_DEF_PARSE_STATUS))
+ return 0;
+
+ if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS ||
+ hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI ||
+ scsisrc->protocol != VIR_DOMAIN_HOSTDEV_SCSI_PROTOCOL_TYPE_ISCSI ||
+ !iscsisrc->src->auth)
+ return 0;
+
+ if (!(priv = qemuDomainStorageSourcePrivateFetch(iscsisrc->src)))
+ return -1;
+
+ if (priv->secinfo)
+ return 0;
+
+ authalias = g_strdup_printf("%s-secret0", hostdev->info->alias);
+
+ if (qemuStorageSourcePrivateDataAssignSecinfo(&priv->secinfo, &authalias) < 0)
+ return -1;
+
+ return 0;
+}
+
+
+/**
+ * qemuDomainDeviceHostdevDefPostParseRestoreBackendAlias:
+ *
+ * Re-generate backend alias if it wasn't stored in the status XML by an older
+ * libvirtd.
+ *
+ * Note that qemuCaps should be always present for a status XML.
+ */
+static int
+qemuDomainDeviceHostdevDefPostParseRestoreBackendAlias(virDomainHostdevDef *hostdev,
+ unsigned int parseFlags)
+{
+ virDomainHostdevSubsysSCSI *scsisrc = &hostdev->source.subsys.u.scsi;
+ virStorageSource *src;
+
+ if (!(parseFlags & VIR_DOMAIN_DEF_PARSE_STATUS))
+ return 0;
+
+ if (hostdev->mode != VIR_DOMAIN_HOSTDEV_MODE_SUBSYS ||
+ hostdev->source.subsys.type != VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_SCSI)
+ return 0;
+
+ switch (scsisrc->protocol) {
+ case VIR_DOMAIN_HOSTDEV_SCSI_PROTOCOL_TYPE_NONE:
+ if (!scsisrc->u.host.src)
+ scsisrc->u.host.src = virStorageSourceNew();
+
+ src = scsisrc->u.host.src;
+ break;
+
+ case VIR_DOMAIN_HOSTDEV_SCSI_PROTOCOL_TYPE_ISCSI:
+ src = scsisrc->u.iscsi.src;
+ break;
+
+ case VIR_DOMAIN_HOSTDEV_SCSI_PROTOCOL_TYPE_LAST:
+ default:
+ virReportEnumRangeError(virDomainHostdevSCSIProtocolType, scsisrc->protocol);
+ return -1;
+ }
+
+ if (!qemuBlockStorageSourceGetStorageNodename(src))
+ qemuBlockStorageSourceSetStorageNodename(src, g_strdup_printf("libvirt-%s-backend", hostdev->info->alias));
+
+ return 0;
+}
+
+
+static int
+qemuDomainHostdevDefMdevPostParse(virDomainHostdevSubsysMediatedDev *mdevsrc)
+{
+ /* QEMU 2.12 added support for vfio-pci display type, we default to
+ * 'display=off' to stay safe from future changes */
+ if (mdevsrc->model == VIR_MDEV_MODEL_TYPE_VFIO_PCI &&
+ mdevsrc->display == VIR_TRISTATE_SWITCH_ABSENT)
+ mdevsrc->display = VIR_TRISTATE_SWITCH_OFF;
+
+ return 0;
+}
+
+
+static int
+qemuDomainHostdevDefPostParse(virDomainHostdevDef *hostdev,
+ unsigned int parseFlags)
+{
+ virDomainHostdevSubsys *subsys = &hostdev->source.subsys;
+
+ if (qemuDomainDeviceHostdevDefPostParseRestoreSecAlias(hostdev, parseFlags) < 0)
+ return -1;
+
+ if (qemuDomainDeviceHostdevDefPostParseRestoreBackendAlias(hostdev, parseFlags) < 0)
+ return -1;
+
+ if (hostdev->mode == VIR_DOMAIN_HOSTDEV_MODE_SUBSYS &&
+ hostdev->source.subsys.type == VIR_DOMAIN_HOSTDEV_SUBSYS_TYPE_MDEV &&
+ qemuDomainHostdevDefMdevPostParse(&subsys->u.mdev) < 0)
+ return -1;
+
+ return 0;
+}
+
+
+static int
+qemuDomainTPMDefPostParse(virDomainTPMDef *tpm,
+ const virDomainDef *def)
+{
+ if (tpm->model == VIR_DOMAIN_TPM_MODEL_DEFAULT) {
+ if (ARCH_IS_PPC64(def->os.arch))
+ tpm->model = VIR_DOMAIN_TPM_MODEL_SPAPR;
+ else
+ tpm->model = VIR_DOMAIN_TPM_MODEL_TIS;
+ }
+
+ /* TPM 1.2 and 2 are not compatible, so we choose a specific version here */
+ if (tpm->type == VIR_DOMAIN_TPM_TYPE_EMULATOR &&
+ tpm->data.emulator.version == VIR_DOMAIN_TPM_VERSION_DEFAULT) {
+ /* tpm-tis on x86 defaults to TPM 1.2 to preserve the
+ * historical behavior, but in all other scenarios we want
+ * TPM 2.0 instead */
+ if (tpm->model == VIR_DOMAIN_TPM_MODEL_TIS &&
+ ARCH_IS_X86(def->os.arch)) {
+ tpm->data.emulator.version = VIR_DOMAIN_TPM_VERSION_1_2;
+ } else {
+ tpm->data.emulator.version = VIR_DOMAIN_TPM_VERSION_2_0;
+ }
+ }
+
+ return 0;
+}
+
+
+static int
+qemuDomainMemoryDefPostParse(virDomainMemoryDef *mem, virArch arch,
+ unsigned int parseFlags)
+{
+ /* Memory alignment can't be done for migration or snapshot
+ * scenarios. This logic was defined by commit c7d7ba85a624.
+ *
+ * There is no easy way to replicate at this point the same conditions
+ * used to call qemuDomainAlignMemorySizes(), which means checking if
+ * we're not migrating and not in a snapshot.
+ *
+ * We can use the PARSE_ABI_UPDATE flag, which is more strict -
+ * existing guests will not activate the flag to avoid breaking
+ * boot ABI. This means that any alignment done here will be replicated
+ * later on by qemuDomainAlignMemorySizes() to contemplate existing
+ * guests as well. */
+ if (parseFlags & VIR_DOMAIN_DEF_PARSE_ABI_UPDATE) {
+ if (ARCH_IS_PPC64(arch)) {
+ unsigned long long ppc64MemModuleAlign = 256 * 1024;
+
+ if (mem->model == VIR_DOMAIN_MEMORY_MODEL_NVDIMM) {
+ if (qemuDomainNVDimmAlignSizePseries(mem) < 0)
+ return -1;
+ } else {
+ mem->size = VIR_ROUND_UP(mem->size, ppc64MemModuleAlign);
+ }
+ }
+ }
+
+ return 0;
+}
+
+
+static int
+qemuDomainPstoreDefPostParse(virDomainPstoreDef *pstore,
+ const virDomainDef *def,
+ virQEMUDriver *driver)
+{
+ g_autoptr(virQEMUDriverConfig) cfg = virQEMUDriverGetConfig(driver);
+
+ switch (pstore->backend) {
+ case VIR_DOMAIN_PSTORE_BACKEND_ACPI_ERST:
+ if (!pstore->path)
+ pstore->path = g_strdup_printf("%s/%s_PSTORE.raw",
+ cfg->nvramDir, def->name);
+ break;
+
+ case VIR_DOMAIN_PSTORE_BACKEND_LAST:
+ break;
+ }
+
+ return 0;
+}
+
+
+static int
+qemuDomainIOMMUDefPostParse(virDomainIOMMUDef *iommu,
+ const virDomainDef *def,
+ virQEMUCaps *qemuCaps,
+ unsigned int parseFlags)
+{
+ /* In case domain has huge number of vCPUS and Extended Interrupt Mode
+ * (EIM) is not explicitly turned off, let's enable it. If we didn't then
+ * guest will have troubles with interrupts. */
+ if (parseFlags & VIR_DOMAIN_DEF_PARSE_ABI_UPDATE &&
+ ARCH_IS_X86(def->os.arch) &&
+ virDomainDefGetVcpusMax(def) > QEMU_MAX_VCPUS_WITHOUT_EIM &&
+ qemuDomainIsQ35(def) &&
+ iommu && iommu->model == VIR_DOMAIN_IOMMU_MODEL_INTEL) {
+
+ /* eim requires intremap. */
+ if (iommu->intremap == VIR_TRISTATE_SWITCH_ABSENT &&
+ virQEMUCapsGet(qemuCaps, QEMU_CAPS_INTEL_IOMMU_INTREMAP)) {
+ iommu->intremap = VIR_TRISTATE_SWITCH_ON;
+ }
+
+ if (iommu->eim == VIR_TRISTATE_SWITCH_ABSENT &&
+ virQEMUCapsGet(qemuCaps, QEMU_CAPS_INTEL_IOMMU_EIM)) {
+ iommu->eim = VIR_TRISTATE_SWITCH_ON;
+ }
+ }
+
+ return 0;
+}
+
+
+int
+qemuDomainDeviceDefPostParse(virDomainDeviceDef *dev,
+ const virDomainDef *def,
+ unsigned int parseFlags,
+ void *opaque,
+ void *parseOpaque)
+{
+ virQEMUDriver *driver = opaque;
+ /* Note that qemuCaps may be NULL when this function is called. This
+ * function shall not fail in that case. It will be re-run on VM startup
+ * with the capabilities populated. */
+ virQEMUCaps *qemuCaps = parseOpaque;
+ int ret = -1;
+
+ switch (dev->type) {
+ case VIR_DOMAIN_DEVICE_NET:
+ ret = qemuDomainDeviceNetDefPostParse(dev->data.net, def, qemuCaps);
+ break;
+
+ case VIR_DOMAIN_DEVICE_DISK:
+ ret = qemuDomainDeviceDiskDefPostParse(dev->data.disk, parseFlags);
+ break;
+
+ case VIR_DOMAIN_DEVICE_VIDEO:
+ ret = qemuDomainDeviceVideoDefPostParse(dev->data.video, def, qemuCaps);
+ break;
+
+ case VIR_DOMAIN_DEVICE_PANIC:
+ ret = qemuDomainDevicePanicDefPostParse(dev->data.panic, def);
+ break;
+
+ case VIR_DOMAIN_DEVICE_CONTROLLER:
+ ret = qemuDomainControllerDefPostParse(dev->data.controller, def,
+ qemuCaps, parseFlags);
+ break;
+
+ case VIR_DOMAIN_DEVICE_SHMEM:
+ ret = qemuDomainShmemDefPostParse(dev->data.shmem);
+ break;
+
+ case VIR_DOMAIN_DEVICE_CHR:
+ ret = qemuDomainChrDefPostParse(dev->data.chr, def, driver, parseFlags);
+ break;
+
+ case VIR_DOMAIN_DEVICE_VSOCK:
+ ret = qemuDomainVsockDefPostParse(dev->data.vsock);
+ break;
+
+ case VIR_DOMAIN_DEVICE_HOSTDEV:
+ ret = qemuDomainHostdevDefPostParse(dev->data.hostdev, parseFlags);
+ break;
+
+ case VIR_DOMAIN_DEVICE_TPM:
+ ret = qemuDomainTPMDefPostParse(dev->data.tpm, def);
+ break;
+
+ case VIR_DOMAIN_DEVICE_MEMORY:
+ ret = qemuDomainMemoryDefPostParse(dev->data.memory, def->os.arch,
+ parseFlags);
+ break;
+
+ case VIR_DOMAIN_DEVICE_PSTORE:
+ ret = qemuDomainPstoreDefPostParse(dev->data.pstore, def, driver);
+ break;
+
+ case VIR_DOMAIN_DEVICE_IOMMU:
+ ret = qemuDomainIOMMUDefPostParse(dev->data.iommu, def,
+ qemuCaps, parseFlags);
+ break;
+
+ case VIR_DOMAIN_DEVICE_LEASE:
+ case VIR_DOMAIN_DEVICE_FS:
+ case VIR_DOMAIN_DEVICE_INPUT:
+ case VIR_DOMAIN_DEVICE_SOUND:
+ case VIR_DOMAIN_DEVICE_WATCHDOG:
+ case VIR_DOMAIN_DEVICE_GRAPHICS:
+ case VIR_DOMAIN_DEVICE_HUB:
+ case VIR_DOMAIN_DEVICE_REDIRDEV:
+ case VIR_DOMAIN_DEVICE_SMARTCARD:
+ case VIR_DOMAIN_DEVICE_MEMBALLOON:
+ case VIR_DOMAIN_DEVICE_NVRAM:
+ case VIR_DOMAIN_DEVICE_RNG:
+ case VIR_DOMAIN_DEVICE_AUDIO:
+ case VIR_DOMAIN_DEVICE_CRYPTO:
+ ret = 0;
+ break;
+
+ case VIR_DOMAIN_DEVICE_NONE:
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("unexpected VIR_DOMAIN_DEVICE_NONE"));
+ break;
+
+ case VIR_DOMAIN_DEVICE_LAST:
+ default:
+ virReportEnumRangeError(virDomainDeviceType, dev->type);
+ break;
+ }
+
+ return ret;
+}
+
+
+int
+qemuDomainDefPostParseBasic(virDomainDef *def,
+ void *opaque G_GNUC_UNUSED)
+{
+ virQEMUDriver *driver = opaque;
+
+ /* check for emulator and create a default one if needed */
+ if (!def->emulator) {
+ if (!(def->emulator = virQEMUCapsGetDefaultEmulator(
+ driver->hostarch, def->os.arch))) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+ _("No emulator found for arch '%1$s'"),
+ virArchToString(def->os.arch));
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+
+static int
+qemuCanonicalizeMachine(virDomainDef *def, virQEMUCaps *qemuCaps)
+{
+ const char *canon;
+
+ if (!(canon = virQEMUCapsGetCanonicalMachine(qemuCaps, def->virtType,
+ def->os.machine)))
+ return 0;
+
+ if (STRNEQ(canon, def->os.machine)) {
+ char *tmp;
+ tmp = g_strdup(canon);
+ VIR_FREE(def->os.machine);
+ def->os.machine = tmp;
+ }
+
+ return 0;
+}
+
+
+static int
+qemuDomainDefMachinePostParse(virDomainDef *def,
+ virQEMUCaps *qemuCaps)
+{
+ if (!def->os.machine) {
+ const char *machine = virQEMUCapsGetPreferredMachine(qemuCaps,
+ def->virtType);
+ if (!machine) {
+ virReportError(VIR_ERR_INVALID_ARG,
+ _("could not get preferred machine for %1$s type=%2$s"),
+ def->emulator,
+ virDomainVirtTypeToString(def->virtType));
+ return -1;
+ }
+
+ def->os.machine = g_strdup(machine);
+ }
+
+ if (qemuCanonicalizeMachine(def, qemuCaps) < 0)
+ return -1;
+
+ return 0;
+}
+
+
+/**
+ * qemuDomainDefACPIPostParse:
+ * @def: domain definition
+ * @qemuCaps: qemu capabilities object
+ *
+ * Fixup the use of ACPI flag on certain architectures that never supported it
+ * and users for some reason used it, which would break migration to newer
+ * libvirt versions which check whether given machine type supports ACPI.
+ *
+ * The fixup is done in post-parse as it's hard to update the ABI stability
+ * check on source of the migration.
+ */
+static void
+qemuDomainDefACPIPostParse(virDomainDef *def,
+ virQEMUCaps *qemuCaps,
+ unsigned int parseFlags)
+{
+ /* Only cases when ACPI is enabled need to be fixed up */
+ if (def->features[VIR_DOMAIN_FEATURE_ACPI] != VIR_TRISTATE_SWITCH_ON)
+ return;
+
+ /* Strip the <acpi/> feature only for non-fresh configs, in order to still
+ * produce an error if the feature is present in a newly defined one.
+ *
+ * The use of the VIR_DOMAIN_DEF_PARSE_ABI_UPDATE looks counter-intuitive,
+ * but it's used only in qemuDomainCreateXML/qemuDomainDefineXMLFlags APIs
+ * */
+ if (parseFlags & VIR_DOMAIN_DEF_PARSE_ABI_UPDATE)
+ return;
+
+ /* This fixup is applicable _only_ on architectures which were present as of
+ * libvirt-9.2 and *never* supported ACPI. The fixup is currently done only
+ * for existing users of s390(x) to fix migration for configs which had
+ * <acpi/> despite being ignored.
+ */
+ if (def->os.arch != VIR_ARCH_S390 &&
+ def->os.arch != VIR_ARCH_S390X)
+ return;
+
+ /* To be sure, we only strip ACPI if given machine type doesn't support it */
+ if (virQEMUCapsMachineSupportsACPI(qemuCaps, def->virtType, def->os.machine) != VIR_TRISTATE_BOOL_NO)
+ return;
+
+ def->features[VIR_DOMAIN_FEATURE_ACPI] = VIR_TRISTATE_SWITCH_ABSENT;
+}
+
+
+static int
+qemuDomainDefBootPostParse(virDomainDef *def,
+ virQEMUDriver *driver,
+ unsigned int parseFlags)
+{
+ bool abiUpdate = !!(parseFlags & VIR_DOMAIN_DEF_PARSE_ABI_UPDATE);
+
+ /* If we're loading an existing configuration from disk, we
+ * should try as hard as possible to preserve historical
+ * behavior. In particular, firmware autoselection being enabled
+ * could never have resulted, before libvirt 9.2.0, in anything
+ * but a raw firmware image being selected.
+ *
+ * In order to ensure that existing domains keep working even if
+ * a firmware descriptor for a build with a different format is
+ * given higher priority, explicitly add this requirement to the
+ * definition before performing firmware selection */
+ if (!abiUpdate && def->os.firmware) {
+ if (!def->os.loader)
+ def->os.loader = virDomainLoaderDefNew();
+ if (!def->os.loader->format)
+ def->os.loader->format = VIR_STORAGE_FILE_RAW;
+ }
+
+ /* Firmware selection can fail for a number of reasons, but the
+ * most likely one is that the requested configuration contains
+ * mistakes or includes constraints that are impossible to
+ * satisfy on the current system.
+ *
+ * If that happens, we have to react differently based on the
+ * situation: if we're defining a new domain or updating its ABI,
+ * we should let the user know immediately so that they can
+ * change the requested configuration, hopefully into one that we
+ * can work with; if we're loading the configuration of an
+ * existing domain from disk, however, we absolutely cannot error
+ * out here, or the domain will disappear.
+ *
+ * To handle the second case gracefully, we clear any reported
+ * errors and continue as if nothing had happened. When it's time
+ * to start the domain, qemuFirmwareFillDomain() will be run
+ * again, fail in the same way, and at that point we'll have a
+ * chance to inform the user of any issues */
+ if (qemuFirmwareFillDomain(driver, def, abiUpdate) < 0) {
+ if (abiUpdate) {
+ return -1;
+ } else {
+ virResetLastError();
+ return 0;
+ }
+ }
+
+ return 0;
+}
+
+
+static int
+qemuDomainDefAddImplicitInputDevice(virDomainDef *def,
+ virQEMUCaps *qemuCaps)
+{
+ if (virQEMUCapsSupportsI8042(qemuCaps, def) &&
+ def->features[VIR_DOMAIN_FEATURE_PS2] != VIR_TRISTATE_SWITCH_OFF) {
+ if (virDomainDefMaybeAddInput(def,
+ VIR_DOMAIN_INPUT_TYPE_MOUSE,
+ VIR_DOMAIN_INPUT_BUS_PS2) < 0)
+ return -1;
+
+ if (virDomainDefMaybeAddInput(def,
+ VIR_DOMAIN_INPUT_TYPE_KBD,
+ VIR_DOMAIN_INPUT_BUS_PS2) < 0)
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int
+qemuDomainDefSetDefaultCPU(virDomainDef *def,
+ virArch hostarch,
+ virQEMUCaps *qemuCaps)
+{
+ const char *model;
+
+ if (def->cpu &&
+ (def->cpu->mode != VIR_CPU_MODE_CUSTOM ||
+ def->cpu->model))
+ return 0;
+
+ if (!virCPUArchIsSupported(def->os.arch))
+ return 0;
+
+ /* Default CPU model info from QEMU is usable for TCG only except for
+ * x86, s390, and ppc64. */
+ if (!ARCH_IS_X86(def->os.arch) &&
+ !ARCH_IS_S390(def->os.arch) &&
+ !ARCH_IS_PPC64(def->os.arch) &&
+ def->virtType != VIR_DOMAIN_VIRT_QEMU)
+ return 0;
+
+ model = virQEMUCapsGetMachineDefaultCPU(qemuCaps, def->os.machine, def->virtType);
+ if (!model) {
+ VIR_DEBUG("Unknown default CPU model for domain '%s'", def->name);
+ return 0;
+ }
+
+ if (STREQ(model, "host") && def->virtType != VIR_DOMAIN_VIRT_KVM) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("QEMU reports invalid default CPU model \"host\" for non-kvm domain virt type"));
+ return -1;
+ }
+
+ if (!def->cpu)
+ def->cpu = virCPUDefNew();
+
+ def->cpu->type = VIR_CPU_TYPE_GUEST;
+
+ if (STREQ(model, "host")) {
+ if (ARCH_IS_S390(def->os.arch) &&
+ virQEMUCapsIsCPUModeSupported(qemuCaps, hostarch, def->virtType,
+ VIR_CPU_MODE_HOST_MODEL,
+ def->os.machine)) {
+ def->cpu->mode = VIR_CPU_MODE_HOST_MODEL;
+ } else {
+ def->cpu->mode = VIR_CPU_MODE_HOST_PASSTHROUGH;
+ }
+
+ VIR_DEBUG("Setting default CPU mode for domain '%s' to %s",
+ def->name, virCPUModeTypeToString(def->cpu->mode));
+ } else {
+ /* We need to turn off all CPU checks when the domain is started
+ * because the default CPU (e.g., qemu64) may not be runnable on any
+ * host. QEMU will just disable the unavailable features and we will
+ * update the CPU definition accordingly and set check to FULL when
+ * starting the domain. */
+ def->cpu->check = VIR_CPU_CHECK_NONE;
+ def->cpu->mode = VIR_CPU_MODE_CUSTOM;
+ def->cpu->match = VIR_CPU_MATCH_EXACT;
+ def->cpu->fallback = VIR_CPU_FALLBACK_FORBID;
+ def->cpu->model = g_strdup(model);
+
+ VIR_DEBUG("Setting default CPU model for domain '%s' to %s",
+ def->name, model);
+ }
+
+ return 0;
+}
+
+
+static int
+qemuDomainDefAddDefaultDevices(virQEMUDriver *driver,
+ virDomainDef *def,
+ virQEMUCaps *qemuCaps)
+{
+ bool addDefaultUSB = false;
+ int usbModel = -1; /* "default for machinetype" */
+ int pciRoot; /* index within def->controllers */
+ bool addImplicitSATA = false;
+ bool addPCIRoot = false;
+ bool addPCIeRoot = false;
+ bool addDefaultMemballoon = false;
+ bool addDefaultUSBKBD = false;
+ bool addDefaultUSBMouse = false;
+ bool addPanicDevice = false;
+ bool addITCOWatchdog = false;
+ bool addIOMMU = false;
+
+ /* add implicit input devices */
+ if (qemuDomainDefAddImplicitInputDevice(def, qemuCaps) < 0)
+ return -1;
+
+ /* Add implicit PCI root controller if the machine has one */
+ switch (def->os.arch) {
+ case VIR_ARCH_I686:
+ case VIR_ARCH_X86_64:
+ addDefaultMemballoon = true;
+
+ if (STREQ(def->os.machine, "isapc")) {
+ break;
+ }
+
+ addDefaultUSB = true;
+
+ if (qemuDomainIsQ35(def)) {
+ addPCIeRoot = true;
+ addImplicitSATA = true;
+ addITCOWatchdog = true;
+
+ if (virDomainDefGetVcpusMax(def) > QEMU_MAX_VCPUS_WITHOUT_EIM) {
+ addIOMMU = true;
+ }
+
+ /* Prefer adding a USB3 controller if supported, fall back
+ * to USB2 if there is no USB3 available, and if that's
+ * unavailable don't add anything.
+ */
+ if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_QEMU_XHCI))
+ usbModel = VIR_DOMAIN_CONTROLLER_MODEL_USB_QEMU_XHCI;
+ else if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_NEC_USB_XHCI))
+ usbModel = VIR_DOMAIN_CONTROLLER_MODEL_USB_NEC_XHCI;
+ else if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_ICH9_USB_EHCI1))
+ usbModel = VIR_DOMAIN_CONTROLLER_MODEL_USB_ICH9_EHCI1;
+ else
+ addDefaultUSB = false;
+ break;
+ }
+ if (qemuDomainIsI440FX(def))
+ addPCIRoot = true;
+ break;
+
+ case VIR_ARCH_ARMV6L:
+ case VIR_ARCH_ARMV7L:
+ case VIR_ARCH_ARMV7B:
+ case VIR_ARCH_AARCH64:
+ if (STREQ(def->os.machine, "versatilepb"))
+ addPCIRoot = true;
+
+ /* Add default USB for the two machine types which historically
+ * supported -usb */
+ if (STREQ(def->os.machine, "versatilepb") ||
+ STRPREFIX(def->os.machine, "realview")) {
+ addDefaultUSB = true;
+ usbModel = VIR_DOMAIN_CONTROLLER_MODEL_USB_PCI_OHCI;
+ }
+
+ if (qemuDomainIsARMVirt(def))
+ addPCIeRoot = true;
+
+ break;
+
+ case VIR_ARCH_PPC64:
+ case VIR_ARCH_PPC64LE:
+ addPCIRoot = true;
+ addDefaultUSB = true;
+ addDefaultUSBKBD = true;
+ addDefaultUSBMouse = true;
+ addDefaultMemballoon = true;
+ /* For pSeries guests, the firmware provides the same
+ * functionality as the pvpanic device, so automatically
+ * add the definition if not already present */
+ if (qemuDomainIsPSeries(def))
+ addPanicDevice = true;
+ break;
+
+ case VIR_ARCH_ALPHA:
+ case VIR_ARCH_PPC:
+ case VIR_ARCH_PPCEMB:
+ case VIR_ARCH_SH4:
+ case VIR_ARCH_SH4EB:
+ addDefaultUSB = true;
+ addDefaultMemballoon = true;
+ addPCIRoot = true;
+ break;
+
+ case VIR_ARCH_RISCV32:
+ case VIR_ARCH_RISCV64:
+ addDefaultMemballoon = true;
+ if (qemuDomainIsRISCVVirt(def))
+ addPCIeRoot = true;
+ break;
+
+ case VIR_ARCH_S390:
+ case VIR_ARCH_S390X:
+ addDefaultMemballoon = true;
+ addPanicDevice = true;
+ addPCIRoot = virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_ZPCI);
+ break;
+
+ case VIR_ARCH_SPARC64:
+ addDefaultUSB = true;
+ addDefaultMemballoon = true;
+ addPCIRoot = true;
+ break;
+
+ case VIR_ARCH_MIPS:
+ case VIR_ARCH_MIPSEL:
+ case VIR_ARCH_MIPS64:
+ case VIR_ARCH_MIPS64EL:
+ addDefaultUSB = true;
+ addDefaultMemballoon = true;
+ if (qemuDomainIsMipsMalta(def))
+ addPCIRoot = true;
+ break;
+
+ case VIR_ARCH_LOONGARCH64:
+ addPCIeRoot = true;
+ break;
+
+ case VIR_ARCH_CRIS:
+ case VIR_ARCH_ITANIUM:
+ case VIR_ARCH_LM32:
+ case VIR_ARCH_M68K:
+ case VIR_ARCH_MICROBLAZE:
+ case VIR_ARCH_MICROBLAZEEL:
+ case VIR_ARCH_OR32:
+ case VIR_ARCH_PARISC:
+ case VIR_ARCH_PARISC64:
+ case VIR_ARCH_PPCLE:
+ case VIR_ARCH_SPARC:
+ case VIR_ARCH_UNICORE32:
+ case VIR_ARCH_XTENSA:
+ case VIR_ARCH_XTENSAEB:
+ case VIR_ARCH_NONE:
+ case VIR_ARCH_LAST:
+ default:
+ break;
+ }
+
+ if (addDefaultUSB &&
+ virDomainControllerFind(def, VIR_DOMAIN_CONTROLLER_TYPE_USB, 0) < 0 &&
+ virDomainDefAddUSBController(def, 0, usbModel) < 0)
+ return -1;
+
+ if (addImplicitSATA &&
+ virDomainDefMaybeAddController(
+ def, VIR_DOMAIN_CONTROLLER_TYPE_SATA, 0, -1) < 0)
+ return -1;
+
+ pciRoot = virDomainControllerFind(def, VIR_DOMAIN_CONTROLLER_TYPE_PCI, 0);
+
+ /* NB: any machine that sets addPCIRoot to true must also return
+ * true from the function qemuDomainSupportsPCI().
+ */
+ if (addPCIRoot) {
+ if (pciRoot >= 0) {
+ if (def->controllers[pciRoot]->model != VIR_DOMAIN_CONTROLLER_MODEL_PCI_ROOT) {
+ virReportError(VIR_ERR_XML_ERROR,
+ _("The PCI controller with index='0' must be model='pci-root' for this machine type, but model='%1$s' was found instead"),
+ virDomainControllerModelPCITypeToString(def->controllers[pciRoot]->model));
+ return -1;
+ }
+ } else if (!virDomainDefAddController(def, VIR_DOMAIN_CONTROLLER_TYPE_PCI, 0,
+ VIR_DOMAIN_CONTROLLER_MODEL_PCI_ROOT)) {
+ return -1;
+ }
+ }
+
+ /* When a machine has a pcie-root, make sure that there is always
+ * a dmi-to-pci-bridge controller added as bus 1, and a pci-bridge
+ * as bus 2, so that standard PCI devices can be connected
+ *
+ * NB: any machine that sets addPCIeRoot to true must also return
+ * true from the function qemuDomainSupportsPCI().
+ */
+ if (addPCIeRoot) {
+ if (pciRoot >= 0) {
+ if (def->controllers[pciRoot]->model != VIR_DOMAIN_CONTROLLER_MODEL_PCIE_ROOT) {
+ virReportError(VIR_ERR_XML_ERROR,
+ _("The PCI controller with index='0' must be model='pcie-root' for this machine type, but model='%1$s' was found instead"),
+ virDomainControllerModelPCITypeToString(def->controllers[pciRoot]->model));
+ return -1;
+ }
+ } else if (!virDomainDefAddController(def, VIR_DOMAIN_CONTROLLER_TYPE_PCI, 0,
+ VIR_DOMAIN_CONTROLLER_MODEL_PCIE_ROOT)) {
+ return -1;
+ }
+ }
+
+ if (addDefaultMemballoon && !def->memballoon) {
+ virDomainMemballoonDef *memballoon;
+ memballoon = g_new0(virDomainMemballoonDef, 1);
+
+ memballoon->model = VIR_DOMAIN_MEMBALLOON_MODEL_VIRTIO;
+ def->memballoon = memballoon;
+ }
+
+ if (addDefaultUSBMouse) {
+ bool hasUSBTablet = false;
+ size_t j;
+
+ for (j = 0; j < def->ninputs; j++) {
+ if (def->inputs[j]->type == VIR_DOMAIN_INPUT_TYPE_TABLET &&
+ def->inputs[j]->bus == VIR_DOMAIN_INPUT_BUS_USB) {
+ hasUSBTablet = true;
+ break;
+ }
+ }
+
+ /* Historically, we have automatically added USB keyboard and
+ * mouse to some guests. While the former device is generally
+ * safe to have, adding the latter is undesiderable if a USB
+ * tablet is already present in the guest */
+ if (hasUSBTablet)
+ addDefaultUSBMouse = false;
+ }
+
+ if (addDefaultUSBKBD &&
+ def->ngraphics > 0 &&
+ virDomainDefMaybeAddInput(def,
+ VIR_DOMAIN_INPUT_TYPE_KBD,
+ VIR_DOMAIN_INPUT_BUS_USB) < 0)
+ return -1;
+
+ if (addDefaultUSBMouse &&
+ def->ngraphics > 0 &&
+ virDomainDefMaybeAddInput(def,
+ VIR_DOMAIN_INPUT_TYPE_MOUSE,
+ VIR_DOMAIN_INPUT_BUS_USB) < 0)
+ return -1;
+
+ if (addPanicDevice) {
+ virDomainPanicModel defaultModel = qemuDomainDefaultPanicModel(def);
+ size_t j;
+
+ for (j = 0; j < def->npanics; j++) {
+ if (def->panics[j]->model == VIR_DOMAIN_PANIC_MODEL_DEFAULT ||
+ def->panics[j]->model == defaultModel)
+ break;
+ }
+
+ if (j == def->npanics) {
+ virDomainPanicDef *panic = g_new0(virDomainPanicDef, 1);
+
+ VIR_APPEND_ELEMENT_COPY(def->panics, def->npanics, panic);
+ }
+ }
+
+ if (addITCOWatchdog) {
+ size_t i = 0;
+
+ for (i = 0; i < def->nwatchdogs; i++) {
+ if (def->watchdogs[i]->model == VIR_DOMAIN_WATCHDOG_MODEL_ITCO)
+ break;
+ }
+
+ if (i == def->nwatchdogs) {
+ virDomainWatchdogDef *watchdog = g_new0(virDomainWatchdogDef, 1);
+
+ watchdog->model = VIR_DOMAIN_WATCHDOG_MODEL_ITCO;
+ if (def->nwatchdogs)
+ watchdog->action = def->watchdogs[0]->action;
+ else
+ watchdog->action = VIR_DOMAIN_WATCHDOG_ACTION_RESET;
+
+ VIR_APPEND_ELEMENT(def->watchdogs, def->nwatchdogs, watchdog);
+ }
+ }
+
+ if (addIOMMU && !def->iommu &&
+ virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_INTEL_IOMMU) &&
+ virQEMUCapsGet(qemuCaps, QEMU_CAPS_INTEL_IOMMU_INTREMAP) &&
+ virQEMUCapsGet(qemuCaps, QEMU_CAPS_INTEL_IOMMU_EIM)) {
+ g_autoptr(virDomainIOMMUDef) iommu = NULL;
+
+ iommu = virDomainIOMMUDefNew();
+ iommu->model = VIR_DOMAIN_IOMMU_MODEL_INTEL;
+ /* eim requires intremap. */
+ iommu->intremap = VIR_TRISTATE_SWITCH_ON;
+ iommu->eim = VIR_TRISTATE_SWITCH_ON;
+
+ def->iommu = g_steal_pointer(&iommu);
+ }
+
+ if (qemuDomainDefAddDefaultAudioBackend(driver, def) < 0)
+ return -1;
+
+ return 0;
+}
+
+
+/**
+ * qemuDomainDefEnableDefaultFeatures:
+ * @def: domain definition
+ * @qemuCaps: QEMU capabilities
+ *
+ * Make sure that features that should be enabled by default are actually
+ * enabled and configure default values related to those features.
+ */
+static void
+qemuDomainDefEnableDefaultFeatures(virDomainDef *def,
+ virQEMUCaps *qemuCaps)
+{
+ /* The virt machine type always uses GIC: if the relevant information
+ * was not included in the domain XML, we need to choose a suitable
+ * GIC version ourselves */
+ if ((def->features[VIR_DOMAIN_FEATURE_GIC] == VIR_TRISTATE_SWITCH_ABSENT &&
+ qemuDomainIsARMVirt(def)) ||
+ (def->features[VIR_DOMAIN_FEATURE_GIC] == VIR_TRISTATE_SWITCH_ON &&
+ def->gic_version == VIR_GIC_VERSION_NONE)) {
+ virGICVersion version;
+
+ VIR_DEBUG("Looking for usable GIC version in domain capabilities");
+ for (version = VIR_GIC_VERSION_LAST - 1;
+ version > VIR_GIC_VERSION_NONE;
+ version--) {
+
+ /* We want to use the highest available GIC version for guests;
+ * however, the emulated GICv3 is currently lacking a MSI controller,
+ * making it unsuitable for the pure PCIe topology we aim for.
+ *
+ * For that reason, we skip this step entirely for TCG guests,
+ * and rely on the code below to pick the default version, GICv2,
+ * which supports all the features we need.
+ *
+ * See https://bugzilla.redhat.com/show_bug.cgi?id=1414081 */
+ if (version == VIR_GIC_VERSION_3 &&
+ def->virtType == VIR_DOMAIN_VIRT_QEMU) {
+ continue;
+ }
+
+ if (virQEMUCapsSupportsGICVersion(qemuCaps,
+ def->virtType,
+ version)) {
+ VIR_DEBUG("Using GIC version %s",
+ virGICVersionTypeToString(version));
+ def->gic_version = version;
+ break;
+ }
+ }
+
+ /* Use the default GIC version (GICv2) as a last-ditch attempt
+ * if no match could be found above */
+ if (def->gic_version == VIR_GIC_VERSION_NONE) {
+ VIR_DEBUG("Using GIC version 2 (default)");
+ def->gic_version = VIR_GIC_VERSION_2;
+ }
+
+ /* Even if we haven't found a usable GIC version in the domain
+ * capabilities, we still want to enable this */
+ def->features[VIR_DOMAIN_FEATURE_GIC] = VIR_TRISTATE_SWITCH_ON;
+ }
+}
+
+
+static int
+qemuDomainRecheckInternalPaths(virDomainDef *def,
+ virQEMUDriverConfig *cfg,
+ unsigned int flags)
+{
+ size_t i = 0;
+ size_t j = 0;
+
+ for (i = 0; i < def->ngraphics; ++i) {
+ virDomainGraphicsDef *graphics = def->graphics[i];
+
+ for (j = 0; j < graphics->nListens; ++j) {
+ virDomainGraphicsListenDef *glisten = &graphics->listens[j];
+
+ /* This will happen only if we parse XML from old libvirts where
+ * unix socket was available only for VNC graphics. In this
+ * particular case we should follow the behavior and if we remove
+ * the auto-generated socket based on config option from qemu.conf
+ * we need to change the listen type to address. */
+ if (graphics->type == VIR_DOMAIN_GRAPHICS_TYPE_VNC &&
+ glisten->type == VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_SOCKET &&
+ glisten->socket &&
+ !glisten->autoGenerated &&
+ STRPREFIX(glisten->socket, cfg->libDir)) {
+ if (flags & VIR_DOMAIN_DEF_PARSE_INACTIVE) {
+ VIR_FREE(glisten->socket);
+ glisten->type = VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_ADDRESS;
+ } else {
+ glisten->fromConfig = true;
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+
+static int
+qemuDomainDefVcpusPostParse(virDomainDef *def)
+{
+ unsigned int maxvcpus = virDomainDefGetVcpusMax(def);
+ virDomainVcpuDef *vcpu;
+ virDomainVcpuDef *prevvcpu;
+ size_t i;
+ bool has_order = false;
+
+ /* vcpu 0 needs to be present, first, and non-hotpluggable */
+ vcpu = virDomainDefGetVcpu(def, 0);
+ if (!vcpu->online) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+ _("vcpu 0 can't be offline"));
+ return -1;
+ }
+ if (vcpu->hotpluggable == VIR_TRISTATE_BOOL_YES) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+ _("vcpu0 can't be hotpluggable"));
+ return -1;
+ }
+ if (vcpu->order != 0 && vcpu->order != 1) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+ _("vcpu0 must be enabled first"));
+ return -1;
+ }
+
+ if (vcpu->order != 0)
+ has_order = true;
+
+ prevvcpu = vcpu;
+
+ /* all online vcpus or non online vcpu need to have order set */
+ for (i = 1; i < maxvcpus; i++) {
+ vcpu = virDomainDefGetVcpu(def, i);
+
+ if (vcpu->online &&
+ (vcpu->order != 0) != has_order) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+ _("all vcpus must have either set or unset order"));
+ return -1;
+ }
+
+ /* few conditions for non-hotpluggable (thus online) vcpus */
+ if (vcpu->hotpluggable == VIR_TRISTATE_BOOL_NO) {
+ /* they can be ordered only at the beginning */
+ if (prevvcpu->hotpluggable == VIR_TRISTATE_BOOL_YES) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+ _("online non-hotpluggable vcpus need to be ordered prior to hotplugable vcpus"));
+ return -1;
+ }
+
+ /* they need to be in order (qemu doesn't support any order yet).
+ * Also note that multiple vcpus may share order on some platforms */
+ if (prevvcpu->order > vcpu->order) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+ _("online non-hotpluggable vcpus must be ordered in ascending order"));
+ return -1;
+ }
+ }
+
+ prevvcpu = vcpu;
+ }
+
+ return 0;
+}
+
+
+static int
+qemuDomainDefCPUPostParse(virDomainDef *def,
+ virQEMUCaps *qemuCaps)
+{
+ virCPUFeatureDef *sveFeature = NULL;
+ bool sveVectorLengthsProvided = false;
+ size_t i;
+
+ if (!def->cpu)
+ return 0;
+
+ for (i = 0; i < def->cpu->nfeatures; i++) {
+ virCPUFeatureDef *feature = &def->cpu->features[i];
+
+ if (STREQ(feature->name, "sve")) {
+ sveFeature = feature;
+ } else if (STRPREFIX(feature->name, "sve")) {
+ sveVectorLengthsProvided = true;
+ }
+ }
+
+ if (sveVectorLengthsProvided) {
+ if (sveFeature) {
+ if (sveFeature->policy == VIR_CPU_FEATURE_DISABLE ||
+ sveFeature->policy == VIR_CPU_FEATURE_FORBID) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+ _("SVE disabled, but SVE vector lengths provided"));
+ return -1;
+ } else {
+ sveFeature->policy = VIR_CPU_FEATURE_REQUIRE;
+ }
+ } else {
+ VIR_RESIZE_N(def->cpu->features, def->cpu->nfeatures_max,
+ def->cpu->nfeatures, 1);
+
+ def->cpu->features[def->cpu->nfeatures].name = g_strdup("sve");
+ def->cpu->features[def->cpu->nfeatures].policy = VIR_CPU_FEATURE_REQUIRE;
+
+ def->cpu->nfeatures++;
+ }
+ }
+
+ /* Running domains were either started before QEMU_CAPS_CPU_MIGRATABLE was
+ * introduced and thus we can't rely on it or they already have the
+ * migratable default set. */
+ if (def->id == -1 &&
+ qemuCaps &&
+ def->cpu->mode == VIR_CPU_MODE_HOST_PASSTHROUGH &&
+ def->cpu->migratable == VIR_TRISTATE_SWITCH_ABSENT) {
+ if (virQEMUCapsGet(qemuCaps, QEMU_CAPS_CPU_MIGRATABLE))
+ def->cpu->migratable = VIR_TRISTATE_SWITCH_ON;
+ else if (ARCH_IS_X86(def->os.arch))
+ def->cpu->migratable = VIR_TRISTATE_SWITCH_OFF;
+ }
+
+ /* Nothing to be done if only CPU topology is specified. */
+ if (def->cpu->mode == VIR_CPU_MODE_CUSTOM &&
+ !def->cpu->model)
+ return 0;
+
+ if (def->cpu->check != VIR_CPU_CHECK_DEFAULT)
+ return 0;
+
+ switch ((virCPUMode) def->cpu->mode) {
+ case VIR_CPU_MODE_HOST_PASSTHROUGH:
+ case VIR_CPU_MODE_MAXIMUM:
+ def->cpu->check = VIR_CPU_CHECK_NONE;
+ break;
+
+ case VIR_CPU_MODE_HOST_MODEL:
+ def->cpu->check = VIR_CPU_CHECK_PARTIAL;
+ break;
+
+ case VIR_CPU_MODE_CUSTOM:
+ /* Custom CPUs in TCG mode are not compared to host CPU by default. */
+ if (def->virtType == VIR_DOMAIN_VIRT_QEMU)
+ def->cpu->check = VIR_CPU_CHECK_NONE;
+ else
+ def->cpu->check = VIR_CPU_CHECK_PARTIAL;
+ break;
+
+ case VIR_CPU_MODE_LAST:
+ break;
+ }
+
+ return 0;
+}
+
+
+static int
+qemuDomainDefTsegPostParse(virDomainDef *def,
+ virQEMUCaps *qemuCaps)
+{
+ if (def->features[VIR_DOMAIN_FEATURE_SMM] != VIR_TRISTATE_SWITCH_ON)
+ return 0;
+
+ if (!def->tseg_specified)
+ return 0;
+
+ if (!qemuDomainIsQ35(def)) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+ _("SMM TSEG is only supported with q35 machine type"));
+ return -1;
+ }
+
+ if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_MCH_EXTENDED_TSEG_MBYTES)) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+ _("Setting TSEG size is not supported with this QEMU binary"));
+ return -1;
+ }
+
+ if (def->tseg_size & ((1 << 20) - 1)) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
+ _("SMM TSEG size must be divisible by 1 MiB"));
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int
+qemuDomainDefNumaAutoAdd(virDomainDef *def,
+ unsigned int parseFlags)
+{
+ bool abiUpdate = !!(parseFlags & VIR_DOMAIN_DEF_PARSE_ABI_UPDATE);
+ unsigned long long nodeMem;
+ size_t i;
+
+ if (!abiUpdate ||
+ !virDomainDefHasMemoryHotplug(def) ||
+ virDomainNumaGetNodeCount(def->numa) > 0) {
+ return 0;
+ }
+
+ nodeMem = virDomainDefGetMemoryTotal(def);
+
+ if (!def->numa)
+ def->numa = virDomainNumaNew();
+
+ virDomainNumaSetNodeCount(def->numa, 1);
+
+ for (i = 0; i < def->nmems; i++) {
+ virDomainMemoryDef *mem = def->mems[i];
+
+ if (mem->size > nodeMem) {
+ virReportError(VIR_ERR_XML_ERROR, "%s",
+ _("Total size of memory devices exceeds the total memory size"));
+ return -1;
+ }
+
+ nodeMem -= mem->size;
+
+ switch (mem->model) {
+ case VIR_DOMAIN_MEMORY_MODEL_DIMM:
+ case VIR_DOMAIN_MEMORY_MODEL_NVDIMM:
+ case VIR_DOMAIN_MEMORY_MODEL_VIRTIO_MEM:
+ if (mem->targetNode == -1)
+ mem->targetNode = 0;
+ break;
+
+ case VIR_DOMAIN_MEMORY_MODEL_NONE:
+ case VIR_DOMAIN_MEMORY_MODEL_VIRTIO_PMEM:
+ case VIR_DOMAIN_MEMORY_MODEL_SGX_EPC:
+ case VIR_DOMAIN_MEMORY_MODEL_LAST:
+ break;
+ }
+ }
+
+ virDomainNumaSetNodeMemorySize(def->numa, 0, nodeMem);
+
+ return 0;
+}
+
+
+static int
+qemuDomainDefNumaCPUsPostParse(virDomainDef *def,
+ virQEMUCaps *qemuCaps,
+ unsigned int parseFlags)
+{
+ if (qemuDomainDefNumaAutoAdd(def, parseFlags) < 0)
+ return -1;
+
+ return qemuDomainDefNumaCPUsRectify(def, qemuCaps);
+}
+
+
+int
+qemuDomainDefPostParse(virDomainDef *def,
+ unsigned int parseFlags,
+ void *opaque,
+ void *parseOpaque)
+{
+ virQEMUDriver *driver = opaque;
+ g_autoptr(virQEMUDriverConfig) cfg = virQEMUDriverGetConfig(driver);
+ virQEMUCaps *qemuCaps = parseOpaque;
+
+ /* Note that qemuCaps may be NULL when this function is called. This
+ * function shall not fail in that case. It will be re-run on VM startup
+ * with the capabilities populated.
+ */
+ if (!qemuCaps)
+ return 1;
+
+ if (qemuDomainDefMachinePostParse(def, qemuCaps) < 0)
+ return -1;
+
+ qemuDomainDefACPIPostParse(def, qemuCaps, parseFlags);
+
+ if (qemuDomainDefBootPostParse(def, driver, parseFlags) < 0)
+ return -1;
+
+ if (qemuDomainDefAddDefaultDevices(driver, def, qemuCaps) < 0)
+ return -1;
+
+ if (qemuDomainDefSetDefaultCPU(def, driver->hostarch, qemuCaps) < 0)
+ return -1;
+
+ qemuDomainDefEnableDefaultFeatures(def, qemuCaps);
+
+ if (qemuDomainRecheckInternalPaths(def, cfg, parseFlags) < 0)
+ return -1;
+
+ if (qemuSecurityVerify(driver->securityManager, def) < 0)
+ return -1;
+
+ if (qemuDomainDefVcpusPostParse(def) < 0)
+ return -1;
+
+ if (qemuDomainDefCPUPostParse(def, qemuCaps) < 0)
+ return -1;
+
+ if (qemuDomainDefTsegPostParse(def, qemuCaps) < 0)
+ return -1;
+
+ if (qemuDomainDefNumaCPUsPostParse(def, qemuCaps, parseFlags) < 0)
+ return -1;
+
+ return 0;
+}
+
+
+int
+qemuDomainPostParseDataAlloc(const virDomainDef *def,
+ unsigned int parseFlags G_GNUC_UNUSED,
+ void *opaque,
+ void **parseOpaque)
+{
+ virQEMUDriver *driver = opaque;
+
+ if (!(*parseOpaque = virQEMUCapsCacheLookup(driver->qemuCapsCache,
+ def->emulator)))
+ return 1;
+
+ return 0;
+}
+
+
+void
+qemuDomainPostParseDataFree(void *parseOpaque)
+{
+ virQEMUCaps *qemuCaps = parseOpaque;
+
+ virObjectUnref(qemuCaps);
+}
diff --git a/src/qemu/qemu_postparse.h b/src/qemu/qemu_postparse.h
new file mode 100644
index 0000000000..ac69c14604
--- /dev/null
+++ b/src/qemu/qemu_postparse.h
@@ -0,0 +1,54 @@
+/*
+ * qemu_postparse.h: QEMU domain PostParse functions
+ *
+ * Copyright (C) 2006-2024 Red Hat, Inc.
+ * Copyright (C) 2006 Daniel P. Berrange
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include "virconftypes.h"
+
+int
+qemuDomainDeviceDiskDefPostParse(virDomainDiskDef *disk,
+ unsigned int parseFlags);
+
+int
+qemuDomainDeviceDefPostParse(virDomainDeviceDef *dev,
+ const virDomainDef *def,
+ unsigned int parseFlags,
+ void *opaque,
+ void *parseOpaque);
+
+int
+qemuDomainDefPostParseBasic(virDomainDef *def,
+ void *opaque);
+
+int
+qemuDomainDefPostParse(virDomainDef *def,
+ unsigned int parseFlags,
+ void *opaque,
+ void *parseOpaque);
+
+int
+qemuDomainPostParseDataAlloc(const virDomainDef *def,
+ unsigned int parseFlags,
+ void *opaque,
+ void **parseOpaque);
+
+void
+qemuDomainPostParseDataFree(void *parseOpaque);
diff --git a/tests/qemublocktest.c b/tests/qemublocktest.c
index 7ffa445c37..ac4d87b527 100644
--- a/tests/qemublocktest.c
+++ b/tests/qemublocktest.c
@@ -27,6 +27,7 @@
#include "qemu/qemu_monitor_json.h"
#include "qemu/qemu_backup.h"
#include "qemu/qemu_checkpoint.h"
+#include "qemu/qemu_postparse.h"
#include "qemu/qemu_validate.h"
#define LIBVIRT_SNAPSHOT_CONF_PRIV_H_ALLOW
--
2.45.2
1 month, 1 week