QEMU 2.1 added support for the kvm=off option to the -cpu command,
allowing the KVM hypervisor signature to be hidden from the guest.
This enables disabling of some paravirualization features in the
guest as well as allowing certain drivers which test for the
hypervisor to load. Domain XML syntax is as follows:
<domain type='kvm>
...
<features>
...
<kvm>
<hidden state='on'/>
</kvm>
</features>
...
Signed-off-by: Alex Williamson <alex.williamson(a)redhat.com>
---
Hearing no further comments, here's v2:
- white space fix in docs/formatdomain.html.in
- s/virBufferAsprintf/virBufferAddLit/ in src/qemu/qemu_command.c
docs/formatdomain.html.in | 21 ++++
docs/schemas/domaincommon.rng | 18 +++-
src/conf/domain_conf.c | 100 ++++++++++++++++++++
src/conf/domain_conf.h | 9 ++
src/qemu/qemu_command.c | 22 ++++
tests/qemuargv2xmltest.c | 2
.../qemuxml2argv-kvm-features-off.args | 5 +
.../qemuxml2argv-kvm-features-off.xml | 27 +++++
.../qemuxml2argv-kvm-features.args | 5 +
.../qemuxml2argvdata/qemuxml2argv-kvm-features.xml | 27 +++++
tests/qemuxml2argvtest.c | 3 +
tests/qemuxml2xmltest.c | 3 +
12 files changed, 240 insertions(+), 2 deletions(-)
create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-kvm-features-off.args
create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-kvm-features-off.xml
create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-kvm-features.args
create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-kvm-features.xml
diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in
index bd99ae0..bcc0bbd 100644
--- a/docs/formatdomain.html.in
+++ b/docs/formatdomain.html.in
@@ -1232,6 +1232,9 @@
<vapic state='on'/>
<spinlocks state='on' retries='4096'/>
</hyperv>
+ <kvm>
+ <hidden state='on'/>
+ </kvm>
<pvspinlock/>
</features>
@@ -1310,7 +1313,23 @@
can be explicitly disabled by using
<code>state='off'</code>
attribute.
</dd>
-
+ <dt><code>kvm</code></dt>
+ <dd>Various features to change the behavior of the KVM hypervisor.
+ <table class="top_table">
+ <tr>
+ <th>Feature</th>
+ <th>Description</th>
+ <th>Value</th>
+ <th>Since</th>
+ </tr>
+ <tr>
+ <td>hidden</td>
+ <td>Hide the KVM hypervisor from standard MSR based discovery</td>
+ <td>on, off</td>
+ <td><span class="since">2.1.0 (QEMU
only)</span></td>
+ </tr>
+ </table>
+ </dd>
</dl>
<h3><a name="elementsTime">Time keeping</a></h3>
diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng
index 033f2f6..5c99a14 100644
--- a/docs/schemas/domaincommon.rng
+++ b/docs/schemas/domaincommon.rng
@@ -3826,7 +3826,7 @@
</define>
<!--
A set of optional features: PAE, APIC, ACPI,
- HyperV Enlightenment, paravirtual spinlocks and HAP support
+ HyperV Enlightenment, KVM features, paravirtual spinlocks and HAP support
-->
<define name="features">
<optional>
@@ -3868,6 +3868,9 @@
</element>
</optional>
<optional>
+ <ref name="kvm"/>
+ </optional>
+ <optional>
<element name="privnet">
<empty/>
</element>
@@ -4472,6 +4475,19 @@
</element>
</define>
+ <!-- Optional KVM features -->
+ <define name="kvm">
+ <element name="kvm">
+ <interleave>
+ <optional>
+ <element name="hidden">
+ <ref name="featurestate"/>
+ </element>
+ </optional>
+ </interleave>
+ </element>
+ </define>
+
<!-- Optional capabilities features -->
<define name="capabilities">
<element name="capabilities">
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index 934f6cb..8b0bdb0 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -142,6 +142,7 @@ VIR_ENUM_IMPL(virDomainFeature, VIR_DOMAIN_FEATURE_LAST,
"viridian",
"privnet",
"hyperv",
+ "kvm",
"pvspinlock",
"capabilities")
@@ -155,6 +156,9 @@ VIR_ENUM_IMPL(virDomainHyperv, VIR_DOMAIN_HYPERV_LAST,
"vapic",
"spinlocks")
+VIR_ENUM_IMPL(virDomainKVM, VIR_DOMAIN_KVM_LAST,
+ "hidden")
+
VIR_ENUM_IMPL(virDomainCapsFeature, VIR_DOMAIN_CAPS_FEATURE_LAST,
"audit_control",
"audit_write",
@@ -12168,6 +12172,7 @@ virDomainDefParseXML(xmlDocPtr xml,
case VIR_DOMAIN_FEATURE_VIRIDIAN:
case VIR_DOMAIN_FEATURE_PRIVNET:
case VIR_DOMAIN_FEATURE_HYPERV:
+ case VIR_DOMAIN_FEATURE_KVM:
def->features[val] = VIR_TRISTATE_SWITCH_ON;
break;
@@ -12295,6 +12300,54 @@ virDomainDefParseXML(xmlDocPtr xml,
ctxt->node = node;
}
+ if (def->features[VIR_DOMAIN_FEATURE_KVM] == VIR_TRISTATE_SWITCH_ON) {
+ int feature;
+ int value;
+ node = ctxt->node;
+ if ((n = virXPathNodeSet("./features/kvm/*", ctxt, &nodes)) <
0)
+ goto error;
+
+ for (i = 0; i < n; i++) {
+ feature = virDomainKVMTypeFromString((const char *)nodes[i]->name);
+ if (feature < 0) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+ _("unsupported KVM feature: %s"),
+ nodes[i]->name);
+ goto error;
+ }
+
+ ctxt->node = nodes[i];
+
+ switch ((virDomainKVM) feature) {
+ case VIR_DOMAIN_KVM_HIDDEN:
+ if (!(tmp = virXPathString("string(./@state)", ctxt))) {
+ virReportError(VIR_ERR_XML_ERROR,
+ _("missing 'state' attribute for
"
+ "KVM feature '%s'"),
+ nodes[i]->name);
+ goto error;
+ }
+
+ if ((value = virTristateSwitchTypeFromString(tmp)) < 0) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+ _("invalid value of state argument "
+ "for KVM feature '%s'"),
+ nodes[i]->name);
+ goto error;
+ }
+
+ VIR_FREE(tmp);
+ def->kvm_features[feature] = value;
+ break;
+
+ case VIR_DOMAIN_KVM_LAST:
+ break;
+ }
+ }
+ VIR_FREE(nodes);
+ ctxt->node = node;
+ }
+
if ((n = virXPathNodeSet("./features/capabilities/*", ctxt, &nodes))
< 0)
goto error;
@@ -14303,6 +14356,29 @@ virDomainDefFeaturesCheckABIStability(virDomainDefPtr src,
}
}
+ /* kvm */
+ if (src->features[VIR_DOMAIN_FEATURE_KVM] == VIR_TRISTATE_SWITCH_ON) {
+ for (i = 0; i < VIR_DOMAIN_KVM_LAST; i++) {
+ switch ((virDomainKVM) i) {
+ case VIR_DOMAIN_KVM_HIDDEN:
+ if (src->kvm_features[i] != dst->kvm_features[i]) {
+ virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
+ _("State of KVM feature '%s' differs:
"
+ "source: '%s', destination:
'%s'"),
+ virDomainKVMTypeToString(i),
+
virTristateSwitchTypeToString(src->kvm_features[i]),
+
virTristateSwitchTypeToString(dst->kvm_features[i]));
+ return false;
+ }
+
+ break;
+
+ case VIR_DOMAIN_KVM_LAST:
+ break;
+ }
+ }
+ }
+
return true;
}
@@ -18133,6 +18209,30 @@ virDomainDefFormatInternal(virDomainDefPtr def,
virBufferAddLit(buf, "</hyperv>\n");
break;
+ case VIR_DOMAIN_FEATURE_KVM:
+ if (def->features[i] != VIR_TRISTATE_SWITCH_ON)
+ break;
+
+ virBufferAddLit(buf, "<kvm>\n");
+ virBufferAdjustIndent(buf, 2);
+ for (j = 0; j < VIR_DOMAIN_KVM_LAST; j++) {
+ switch ((virDomainKVM) j) {
+ case VIR_DOMAIN_KVM_HIDDEN:
+ if (def->kvm_features[j])
+ virBufferAsprintf(buf, "<%s
state='%s'/>\n",
+ virDomainKVMTypeToString(j),
+ virTristateSwitchTypeToString(
+ def->kvm_features[j]));
+ break;
+
+ case VIR_DOMAIN_KVM_LAST:
+ break;
+ }
+ }
+ virBufferAdjustIndent(buf, -2);
+ virBufferAddLit(buf, "</kvm>\n");
+ break;
+
case VIR_DOMAIN_FEATURE_CAPABILITIES:
if (def->features[i] == VIR_DOMAIN_CAPABILITIES_POLICY_DEFAULT
&&
!virDomainDefHasCapabilitiesFeatures(def))
diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h
index ff7d640..cca9c0f 100644
--- a/src/conf/domain_conf.h
+++ b/src/conf/domain_conf.h
@@ -1515,6 +1515,7 @@ typedef enum {
VIR_DOMAIN_FEATURE_VIRIDIAN,
VIR_DOMAIN_FEATURE_PRIVNET,
VIR_DOMAIN_FEATURE_HYPERV,
+ VIR_DOMAIN_FEATURE_KVM,
VIR_DOMAIN_FEATURE_PVSPINLOCK,
VIR_DOMAIN_FEATURE_CAPABILITIES,
@@ -1530,6 +1531,12 @@ typedef enum {
} virDomainHyperv;
typedef enum {
+ VIR_DOMAIN_KVM_HIDDEN = 0,
+
+ VIR_DOMAIN_KVM_LAST
+} virDomainKVM;
+
+typedef enum {
VIR_DOMAIN_CAPABILITIES_POLICY_DEFAULT = 0,
VIR_DOMAIN_CAPABILITIES_POLICY_ALLOW,
VIR_DOMAIN_CAPABILITIES_POLICY_DENY,
@@ -1943,6 +1950,7 @@ struct _virDomainDef {
int features[VIR_DOMAIN_FEATURE_LAST];
int apic_eoi;
int hyperv_features[VIR_DOMAIN_HYPERV_LAST];
+ int kvm_features[VIR_DOMAIN_KVM_LAST];
unsigned int hyperv_spinlocks;
/* These options are of type virTristateSwitch: ON = keep, OFF = drop */
@@ -2626,6 +2634,7 @@ VIR_ENUM_DECL(virDomainGraphicsSpiceStreamingMode)
VIR_ENUM_DECL(virDomainGraphicsSpiceMouseMode)
VIR_ENUM_DECL(virDomainGraphicsVNCSharePolicy)
VIR_ENUM_DECL(virDomainHyperv)
+VIR_ENUM_DECL(virDomainKVM)
VIR_ENUM_DECL(virDomainRNGModel)
VIR_ENUM_DECL(virDomainRNGBackend)
VIR_ENUM_DECL(virDomainTPMModel)
diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c
index 8a69976..d9f4b71 100644
--- a/src/qemu/qemu_command.c
+++ b/src/qemu/qemu_command.c
@@ -6228,6 +6228,25 @@ qemuBuildCpuArgStr(virQEMUDriverPtr driver,
}
}
+ if (def->features[VIR_DOMAIN_FEATURE_KVM] == VIR_TRISTATE_SWITCH_ON) {
+ if (!have_cpu) {
+ virBufferAdd(&buf, default_model, -1);
+ have_cpu = true;
+ }
+
+ for (i = 0; i < VIR_DOMAIN_KVM_LAST; i++) {
+ switch ((virDomainKVM) i) {
+ case VIR_DOMAIN_KVM_HIDDEN:
+ if (def->kvm_features[i] == VIR_TRISTATE_SWITCH_ON)
+ virBufferAddLit(&buf, ",kvm=off");
+ break;
+
+ case VIR_DOMAIN_KVM_LAST:
+ break;
+ }
+ }
+ }
+
if (virBufferCheckError(&buf) < 0)
goto cleanup;
@@ -10692,6 +10711,9 @@ qemuParseCommandLineCPU(virDomainDefPtr dom,
}
virStringFreeList(hv_tokens);
hv_tokens = NULL;
+ } else if (STREQ(tokens[i], "kvm=off")) {
+ dom->features[VIR_DOMAIN_FEATURE_KVM] = VIR_TRISTATE_SWITCH_ON;
+ dom->kvm_features[VIR_DOMAIN_KVM_HIDDEN] = VIR_TRISTATE_SWITCH_ON;
}
}
diff --git a/tests/qemuargv2xmltest.c b/tests/qemuargv2xmltest.c
index 6bad7d6..9c00cd9 100644
--- a/tests/qemuargv2xmltest.c
+++ b/tests/qemuargv2xmltest.c
@@ -284,6 +284,8 @@ mymain(void)
DO_TEST("hyperv");
+ DO_TEST("kvm-features");
+
DO_TEST("pseries-nvram");
DO_TEST("pseries-disk");
diff --git a/tests/qemuxml2argvdata/qemuxml2argv-kvm-features-off.args
b/tests/qemuxml2argvdata/qemuxml2argv-kvm-features-off.args
new file mode 100644
index 0000000..363fd2a
--- /dev/null
+++ b/tests/qemuxml2argvdata/qemuxml2argv-kvm-features-off.args
@@ -0,0 +1,5 @@
+LC_ALL=C PATH=/bin HOME=/home/test USER=test LOGNAME=test QEMU_AUDIO_DRV=none \
+/usr/bin/qemu \
+-S -M pc -cpu qemu32 -m 214 -smp 6 -nographic \
+-monitor unix:/tmp/test-monitor,server,nowait \
+-boot n -usb -net none -serial none -parallel none
diff --git a/tests/qemuxml2argvdata/qemuxml2argv-kvm-features-off.xml
b/tests/qemuxml2argvdata/qemuxml2argv-kvm-features-off.xml
new file mode 100644
index 0000000..64b8cd8
--- /dev/null
+++ b/tests/qemuxml2argvdata/qemuxml2argv-kvm-features-off.xml
@@ -0,0 +1,27 @@
+<domain type='qemu'>
+ <name>QEMUGuest1</name>
+ <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid>
+ <memory unit='KiB'>219100</memory>
+ <currentMemory unit='KiB'>219100</currentMemory>
+ <vcpu placement='static'>6</vcpu>
+ <os>
+ <type arch='i686' machine='pc'>hvm</type>
+ <boot dev='network'/>
+ </os>
+ <features>
+ <acpi/>
+ <kvm>
+ <hidden state='off'/>
+ </kvm>
+ </features>
+ <clock offset='utc'/>
+ <on_poweroff>destroy</on_poweroff>
+ <on_reboot>restart</on_reboot>
+ <on_crash>destroy</on_crash>
+ <devices>
+ <emulator>/usr/bin/qemu</emulator>
+ <controller type='usb' index='0'/>
+ <controller type='pci' index='0' model='pci-root'/>
+ <memballoon model='virtio'/>
+ </devices>
+</domain>
diff --git a/tests/qemuxml2argvdata/qemuxml2argv-kvm-features.args
b/tests/qemuxml2argvdata/qemuxml2argv-kvm-features.args
new file mode 100644
index 0000000..b223951
--- /dev/null
+++ b/tests/qemuxml2argvdata/qemuxml2argv-kvm-features.args
@@ -0,0 +1,5 @@
+LC_ALL=C PATH=/bin HOME=/home/test USER=test LOGNAME=test QEMU_AUDIO_DRV=none \
+/usr/bin/qemu -S -M pc \
+-cpu qemu32,kvm=off -m 214 -smp 6 -nographic -monitor \
+unix:/tmp/test-monitor,server,nowait -boot n -usb -net none -serial none \
+-parallel none
diff --git a/tests/qemuxml2argvdata/qemuxml2argv-kvm-features.xml
b/tests/qemuxml2argvdata/qemuxml2argv-kvm-features.xml
new file mode 100644
index 0000000..3f2817e
--- /dev/null
+++ b/tests/qemuxml2argvdata/qemuxml2argv-kvm-features.xml
@@ -0,0 +1,27 @@
+<domain type='qemu'>
+ <name>QEMUGuest1</name>
+ <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid>
+ <memory unit='KiB'>219100</memory>
+ <currentMemory unit='KiB'>219100</currentMemory>
+ <vcpu placement='static'>6</vcpu>
+ <os>
+ <type arch='i686' machine='pc'>hvm</type>
+ <boot dev='network'/>
+ </os>
+ <features>
+ <acpi/>
+ <kvm>
+ <hidden state='on'/>
+ </kvm>
+ </features>
+ <clock offset='utc'/>
+ <on_poweroff>destroy</on_poweroff>
+ <on_reboot>restart</on_reboot>
+ <on_crash>destroy</on_crash>
+ <devices>
+ <emulator>/usr/bin/qemu</emulator>
+ <controller type='usb' index='0'/>
+ <controller type='pci' index='0' model='pci-root'/>
+ <memballoon model='none'/>
+ </devices>
+</domain>
diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c
index 62b969c..427ebfc 100644
--- a/tests/qemuxml2argvtest.c
+++ b/tests/qemuxml2argvtest.c
@@ -666,6 +666,9 @@ mymain(void)
DO_TEST("hyperv", NONE);
DO_TEST("hyperv-off", NONE);
+ DO_TEST("kvm-features", NONE);
+ DO_TEST("kvm-features-off", NONE);
+
DO_TEST("hugepages", QEMU_CAPS_MEM_PATH);
DO_TEST("hugepages-pages", QEMU_CAPS_MEM_PATH,
QEMU_CAPS_OBJECT_MEMORY_RAM,
QEMU_CAPS_OBJECT_MEMORY_FILE);
diff --git a/tests/qemuxml2xmltest.c b/tests/qemuxml2xmltest.c
index 5941323..3a174c0 100644
--- a/tests/qemuxml2xmltest.c
+++ b/tests/qemuxml2xmltest.c
@@ -196,6 +196,9 @@ mymain(void)
DO_TEST("hyperv");
DO_TEST("hyperv-off");
+ DO_TEST("kvm-features");
+ DO_TEST("kvm-features-off");
+
DO_TEST("hugepages");
DO_TEST("hugepages-pages");
DO_TEST("hugepages-pages2");