A while back QEMU introduced a new machine option for disabling the i8042 PS/2 controller (commit 4ccd5fe22feb95137d325f422016a6473) which up until then was a built-in device included by all PC machine type descendants unconditionally. This new option allowed users to disable emulation of this controller, thus removing the default PS/2 peripherals. The rationale for why somebody might want to disable PS/2 peripherals is explained in the aforementioned commit. This introduces a new 'ps2' feature which, when disabled, results in no implicit PS/2 bus input devices being automatically added to the domain and addition of the 'i8042=off' machine option to the QEMU command-line. A notable side effect of disabling the i8042 controller in QEMU is that the vmport device won't be created. For this reason we will not allow setting the vmport feature if the ps2 feature is explicitly disabled. Signed-off-by: Kamil Szczęk <kamil@xxxxxxxxxx> --- docs/formatdomain.rst | 5 +++++ src/conf/domain_conf.c | 6 +++++- src/conf/domain_conf.h | 1 + src/conf/domain_validate.c | 23 +++++++++++++++++++++++ src/conf/schemas/domaincommon.rng | 5 +++++ src/qemu/qemu_capabilities.c | 21 +++++++++++++++++++++ src/qemu/qemu_capabilities.h | 4 ++++ src/qemu/qemu_command.c | 5 +++++ src/qemu/qemu_domain.c | 3 ++- src/qemu/qemu_validate.c | 23 +++++++++++++++++++++++ 10 files changed, 94 insertions(+), 2 deletions(-) diff --git a/docs/formatdomain.rst b/docs/formatdomain.rst index c56b739b23..3300a57393 100644 --- a/docs/formatdomain.rst +++ b/docs/formatdomain.rst @@ -2262,6 +2262,11 @@ are: exceptions when enabled (``on``). If the attribute is not defined, the hypervisor default will be used. :since:`Since 10.4.0` (QEMU/KVM and ARM virt guests only) +``ps2`` + Depending on the ``state`` attribute (values ``on``, ``off``) enable or + disable the emulation of a PS/2 controller used by ``ps2`` bus input devices. + If the attribute is not defined, the hypervisor default will be used. + :since:`Since 10.7.0` (QEMU only) Time keeping ------------ diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 86b563fbfb..19d14eb13d 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -185,6 +185,7 @@ VIR_ENUM_IMPL(virDomainFeature, "tcg", "async-teardown", "ras", + "ps2", ); VIR_ENUM_IMPL(virDomainCapabilitiesPolicy, @@ -17015,7 +17016,8 @@ virDomainFeaturesDefParse(virDomainDef *def, case VIR_DOMAIN_FEATURE_HTM: case VIR_DOMAIN_FEATURE_NESTED_HV: case VIR_DOMAIN_FEATURE_CCF_ASSIST: - case VIR_DOMAIN_FEATURE_RAS: { + case VIR_DOMAIN_FEATURE_RAS: + case VIR_DOMAIN_FEATURE_PS2: { virTristateSwitch state; if (virXMLPropTristateSwitch(nodes[i], "state", @@ -20879,6 +20881,7 @@ virDomainDefFeaturesCheckABIStability(virDomainDef *src, case VIR_DOMAIN_FEATURE_NESTED_HV: case VIR_DOMAIN_FEATURE_CCF_ASSIST: case VIR_DOMAIN_FEATURE_RAS: + case VIR_DOMAIN_FEATURE_PS2: if (src->features[i] != dst->features[i]) { virReportError(VIR_ERR_CONFIG_UNSUPPORTED, _("State of feature '%1$s' differs: source: '%2$s', destination: '%3$s'"), @@ -27670,6 +27673,7 @@ virDomainDefFormatFeatures(virBuffer *buf, case VIR_DOMAIN_FEATURE_NESTED_HV: case VIR_DOMAIN_FEATURE_CCF_ASSIST: case VIR_DOMAIN_FEATURE_RAS: + case VIR_DOMAIN_FEATURE_PS2: switch ((virTristateSwitch) def->features[i]) { case VIR_TRISTATE_SWITCH_LAST: case VIR_TRISTATE_SWITCH_ABSENT: diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 95ddf5470e..5b9ed67232 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -2181,6 +2181,7 @@ typedef enum { VIR_DOMAIN_FEATURE_TCG, VIR_DOMAIN_FEATURE_ASYNC_TEARDOWN, VIR_DOMAIN_FEATURE_RAS, + VIR_DOMAIN_FEATURE_PS2, VIR_DOMAIN_FEATURE_LAST } virDomainFeature; diff --git a/src/conf/domain_validate.c b/src/conf/domain_validate.c index 39b8d67928..56f1092841 100644 --- a/src/conf/domain_validate.c +++ b/src/conf/domain_validate.c @@ -2744,6 +2744,29 @@ virDomainInputDefValidate(const virDomainInputDef *input, return -1; } + switch ((virDomainInputBus) input->bus) { + case VIR_DOMAIN_INPUT_BUS_PS2: + if (def->features[VIR_DOMAIN_FEATURE_PS2] == VIR_TRISTATE_SWITCH_OFF) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("ps2 bus inputs require the ps2 feature not to be disabled")); + return -1; + } + break; + + case VIR_DOMAIN_INPUT_BUS_DEFAULT: + case VIR_DOMAIN_INPUT_BUS_USB: + case VIR_DOMAIN_INPUT_BUS_XEN: + case VIR_DOMAIN_INPUT_BUS_PARALLELS: + case VIR_DOMAIN_INPUT_BUS_VIRTIO: + case VIR_DOMAIN_INPUT_BUS_NONE: + break; + + case VIR_DOMAIN_INPUT_BUS_LAST: + default: + virReportEnumRangeError(virDomainInputBus, input->bus); + return -1; + } + return 0; } diff --git a/src/conf/schemas/domaincommon.rng b/src/conf/schemas/domaincommon.rng index 7d58dce465..9b6dddae81 100644 --- a/src/conf/schemas/domaincommon.rng +++ b/src/conf/schemas/domaincommon.rng @@ -6912,6 +6912,11 @@ <ref name="featurestate"/> </element> </optional> + <optional> + <element name="ps2"> + <ref name="featurestate"/> + </element> + </optional> </interleave> </element> </optional> diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c index 19a057d94d..4c135b2558 100644 --- a/src/qemu/qemu_capabilities.c +++ b/src/qemu/qemu_capabilities.c @@ -713,6 +713,7 @@ VIR_ENUM_IMPL(virQEMUCaps, "sev-snp-guest", /* QEMU_CAPS_SEV_SNP_GUEST */ "netdev.user", /* QEMU_CAPS_NETDEV_USER */ "acpi-erst", /* QEMU_CAPS_DEVICE_ACPI_ERST */ + "machine-i8042-opt", /* QEMU_CAPS_MACHINE_I8042_OPT */ ); @@ -1748,6 +1749,10 @@ static struct virQEMUCapsStringFlags virQEMUCapsMachinePropsVirt[] = { { "ras", QEMU_CAPS_MACHINE_VIRT_RAS }, }; +static struct virQEMUCapsStringFlags virQEMUCapsMachinePropsPC[] = { + { "i8042", QEMU_CAPS_MACHINE_I8042_OPT }, +}; + static struct virQEMUCapsStringFlags virQEMUCapsMachinePropsGeneric[] = { { "confidential-guest-support", QEMU_CAPS_MACHINE_CONFIDENTAL_GUEST_SUPPORT }, }; @@ -1759,6 +1764,9 @@ static virQEMUCapsObjectTypeProps virQEMUCapsMachineProps[] = { { "virt", virQEMUCapsMachinePropsVirt, G_N_ELEMENTS(virQEMUCapsMachinePropsVirt), -1 }, + { "pc", virQEMUCapsMachinePropsPC, + G_N_ELEMENTS(virQEMUCapsMachinePropsPC), + -1 }, { "none", virQEMUCapsMachinePropsGeneric, G_N_ELEMENTS(virQEMUCapsMachinePropsGeneric), -1 }, @@ -6026,6 +6034,19 @@ virQEMUCapsSupportsI8042(virQEMUCaps *qemuCaps, STREQ(def->os.machine, "isapc"); } +bool +virQEMUCapsSupportsI8042Toggle(virQEMUCaps *qemuCaps, + const virDomainDef *def) +{ + if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_MACHINE_I8042_OPT)) + return false; + + return qemuDomainIsI440FX(def) || + qemuDomainIsQ35(def) || + qemuDomainIsXenFV(def) || + STREQ(def->os.machine, "isapc"); +} + /* * The preferred machine to use if none is listed explicitly diff --git a/src/qemu/qemu_capabilities.h b/src/qemu/qemu_capabilities.h index d77a4bf4d9..d21cbfbce4 100644 --- a/src/qemu/qemu_capabilities.h +++ b/src/qemu/qemu_capabilities.h @@ -692,6 +692,7 @@ typedef enum { /* virQEMUCapsFlags grouping marker for syntax-check */ QEMU_CAPS_SEV_SNP_GUEST, /* -object sev-snp-guest */ QEMU_CAPS_NETDEV_USER, /* -netdev user */ QEMU_CAPS_DEVICE_ACPI_ERST, /* -device acpi-erst */ + QEMU_CAPS_MACHINE_I8042_OPT, /* -machine xxx,i8042=on/off */ QEMU_CAPS_LAST /* this must always be the last item */ } virQEMUCapsFlags; @@ -720,6 +721,9 @@ bool virQEMUCapsSupportsVmport(virQEMUCaps *qemuCaps, bool virQEMUCapsSupportsI8042(virQEMUCaps *qemuCaps, const virDomainDef *def); +bool virQEMUCapsSupportsI8042Toggle(virQEMUCaps *qemuCaps, + const virDomainDef *def); + const char *virQEMUCapsGetBinary(virQEMUCaps *qemuCaps); virArch virQEMUCapsGetArch(virQEMUCaps *qemuCaps); unsigned int virQEMUCapsGetVersion(virQEMUCaps *qemuCaps); diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index f15e6bda1e..53fbf49e20 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -6887,6 +6887,11 @@ qemuAppendDomainFeaturesMachineParam(virBuffer *buf, virBufferAsprintf(buf, ",ras=%s", str); } + if (def->features[VIR_DOMAIN_FEATURE_PS2] != VIR_TRISTATE_SWITCH_ABSENT) { + const char *str = virTristateSwitchTypeToString(def->features[VIR_DOMAIN_FEATURE_PS2]); + virBufferAsprintf(buf, ",i8042=%s", str); + } + return 0; } diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index 56f09699db..66b0caac24 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -3926,7 +3926,8 @@ static int qemuDomainDefAddImplicitInputDevice(virDomainDef *def, virQEMUCaps *qemuCaps) { - if (virQEMUCapsSupportsI8042(qemuCaps, def)) { + 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) diff --git a/src/qemu/qemu_validate.c b/src/qemu/qemu_validate.c index 7730344c52..2007d040fe 100644 --- a/src/qemu/qemu_validate.c +++ b/src/qemu/qemu_validate.c @@ -143,6 +143,13 @@ qemuValidateDomainDefFeatures(const virDomainDef *def, _("vmport is not available with this QEMU binary")); return -1; } + + if (def->features[i] == VIR_TRISTATE_SWITCH_ON && + def->features[VIR_DOMAIN_FEATURE_PS2] == VIR_TRISTATE_SWITCH_OFF) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("vmport feature requires the ps2 feature not to be disabled")); + return -1; + } break; case VIR_DOMAIN_FEATURE_VMCOREINFO: @@ -242,6 +249,22 @@ qemuValidateDomainDefFeatures(const virDomainDef *def, } break; + case VIR_DOMAIN_FEATURE_PS2: + if (def->features[i] != VIR_TRISTATE_SWITCH_ABSENT && + !virQEMUCapsSupportsI8042(qemuCaps, def)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("ps2 feature is not available with this QEMU binary")); + return -1; + } + + if (def->features[i] != VIR_TRISTATE_SWITCH_ABSENT && + !virQEMUCapsSupportsI8042Toggle(qemuCaps, def)) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("ps2 feature state cannot be controlled with this QEMU binary")); + return -1; + } + break; + case VIR_DOMAIN_FEATURE_SMM: case VIR_DOMAIN_FEATURE_KVM: case VIR_DOMAIN_FEATURE_XEN: -- 2.45.0