diff --git a/MAINTAINERS b/MAINTAINERS
index 822f34344b..878409c121 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -476,6 +476,7 @@ F: docs/system/i386/sgx.rst
 F: target/i386/kvm/
 F: target/i386/sev*
 F: scripts/kvm/vmxcap
+F: tests/functional/test_x86_64_hotplug_cpu.py
 
 Xen emulation on X86 KVM CPUs
 M: David Woodhouse <dwmw2@infradead.org>
diff --git a/tests/functional/meson.build b/tests/functional/meson.build
index 1bc5ba5229..04f8b858ed 100644
--- a/tests/functional/meson.build
+++ b/tests/functional/meson.build
@@ -229,6 +229,7 @@ tests_x86_64_system_thorough = [
   'multiprocess',
   'netdev_ethtool',
   'virtio_gpu',
+  'x86_64_hotplug_cpu',
 ]
 
 tests_xtensa_system_thorough = [
diff --git a/tests/avocado/hotplug_cpu.py b/tests/functional/test_x86_64_hotplug_cpu.py
old mode 100644
new mode 100755
similarity index 25%
rename from tests/avocado/hotplug_cpu.py
rename to tests/functional/test_x86_64_hotplug_cpu.py
index 342c838539..b1d5156c72
--- a/tests/avocado/hotplug_cpu.py
+++ b/tests/functional/test_x86_64_hotplug_cpu.py
@@ -1,3 +1,5 @@
+#!/usr/bin/env python3
+#
 # Functional test that hotplugs a CPU and checks it on a Linux guest
 #
 # Copyright (c) 2021 Red Hat, Inc.
@@ -8,30 +10,60 @@
 # This work is licensed under the terms of the GNU GPL, version 2 or
 # later.  See the COPYING file in the top-level directory.
 
-from avocado_qemu.linuxtest import LinuxTest
+from qemu_test import LinuxKernelTest, Asset, exec_command_and_wait_for_pattern
+
+
+class HotPlugCPU(LinuxKernelTest):
 
+    ASSET_KERNEL = Asset(
+        ('https://archives.fedoraproject.org/pub/archive/fedora/linux/releases'
+         '/31/Server/x86_64/os/images/pxeboot/vmlinuz'),
+        'd4738d03dbbe083ca610d0821d0a8f1488bebbdccef54ce33e3adb35fda00129')
 
-class HotPlugCPU(LinuxTest):
+    ASSET_INITRD = Asset(
+        ('https://archives.fedoraproject.org/pub/archive/fedora/linux/releases'
+         '/31/Server/x86_64/os/images/pxeboot/initrd.img'),
+        '277cd6c7adf77c7e63d73bbb2cded8ef9e2d3a2f100000e92ff1f8396513cd8b')
+
+    def test_hotplug(self):
 
-    def test(self):
-        """
-        :avocado: tags=arch:x86_64
-        :avocado: tags=machine:q35
-        :avocado: tags=accel:kvm
-        """
         self.require_accelerator('kvm')
         self.vm.add_args('-accel', 'kvm')
         self.vm.add_args('-cpu', 'Haswell')
         self.vm.add_args('-smp', '1,sockets=1,cores=2,threads=1,maxcpus=2')
-        self.launch_and_wait()
+        self.vm.add_args('-m', '1G')
+        self.vm.add_args('-append', 'console=ttyS0 rd.rescue')
+
+        self.launch_kernel(self.ASSET_KERNEL.fetch(),
+                           self.ASSET_INITRD.fetch(),
+                           wait_for='Entering emergency mode.')
+        prompt = '# '
+        self.wait_for_console_pattern(prompt)
 
-        self.ssh_command('test -e /sys/devices/system/cpu/cpu0')
-        with self.assertRaises(AssertionError):
-            self.ssh_command('test -e /sys/devices/system/cpu/cpu1')
+        exec_command_and_wait_for_pattern(self,
+                                          'cd /sys/devices/system/cpu/cpu0',
+                                          'cpu0#')
+        exec_command_and_wait_for_pattern(self,
+                                          'cd /sys/devices/system/cpu/cpu1',
+                                          'No such file or directory')
 
         self.vm.cmd('device_add',
                     driver='Haswell-x86_64-cpu',
+                    id='c1',
                     socket_id=0,
                     core_id=1,
                     thread_id=0)
-        self.ssh_command('test -e /sys/devices/system/cpu/cpu1')
+        self.wait_for_console_pattern('CPU1 has been hot-added')
+
+        exec_command_and_wait_for_pattern(self,
+                                          'cd /sys/devices/system/cpu/cpu1',
+                                          'cpu1#')
+
+        self.vm.cmd('device_del', id='c1')
+
+        exec_command_and_wait_for_pattern(self,
+                                          'cd /sys/devices/system/cpu/cpu1',
+                                          'No such file or directory')
+
+if __name__ == '__main__':
+    LinuxKernelTest.main()
