tests/functional/x86_64: add functional test to exercise vm fd change on reset
A new functional test is added that exercises the code changes related to closing of the old KVM VM file descriptor and opening a new one upon VM reset. This normally happens when confidential guests are reset but for non-confidential guests, we use a special machine specific debug/test parameter 'x-change-vmfd-on-reset' to enable this behavior. Only specific code changes related to re-initialisation of SEV-ES, SEV-SNP and TDX platforms are not exercised in this test as they require hardware that supports running confidential guests. Signed-off-by: Ani Sinha <anisinha@redhat.com> Link: https://lore.kernel.org/r/20260225035000.385950-34-anisinha@redhat.com Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Ani Sinha committed
Feb 25, 2026 at 09:19 UTC
da4ba40695beb4df4907b9ce7e88a8beb8dc9c37
3 files changed
+138
MAINTAINERS
+1
@@ -157,6 +157,7 @@ M: Ani Sinha <anisinha@redhat.com>
157
M: Paolo Bonzini <pbonzini@redhat.com>
158
S: Maintained
159
F: stubs/kvm.c
160
+F: tests/functional/x86_64/test_rebuild_vmfd.py
161
162
Guest CPU cores (TCG)
163
---------------------
tests/functional/x86_64/meson.build
+1
@@ -37,4 +37,5 @@ tests_x86_64_system_thorough = [
37
'vhost_user_bridge',
38
'virtio_balloon',
39
'virtio_gpu',
40
+ 'rebuild_vmfd',
41
]
tests/functional/x86_64/test_rebuild_vmfd.py
new
+136
@@ -0,0 +1,136 @@
1
+#!/usr/bin/env python3
2
+#
3
+# Functional tests exercising guest KVM file descriptor change on reset.
4
+#
5
+# Copyright © 2026 Red Hat, Inc.
6
+#
7
+# Author:
8
+# Ani Sinha <anisinha@redhat.com>
9
+#
10
+# SPDX-License-Identifier: GPL-2.0-or-later
11
+
12
+import os
13
+from qemu.machine import machine
14
+
15
+from qemu_test import QemuSystemTest, Asset, exec_command_and_wait_for_pattern
16
+from qemu_test import wait_for_console_pattern
17
+
18
+class KVMGuest(QemuSystemTest):
19
+
20
+ # ASSET UKI was generated using
21
+ # https://gitlab.com/kraxel/edk2-tests/-/blob/unittest/tools/make-supermin.sh
22
+ ASSET_UKI = Asset('https://gitlab.com/anisinha/misc-artifacts/'
23
+ '-/raw/main/uki.x86-64.efi?ref_type=heads',
24
+ 'e0f806bd1fa24111312e1fe849d2ee69808d4343930a5'
25
+ 'dc8c1688da17c65f576')
26
+ # ASSET_OVMF comes from /usr/share/edk2/ovmf/OVMF.stateless.fd of a
27
+ # fedora core 43 distribution which in turn comes from the
28
+ # edk2-ovmf-20251119-3.fc43.noarch rpm of that distribution.
29
+ ASSET_OVMF = Asset('https://gitlab.com/anisinha/misc-artifacts/'
30
+ '-/raw/main/OVMF.stateless.fd?ref_type=heads',
31
+ '58a4275aafa8774bd6b1540adceae4ea434b8db75b476'
32
+ '11839ff47be88cfcf22')
33
+
34
+ def common_vm_setup(self, kvm_args=None, cpu_args=None):
35
+ self.set_machine('q35')
36
+ self.require_accelerator("kvm")
37
+
38
+ self.vm.set_console()
39
+ if kvm_args:
40
+ self.vm.add_args("-accel", "kvm,%s" %kvm_args)
41
+ else:
42
+ self.vm.add_args("-accel", "kvm")
43
+ self.vm.add_args("-smp", "2")
44
+ if cpu_args:
45
+ self.vm.add_args("-cpu", "host,%s" %cpu_args)
46
+ else:
47
+ self.vm.add_args("-cpu", "host")
48
+ self.vm.add_args("-m", "2G")
49
+ self.vm.add_args("-nographic", "-nodefaults")
50
+
51
+
52
+ self.uki_path = self.ASSET_UKI.fetch()
53
+ self.ovmf_path = self.ASSET_OVMF.fetch()
54
+
55
+ self.vm.add_args('-kernel', self.uki_path)
56
+ self.vm.add_args("-bios", self.ovmf_path)
57
+ # enable KVM VMFD change on reset for a non-coco VM
58
+ self.vm.add_args("-machine", "q35,x-change-vmfd-on-reset=on")
59
+
60
+ # enable tracing of basic vmfd change function
61
+ self.vm.add_args("--trace", "kvm_reset_vmfd")
62
+
63
+ def launch_vm(self):
64
+ try:
65
+ self.vm.launch()
66
+ except machine.VMLaunchFailure as e:
67
+ if "Xen HVM guest support not present" in e.output:
68
+ self.skipTest("KVM Xen support is not present "
69
+ "(need v5.12+ kernel with CONFIG_KVM_XEN)")
70
+ elif "Property 'kvm-accel.xen-version' not found" in e.output:
71
+ self.skipTest("QEMU not built with CONFIG_XEN_EMU support")
72
+ else:
73
+ raise e
74
+
75
+ self.log.info('VM launched')
76
+ console_pattern = 'bash-5.1#'
77
+ wait_for_console_pattern(self, console_pattern)
78
+ self.log.info('VM ready with a bash prompt')
79
+
80
+ def vm_console_reset(self):
81
+ exec_command_and_wait_for_pattern(self, '/usr/sbin/reboot -f',
82
+ 'reboot: machine restart')
83
+ console_pattern = '# --- Hello world ---'
84
+ wait_for_console_pattern(self, console_pattern)
85
+ self.vm.shutdown()
86
+
87
+ def vm_qmp_reset(self):
88
+ self.vm.qmp('system_reset')
89
+ console_pattern = '# --- Hello world ---'
90
+ wait_for_console_pattern(self, console_pattern)
91
+ self.vm.shutdown()
92
+
93
+ def check_logs(self):
94
+ self.assertRegex(self.vm.get_log(),
95
+ r'kvm_reset_vmfd')
96
+ self.assertRegex(self.vm.get_log(),
97
+ r'virtual machine state has been rebuilt')
98
+
99
+ def test_reset_console(self):
100
+ self.common_vm_setup()
101
+ self.launch_vm()
102
+ self.vm_console_reset()
103
+ self.check_logs()
104
+
105
+ def test_reset_qmp(self):
106
+ self.common_vm_setup()
107
+ self.launch_vm()
108
+ self.vm_qmp_reset()
109
+ self.check_logs()
110
+
111
+ def test_reset_kvmpit(self):
112
+ self.common_vm_setup()
113
+ self.vm.add_args("--trace", "kvmpit_post_vmfd_change")
114
+ self.launch_vm()
115
+ self.vm_console_reset()
116
+ self.assertRegex(self.vm.get_log(),
117
+ r'kvmpit_post_vmfd_change')
118
+
119
+ def test_reset_xen_emulation(self):
120
+ self.common_vm_setup("xen-version=0x4000a,kernel-irqchip=split")
121
+ self.launch_vm()
122
+ self.vm_console_reset()
123
+ self.check_logs()
124
+
125
+ def test_reset_hyperv_vmbus(self):
126
+ self.common_vm_setup(None, "hv-syndbg,hv-relaxed,hv_time,hv-synic,"
127
+ "hv-vpindex,hv-runtime,hv-stimer")
128
+ self.vm.add_args("-device", "vmbus-bridge,irq=15")
129
+ self.vm.add_args("-trace", "vmbus_handle_vmfd_change")
130
+ self.launch_vm()
131
+ self.vm_console_reset()
132
+ self.assertRegex(self.vm.get_log(),
133
+ r'vmbus_handle_vmfd_change')
134
+
135
+if __name__ == '__main__':
136
+ QemuSystemTest.main()