diff options
Diffstat (limited to 'tests')
38 files changed, 926 insertions, 433 deletions
diff --git a/tests/avocado/migration.py b/tests/avocado/migration.py deleted file mode 100644 index be6234b3c2..0000000000 --- a/tests/avocado/migration.py +++ /dev/null @@ -1,135 +0,0 @@ -# Migration test -# -# Copyright (c) 2019 Red Hat, Inc. -# -# Authors: -# Cleber Rosa <crosa@redhat.com> -# Caio Carrara <ccarrara@redhat.com> -# -# This work is licensed under the terms of the GNU GPL, version 2 or -# later. See the COPYING file in the top-level directory. - - -import tempfile -import os - -from avocado_qemu import QemuSystemTest -from avocado import skipUnless - -from avocado.utils.network import ports -from avocado.utils import wait -from avocado.utils.path import find_command - - -class MigrationTest(QemuSystemTest): - """ - :avocado: tags=migration - """ - - timeout = 10 - - @staticmethod - def migration_finished(vm): - return vm.cmd('query-migrate')['status'] in ('completed', 'failed') - - def assert_migration(self, src_vm, dst_vm): - wait.wait_for(self.migration_finished, - timeout=self.timeout, - step=0.1, - args=(src_vm,)) - wait.wait_for(self.migration_finished, - timeout=self.timeout, - step=0.1, - args=(dst_vm,)) - self.assertEqual(src_vm.cmd('query-migrate')['status'], 'completed') - self.assertEqual(dst_vm.cmd('query-migrate')['status'], 'completed') - self.assertEqual(dst_vm.cmd('query-status')['status'], 'running') - self.assertEqual(src_vm.cmd('query-status')['status'],'postmigrate') - - def do_migrate(self, dest_uri, src_uri=None): - dest_vm = self.get_vm('-incoming', dest_uri) - dest_vm.add_args('-nodefaults') - dest_vm.launch() - if src_uri is None: - src_uri = dest_uri - source_vm = self.get_vm() - source_vm.add_args('-nodefaults') - source_vm.launch() - source_vm.qmp('migrate', uri=src_uri) - self.assert_migration(source_vm, dest_vm) - - def _get_free_port(self): - port = ports.find_free_port() - if port is None: - self.cancel('Failed to find a free port') - return port - - def migration_with_tcp_localhost(self): - dest_uri = 'tcp:localhost:%u' % self._get_free_port() - self.do_migrate(dest_uri) - - def migration_with_unix(self): - with tempfile.TemporaryDirectory(prefix='socket_') as socket_path: - dest_uri = 'unix:%s/qemu-test.sock' % socket_path - self.do_migrate(dest_uri) - - @skipUnless(find_command('nc', default=False), "'nc' command not found") - def migration_with_exec(self): - """The test works for both netcat-traditional and netcat-openbsd packages.""" - free_port = self._get_free_port() - dest_uri = 'exec:nc -l localhost %u' % free_port - src_uri = 'exec:nc localhost %u' % free_port - self.do_migrate(dest_uri, src_uri) - - -@skipUnless('aarch64' in os.uname()[4], "host != target") -class Aarch64(MigrationTest): - """ - :avocado: tags=arch:aarch64 - :avocado: tags=machine:virt - :avocado: tags=cpu:max - """ - - def test_migration_with_tcp_localhost(self): - self.migration_with_tcp_localhost() - - def test_migration_with_unix(self): - self.migration_with_unix() - - def test_migration_with_exec(self): - self.migration_with_exec() - - -@skipUnless('x86_64' in os.uname()[4], "host != target") -class X86_64(MigrationTest): - """ - :avocado: tags=arch:x86_64 - :avocado: tags=machine:pc - :avocado: tags=cpu:qemu64 - """ - - def test_migration_with_tcp_localhost(self): - self.migration_with_tcp_localhost() - - def test_migration_with_unix(self): - self.migration_with_unix() - - def test_migration_with_exec(self): - self.migration_with_exec() - - -@skipUnless('ppc64le' in os.uname()[4], "host != target") -class PPC64(MigrationTest): - """ - :avocado: tags=arch:ppc64 - :avocado: tags=machine:pseries - """ - - def test_migration_with_tcp_localhost(self): - self.migration_with_tcp_localhost() - - def test_migration_with_unix(self): - self.migration_with_unix() - - def test_migration_with_exec(self): - self.migration_with_exec() diff --git a/tests/avocado/replay_kernel.py b/tests/avocado/replay_kernel.py index e22c200a36..b9b54a8793 100644 --- a/tests/avocado/replay_kernel.py +++ b/tests/avocado/replay_kernel.py @@ -144,51 +144,6 @@ class ReplayKernelNormal(ReplayKernelBase): self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=5) - def test_mips_malta(self): - """ - :avocado: tags=arch:mips - :avocado: tags=machine:malta - :avocado: tags=endian:big - """ - deb_url = ('http://snapshot.debian.org/archive/debian/' - '20130217T032700Z/pool/main/l/linux-2.6/' - 'linux-image-2.6.32-5-4kc-malta_2.6.32-48_mips.deb') - deb_hash = 'a8cfc28ad8f45f54811fc6cf74fc43ffcfe0ba04' - deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash) - kernel_path = self.extract_from_deb(deb_path, - '/boot/vmlinux-2.6.32-5-4kc-malta') - kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'console=ttyS0' - console_pattern = 'Kernel command line: %s' % kernel_command_line - - self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=5) - - def test_mips64el_malta(self): - """ - This test requires the ar tool to extract "data.tar.gz" from - the Debian package. - - The kernel can be rebuilt using this Debian kernel source [1] and - following the instructions on [2]. - - [1] http://snapshot.debian.org/package/linux-2.6/2.6.32-48/ - #linux-source-2.6.32_2.6.32-48 - [2] https://kernel-team.pages.debian.net/kernel-handbook/ - ch-common-tasks.html#s-common-official - - :avocado: tags=arch:mips64el - :avocado: tags=machine:malta - """ - deb_url = ('http://snapshot.debian.org/archive/debian/' - '20130217T032700Z/pool/main/l/linux-2.6/' - 'linux-image-2.6.32-5-5kc-malta_2.6.32-48_mipsel.deb') - deb_hash = '1aaec92083bf22fda31e0d27fa8d9a388e5fc3d5' - deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash) - kernel_path = self.extract_from_deb(deb_path, - '/boot/vmlinux-2.6.32-5-5kc-malta') - kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'console=ttyS0' - console_pattern = 'Kernel command line: %s' % kernel_command_line - self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=5) - def test_aarch64_virt(self): """ :avocado: tags=arch:aarch64 @@ -455,123 +410,3 @@ class ReplayKernelNormal(ReplayKernelBase): '/qac-best-of-multiarch/download/day02.tar.xz') file_path = self.fetch_asset(tar_url, asset_hash=tar_hash) self.do_test_advcal_2018(file_path, 'santas-sleigh-ride.elf') - -@skipUnless(os.getenv('AVOCADO_TIMEOUT_EXPECTED'), 'Test might timeout') -class ReplayKernelSlow(ReplayKernelBase): - # Override the timeout, because this kernel includes an inner - # loop which is executed with TB recompilings during replay, - # making it very slow. - timeout = 180 - - def test_mips_malta_cpio(self): - """ - :avocado: tags=arch:mips - :avocado: tags=machine:malta - :avocado: tags=endian:big - :avocado: tags=slowness:high - """ - deb_url = ('http://snapshot.debian.org/archive/debian/' - '20160601T041800Z/pool/main/l/linux/' - 'linux-image-4.5.0-2-4kc-malta_4.5.5-1_mips.deb') - deb_hash = 'a3c84f3e88b54e06107d65a410d1d1e8e0f340f8' - deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash) - kernel_path = self.extract_from_deb(deb_path, - '/boot/vmlinux-4.5.0-2-4kc-malta') - initrd_url = ('https://github.com/groeck/linux-build-test/raw/' - '8584a59ed9e5eb5ee7ca91f6d74bbb06619205b8/rootfs/' - 'mips/rootfs.cpio.gz') - initrd_hash = 'bf806e17009360a866bf537f6de66590de349a99' - initrd_path_gz = self.fetch_asset(initrd_url, asset_hash=initrd_hash) - initrd_path = self.workdir + "rootfs.cpio" - archive.gzip_uncompress(initrd_path_gz, initrd_path) - - kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + - 'console=ttyS0 console=tty ' - 'rdinit=/sbin/init noreboot') - console_pattern = 'Boot successful.' - self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=5, - args=('-initrd', initrd_path)) - - @skipUnless(os.getenv('AVOCADO_ALLOW_UNTRUSTED_CODE'), 'untrusted code') - def test_mips64el_malta_5KEc_cpio(self): - """ - :avocado: tags=arch:mips64el - :avocado: tags=machine:malta - :avocado: tags=endian:little - :avocado: tags=slowness:high - :avocado: tags=cpu:5KEc - """ - kernel_url = ('https://github.com/philmd/qemu-testing-blob/' - 'raw/9ad2df38/mips/malta/mips64el/' - 'vmlinux-3.19.3.mtoman.20150408') - kernel_hash = '00d1d268fb9f7d8beda1de6bebcc46e884d71754' - kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) - initrd_url = ('https://github.com/groeck/linux-build-test/' - 'raw/8584a59e/rootfs/' - 'mipsel64/rootfs.mipsel64r1.cpio.gz') - initrd_hash = '1dbb8a396e916847325284dbe2151167' - initrd_path_gz = self.fetch_asset(initrd_url, algorithm='md5', - asset_hash=initrd_hash) - initrd_path = self.workdir + "rootfs.cpio" - archive.gzip_uncompress(initrd_path_gz, initrd_path) - - kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + - 'console=ttyS0 console=tty ' - 'rdinit=/sbin/init noreboot') - console_pattern = 'Boot successful.' - self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=5, - args=('-initrd', initrd_path)) - - def do_test_mips_malta32el_nanomips(self, kernel_path_xz): - kernel_path = self.workdir + "kernel" - with lzma.open(kernel_path_xz, 'rb') as f_in: - with open(kernel_path, 'wb') as f_out: - shutil.copyfileobj(f_in, f_out) - - kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + - 'mem=256m@@0x0 ' - 'console=ttyS0') - console_pattern = 'Kernel command line: %s' % kernel_command_line - self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=5) - - def test_mips_malta32el_nanomips_4k(self): - """ - :avocado: tags=arch:mipsel - :avocado: tags=machine:malta - :avocado: tags=endian:little - :avocado: tags=cpu:I7200 - """ - kernel_url = ('http://mipsdistros.mips.com/LinuxDistro/nanomips/' - 'kernels/v4.15.18-432-gb2eb9a8b07a1-20180627102142/' - 'generic_nano32r6el_page4k.xz') - kernel_hash = '477456aafd2a0f1ddc9482727f20fe9575565dd6' - kernel_path_xz = self.fetch_asset(kernel_url, asset_hash=kernel_hash) - self.do_test_mips_malta32el_nanomips(kernel_path_xz) - - def test_mips_malta32el_nanomips_16k_up(self): - """ - :avocado: tags=arch:mipsel - :avocado: tags=machine:malta - :avocado: tags=endian:little - :avocado: tags=cpu:I7200 - """ - kernel_url = ('http://mipsdistros.mips.com/LinuxDistro/nanomips/' - 'kernels/v4.15.18-432-gb2eb9a8b07a1-20180627102142/' - 'generic_nano32r6el_page16k_up.xz') - kernel_hash = 'e882868f944c71c816e832e2303b7874d044a7bc' - kernel_path_xz = self.fetch_asset(kernel_url, asset_hash=kernel_hash) - self.do_test_mips_malta32el_nanomips(kernel_path_xz) - - def test_mips_malta32el_nanomips_64k_dbg(self): - """ - :avocado: tags=arch:mipsel - :avocado: tags=machine:malta - :avocado: tags=endian:little - :avocado: tags=cpu:I7200 - """ - kernel_url = ('http://mipsdistros.mips.com/LinuxDistro/nanomips/' - 'kernels/v4.15.18-432-gb2eb9a8b07a1-20180627102142/' - 'generic_nano32r6el_page64k_dbg.xz') - kernel_hash = '18d1c68f2e23429e266ca39ba5349ccd0aeb7180' - kernel_path_xz = self.fetch_asset(kernel_url, asset_hash=kernel_hash) - self.do_test_mips_malta32el_nanomips(kernel_path_xz) diff --git a/tests/functional/meson.build b/tests/functional/meson.build index b62f714220..3f085bfbca 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -35,12 +35,14 @@ test_timeouts = { 'arm_sx1' : 360, 'intel_iommu': 300, 'mips_malta' : 120, + 'mipsel_replay' : 480, 'netdev_ethtool' : 180, 'ppc_40p' : 240, 'ppc64_hv' : 1000, 'ppc64_powernv' : 480, 'ppc64_pseries' : 480, 'ppc64_tuxrun' : 420, + 'ppc64_mac99' : 120, 'riscv64_tuxrun' : 120, 's390x_ccw_virtio' : 420, 'sh4_tuxrun' : 240, @@ -59,6 +61,10 @@ tests_generic_linuxuser = [ tests_generic_bsduser = [ ] +tests_aarch64_system_quick = [ + 'migration', +] + tests_aarch64_system_thorough = [ 'aarch64_aspeed', 'aarch64_raspi3', @@ -68,16 +74,25 @@ tests_aarch64_system_thorough = [ 'aarch64_sbsaref', 'aarch64_sbsaref_alpine', 'aarch64_sbsaref_freebsd', + 'aarch64_tcg_plugins', 'aarch64_tuxrun', 'aarch64_virt', 'aarch64_xlnx_versal', 'multiprocess', ] +tests_alpha_system_quick = [ + 'migration', +] + tests_alpha_system_thorough = [ 'alpha_clipper', ] +tests_arm_system_quick = [ + 'migration', +] + tests_arm_system_thorough = [ 'arm_aspeed_ast1030', 'arm_aspeed_palmetto', @@ -114,6 +129,10 @@ tests_hppa_system_quick = [ 'hppa_seabios', ] +tests_i386_system_quick = [ + 'migration', +] + tests_i386_system_thorough = [ 'i386_tuxrun', ] @@ -139,11 +158,13 @@ tests_microblazeel_system_thorough = [ tests_mips_system_thorough = [ 'mips_malta', + 'mips_replay', 'mips_tuxrun', ] tests_mipsel_system_thorough = [ 'mipsel_malta', + 'mipsel_replay', 'mipsel_tuxrun', ] @@ -155,6 +176,7 @@ tests_mips64el_system_thorough = [ 'mips64el_fuloong2e', 'mips64el_loongson3v', 'mips64el_malta', + 'mips64el_replay', 'mips64el_tuxrun', ] @@ -163,6 +185,7 @@ tests_or1k_system_thorough = [ ] tests_ppc_system_quick = [ + 'migration', 'ppc_74xx', ] @@ -177,15 +200,21 @@ tests_ppc_system_thorough = [ 'ppc_virtex_ml507', ] +tests_ppc64_system_quick = [ + 'migration', +] + tests_ppc64_system_thorough = [ 'ppc64_e500', 'ppc64_hv', 'ppc64_powernv', 'ppc64_pseries', 'ppc64_tuxrun', + 'ppc64_mac99', ] tests_riscv32_system_quick = [ + 'migration', 'riscv_opensbi', ] @@ -194,6 +223,7 @@ tests_riscv32_system_thorough = [ ] tests_riscv64_system_quick = [ + 'migration', 'riscv_opensbi', ] @@ -220,10 +250,18 @@ tests_sh4eb_system_thorough = [ 'sh4eb_r2d', ] +tests_sparc_system_quick = [ + 'migration', +] + tests_sparc_system_thorough = [ 'sparc_sun4m', ] +tests_sparc64_system_quick = [ + 'migration', +] + tests_sparc64_system_thorough = [ 'sparc64_sun4u', 'sparc64_tuxrun', @@ -232,6 +270,7 @@ tests_sparc64_system_thorough = [ tests_x86_64_system_quick = [ 'cpu_queries', 'mem_addr_space', + 'migration', 'pc_cpu_hotplug_props', 'virtio_version', 'x86_cpu_model_versions', diff --git a/tests/functional/qemu_test/__init__.py b/tests/functional/qemu_test/__init__.py index da1830286d..5c972843a6 100644 --- a/tests/functional/qemu_test/__init__.py +++ b/tests/functional/qemu_test/__init__.py @@ -14,7 +14,7 @@ from .cmd import is_readable_executable_file, \ from .testcase import QemuBaseTest, QemuUserTest, QemuSystemTest from .linuxkernel import LinuxKernelTest from .decorators import skipIfMissingCommands, skipIfNotMachine, \ - skipFlakyTest, skipUntrustedTest, skipBigDataTest, \ + skipFlakyTest, skipUntrustedTest, skipBigDataTest, skipSlowTest, \ skipIfMissingImports from .archive import archive_extract from .uncompress import uncompress diff --git a/tests/functional/qemu_test/decorators.py b/tests/functional/qemu_test/decorators.py index df088bc090..1651eb739a 100644 --- a/tests/functional/qemu_test/decorators.py +++ b/tests/functional/qemu_test/decorators.py @@ -2,6 +2,7 @@ # # Decorators useful in functional tests +import importlib import os import platform from unittest import skipUnless @@ -16,15 +17,14 @@ Example: @skipIfMissingCommands("mkisofs", "losetup") ''' def skipIfMissingCommands(*args): - def has_cmds(cmdlist): - for cmd in cmdlist: - if not which(cmd): - return False - return True - - return skipUnless(lambda: has_cmds(args), - 'required command(s) "%s" not installed' % - ", ".join(args)) + has_cmds = True + for cmd in args: + if not which(cmd): + has_cmds = False + break + + return skipUnless(has_cmds, 'required command(s) "%s" not installed' % + ", ".join(args)) ''' Decorator to skip execution of a test if the current @@ -35,9 +35,9 @@ Example @skipIfNotMachine("x86_64", "aarch64") ''' def skipIfNotMachine(*args): - return skipUnless(lambda: platform.machine() in args, - 'not running on one of the required machine(s) "%s"' % - ", ".join(args)) + return skipUnless(platform.machine() in args, + 'not running on one of the required machine(s) "%s"' % + ", ".join(args)) ''' Decorator to skip execution of flaky tests, unless @@ -87,6 +87,20 @@ def skipBigDataTest(): 'Test requires large host storage space') ''' +Decorator to skip execution of tests which have a really long +runtime (and might e.g. time out if QEMU has been compiled with +debugging enabled) unless the $QEMU_TEST_ALLOW_SLOW +environment variable is set + +Example: + + @skipSlowTest() +''' +def skipSlowTest(): + return skipUnless(os.getenv('QEMU_TEST_ALLOW_SLOW'), + 'Test has a very long runtime and might time out') + +''' Decorator to skip execution of a test if the list of python imports is not available. Example: @@ -94,14 +108,13 @@ Example: @skipIfMissingImports("numpy", "cv2") ''' def skipIfMissingImports(*args): - def has_imports(importlist): - for impname in importlist: - try: - import impname - except ImportError: - return False - return True - - return skipUnless(lambda: has_imports(args), - 'required import(s) "%s" not installed' % - ", ".join(args)) + has_imports = True + for impname in args: + try: + importlib.import_module(impname) + except ImportError: + has_imports = False + break + + return skipUnless(has_imports, 'required import(s) "%s" not installed' % + ", ".join(args)) diff --git a/tests/functional/replay_kernel.py b/tests/functional/replay_kernel.py new file mode 100644 index 0000000000..8e8ac7d052 --- /dev/null +++ b/tests/functional/replay_kernel.py @@ -0,0 +1,84 @@ +# Record/replay test that boots a Linux kernel +# +# Copyright (c) 2020 ISP RAS +# +# Author: +# Pavel Dovgalyuk <Pavel.Dovgaluk@ispras.ru> +# +# This work is licensed under the terms of the GNU GPL, version 2 or +# later. See the COPYING file in the top-level directory. + +import os +import logging +import time +import subprocess + +from qemu_test.linuxkernel import LinuxKernelTest + +class ReplayKernelBase(LinuxKernelTest): + """ + Boots a Linux kernel in record mode and checks that the console + is operational and the kernel command line is properly passed + from QEMU to the kernel. + Then replays the same scenario and verifies, that QEMU correctly + terminates. + """ + + timeout = 180 + REPLAY_KERNEL_COMMAND_LINE = 'printk.time=1 panic=-1 ' + + def run_vm(self, kernel_path, kernel_command_line, console_pattern, + record, shift, args, replay_path): + # icount requires TCG to be available + self.require_accelerator('tcg') + + logger = logging.getLogger('replay') + start_time = time.time() + vm = self.get_vm() + vm.set_console() + if record: + logger.info('recording the execution...') + mode = 'record' + else: + logger.info('replaying the execution...') + mode = 'replay' + vm.add_args('-icount', 'shift=%s,rr=%s,rrfile=%s' % + (shift, mode, replay_path), + '-kernel', kernel_path, + '-append', kernel_command_line, + '-net', 'none', + '-no-reboot') + if args: + vm.add_args(*args) + vm.launch() + self.wait_for_console_pattern(console_pattern, vm) + if record: + vm.shutdown() + logger.info('finished the recording with log size %s bytes' + % os.path.getsize(replay_path)) + self.run_replay_dump(replay_path) + logger.info('successfully tested replay-dump.py') + else: + vm.wait() + logger.info('successfully finished the replay') + elapsed = time.time() - start_time + logger.info('elapsed time %.2f sec' % elapsed) + return elapsed + + def run_replay_dump(self, replay_path): + try: + subprocess.check_call(["./scripts/replay-dump.py", + "-f", replay_path], + stdout=subprocess.DEVNULL) + except subprocess.CalledProcessError: + self.fail('replay-dump.py failed') + + def run_rr(self, kernel_path, kernel_command_line, console_pattern, + shift=7, args=None): + replay_path = os.path.join(self.workdir, 'replay.bin') + t1 = self.run_vm(kernel_path, kernel_command_line, console_pattern, + True, shift, args, replay_path) + t2 = self.run_vm(kernel_path, kernel_command_line, console_pattern, + False, shift, args, replay_path) + logger = logging.getLogger('replay') + logger.info('replay overhead {:.2%}'.format(t2 / t1 - 1)) diff --git a/tests/functional/test_aarch64_sbsaref.py b/tests/functional/test_aarch64_sbsaref.py index 99cfb6f29a..e6a55aecfa 100755 --- a/tests/functional/test_aarch64_sbsaref.py +++ b/tests/functional/test_aarch64_sbsaref.py @@ -2,9 +2,11 @@ # # Functional test that boots a kernel and checks the console # -# SPDX-FileCopyrightText: 2023-2024 Linaro Ltd. -# SPDX-FileContributor: Philippe Mathieu-Daudé <philmd@linaro.org> -# SPDX-FileContributor: Marcin Juszkiewicz <marcin.juszkiewicz@linaro.org> +# Copyright (c) 2023-2024 Linaro Ltd. +# +# Authors: +# Philippe Mathieu-Daudé +# Marcin Juszkiewicz # # SPDX-License-Identifier: GPL-2.0-or-later diff --git a/tests/functional/test_aarch64_sbsaref_alpine.py b/tests/functional/test_aarch64_sbsaref_alpine.py index 6dbc90f30e..c660cc7a40 100755 --- a/tests/functional/test_aarch64_sbsaref_alpine.py +++ b/tests/functional/test_aarch64_sbsaref_alpine.py @@ -2,15 +2,17 @@ # # Functional test that boots a kernel and checks the console # -# SPDX-FileCopyrightText: 2023-2024 Linaro Ltd. -# SPDX-FileContributor: Philippe Mathieu-Daudé <philmd@linaro.org> -# SPDX-FileContributor: Marcin Juszkiewicz <marcin.juszkiewicz@linaro.org> +# Copyright (c) 2023-2024 Linaro Ltd. +# +# Authors: +# Philippe Mathieu-Daudé +# Marcin Juszkiewicz # # SPDX-License-Identifier: GPL-2.0-or-later import os -from qemu_test import QemuSystemTest, Asset +from qemu_test import QemuSystemTest, Asset, skipSlowTest from qemu_test import wait_for_console_pattern from unittest import skipUnless from test_aarch64_sbsaref import fetch_firmware @@ -53,8 +55,7 @@ class Aarch64SbsarefAlpine(QemuSystemTest): def test_sbsaref_alpine_linux_max_pauth_impdef(self): self.boot_alpine_linux("max,pauth-impdef=on") - @skipUnless(os.getenv('QEMU_TEST_TIMEOUT_EXPECTED'), - 'Test might timeout due to PAuth emulation') + @skipSlowTest() # Test might timeout due to PAuth emulation def test_sbsaref_alpine_linux_max(self): self.boot_alpine_linux("max") diff --git a/tests/functional/test_aarch64_sbsaref_freebsd.py b/tests/functional/test_aarch64_sbsaref_freebsd.py index 77ba2ba1da..bd6728dc70 100755 --- a/tests/functional/test_aarch64_sbsaref_freebsd.py +++ b/tests/functional/test_aarch64_sbsaref_freebsd.py @@ -2,17 +2,18 @@ # # Functional test that boots a kernel and checks the console # -# SPDX-FileCopyrightText: 2023-2024 Linaro Ltd. -# SPDX-FileContributor: Philippe Mathieu-Daudé <philmd@linaro.org> -# SPDX-FileContributor: Marcin Juszkiewicz <marcin.juszkiewicz@linaro.org> +# Copyright (c) 2023-2024 Linaro Ltd. +# +# Authors: +# Philippe Mathieu-Daudé +# Marcin Juszkiewicz # # SPDX-License-Identifier: GPL-2.0-or-later import os -from qemu_test import QemuSystemTest, Asset +from qemu_test import QemuSystemTest, Asset, skipSlowTest from qemu_test import wait_for_console_pattern -from unittest import skipUnless from test_aarch64_sbsaref import fetch_firmware @@ -50,13 +51,11 @@ class Aarch64SbsarefFreeBSD(QemuSystemTest): def test_sbsaref_freebsd14_max_pauth_off(self): self.boot_freebsd14("max,pauth=off") - @skipUnless(os.getenv('QEMU_TEST_TIMEOUT_EXPECTED'), - 'Test might timeout due to PAuth emulation') + @skipSlowTest() # Test might timeout due to PAuth emulation def test_sbsaref_freebsd14_max_pauth_impdef(self): self.boot_freebsd14("max,pauth-impdef=on") - @skipUnless(os.getenv('QEMU_TEST_TIMEOUT_EXPECTED'), - 'Test might timeout due to PAuth emulation') + @skipSlowTest() # Test might timeout due to PAuth emulation def test_sbsaref_freebsd14_max(self): self.boot_freebsd14("max") diff --git a/tests/functional/test_aarch64_tcg_plugins.py b/tests/functional/test_aarch64_tcg_plugins.py index 01660eb090..7e8beacc83 100755 --- a/tests/functional/test_aarch64_tcg_plugins.py +++ b/tests/functional/test_aarch64_tcg_plugins.py @@ -15,6 +15,7 @@ import tempfile import mmap import re +from qemu.machine.machine import VMLaunchFailure from qemu_test import LinuxKernelTest, Asset @@ -43,10 +44,12 @@ class PluginKernelBase(LinuxKernelTest): try: vm.launch() - except: - # TODO: probably fails because plugins not enabled but we - # can't currently probe for the feature. - self.cancel("TCG Plugins not enabled?") + except VMLaunchFailure as excp: + if "plugin interface not enabled in this build" in excp.output: + self.skipTest("TCG plugins not enabled") + else: + self.log.info(f"unhandled launch failure: {excp.output}") + raise excp self.wait_for_console_pattern(console_pattern, vm) # ensure logs are flushed @@ -65,7 +68,7 @@ class PluginKernelNormal(PluginKernelBase): kernel_path = self.ASSET_KERNEL.fetch() kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + 'console=ttyAMA0') - console_pattern = 'Kernel panic - not syncing: VFS:' + console_pattern = 'Please append a correct "root=" boot option' plugin_log = tempfile.NamedTemporaryFile(mode="r+t", prefix="plugin", suffix=".log") @@ -91,7 +94,7 @@ class PluginKernelNormal(PluginKernelBase): kernel_path = self.ASSET_KERNEL.fetch() kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + 'console=ttyAMA0') - console_pattern = 'Kernel panic - not syncing: VFS:' + console_pattern = 'Please append a correct "root=" boot option' plugin_log = tempfile.NamedTemporaryFile(mode="r+t", prefix="plugin", suffix=".log") diff --git a/tests/functional/test_arm_quanta_gsj.py b/tests/functional/test_arm_quanta_gsj.py index 7b82e2185c..da60aeb659 100755 --- a/tests/functional/test_arm_quanta_gsj.py +++ b/tests/functional/test_arm_quanta_gsj.py @@ -7,8 +7,8 @@ import os from qemu_test import LinuxKernelTest, Asset, exec_command_and_wait_for_pattern -from qemu_test import interrupt_interactive_console_until_pattern -from unittest import skipUnless +from qemu_test import interrupt_interactive_console_until_pattern, skipSlowTest + class EmcraftSf2Machine(LinuxKernelTest): @@ -32,7 +32,7 @@ class EmcraftSf2Machine(LinuxKernelTest): '20200711-gsj-qemu-0/nuvoton-npcm730-gsj.dtb'), '3249b2da787d4b9ad4e61f315b160abfceb87b5e1895a7ce898ce7f40c8d4045') - @skipUnless(os.getenv('QEMU_TEST_TIMEOUT_EXPECTED'), 'Test might timeout') + @skipSlowTest() def test_arm_quanta_gsj(self): self.set_machine('quanta-gsj') image_path = self.uncompress(self.ASSET_IMAGE, format='gz') diff --git a/tests/functional/test_migration.py b/tests/functional/test_migration.py new file mode 100755 index 0000000000..44804113cf --- /dev/null +++ b/tests/functional/test_migration.py @@ -0,0 +1,100 @@ +#!/usr/bin/env python3 +# +# Migration test +# +# Copyright (c) 2019 Red Hat, Inc. +# +# Authors: +# Cleber Rosa <crosa@redhat.com> +# Caio Carrara <ccarrara@redhat.com> +# +# This work is licensed under the terms of the GNU GPL, version 2 or +# later. See the COPYING file in the top-level directory. + + +import tempfile +import os +import time + +from qemu_test import QemuSystemTest, skipIfMissingCommands +from qemu_test.ports import Ports + +class MigrationTest(QemuSystemTest): + + timeout = 10 + + @staticmethod + def migration_finished(vm): + return vm.cmd('query-migrate')['status'] in ('completed', 'failed') + + def assert_migration(self, src_vm, dst_vm): + + end = time.monotonic() + self.timeout + while time.monotonic() < end and not self.migration_finished(src_vm): + time.sleep(0.1) + + end = time.monotonic() + self.timeout + while time.monotonic() < end and not self.migration_finished(dst_vm): + time.sleep(0.1) + + self.assertEqual(src_vm.cmd('query-migrate')['status'], 'completed') + self.assertEqual(dst_vm.cmd('query-migrate')['status'], 'completed') + self.assertEqual(dst_vm.cmd('query-status')['status'], 'running') + self.assertEqual(src_vm.cmd('query-status')['status'],'postmigrate') + + def select_machine(self): + target_machine = { + 'aarch64': 'quanta-gsj', + 'alpha': 'clipper', + 'arm': 'npcm750-evb', + 'i386': 'isapc', + 'ppc': 'sam460ex', + 'ppc64': 'mac99', + 'riscv32': 'spike', + 'riscv64': 'virt', + 'sparc': 'SS-4', + 'sparc64': 'sun4u', + 'x86_64': 'microvm', + } + self.set_machine(target_machine[self.arch]) + + def do_migrate(self, dest_uri, src_uri=None): + self.select_machine() + dest_vm = self.get_vm('-incoming', dest_uri, name="dest-qemu") + dest_vm.add_args('-nodefaults') + dest_vm.launch() + if src_uri is None: + src_uri = dest_uri + source_vm = self.get_vm(name="source-qemu") + source_vm.add_args('-nodefaults') + source_vm.launch() + source_vm.qmp('migrate', uri=src_uri) + self.assert_migration(source_vm, dest_vm) + + def _get_free_port(self, ports): + port = ports.find_free_port() + if port is None: + self.skipTest('Failed to find a free port') + return port + + def test_migration_with_tcp_localhost(self): + with Ports() as ports: + dest_uri = 'tcp:localhost:%u' % self._get_free_port(ports) + self.do_migrate(dest_uri) + + def test_migration_with_unix(self): + with tempfile.TemporaryDirectory(prefix='socket_') as socket_path: + dest_uri = 'unix:%s/qemu-test.sock' % socket_path + self.do_migrate(dest_uri) + + @skipIfMissingCommands('nc') + def test_migration_with_exec(self): + """The test works for both netcat-traditional and netcat-openbsd packages.""" + with Ports() as ports: + free_port = self._get_free_port(ports) + dest_uri = 'exec:nc -l localhost %u' % free_port + src_uri = 'exec:nc localhost %u' % free_port + self.do_migrate(dest_uri, src_uri) + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/functional/test_mips64el_replay.py b/tests/functional/test_mips64el_replay.py new file mode 100755 index 0000000000..4f63d7fb34 --- /dev/null +++ b/tests/functional/test_mips64el_replay.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python3 +# +# Replay tests for the little-endian 64-bit MIPS Malta board +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import os +import logging + +from qemu_test import Asset, exec_command_and_wait_for_pattern +from qemu_test import skipIfMissingImports, skipFlakyTest, skipUntrustedTest +from replay_kernel import ReplayKernelBase + + +class Mips64elReplay(ReplayKernelBase): + + ASSET_KERNEL_2_63_2 = Asset( + ('http://snapshot.debian.org/archive/debian/' + '20130217T032700Z/pool/main/l/linux-2.6/' + 'linux-image-2.6.32-5-5kc-malta_2.6.32-48_mipsel.deb'), + '35eb476f03be589824b0310358f1c447d85e645b88cbcd2ac02b97ef560f9f8d') + + def test_replay_mips64el_malta(self): + self.set_machine('malta') + kernel_path = self.archive_extract(self.ASSET_KERNEL_2_63_2, + member='boot/vmlinux-2.6.32-5-5kc-malta') + kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'console=ttyS0' + console_pattern = 'Kernel command line: %s' % kernel_command_line + self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=5) + + + ASSET_KERNEL_3_19_3 = Asset( + ('https://github.com/philmd/qemu-testing-blob/' + 'raw/9ad2df38/mips/malta/mips64el/' + 'vmlinux-3.19.3.mtoman.20150408'), + '8d3beb003bc66051ead98e7172139017fcf9ce2172576541c57e86418dfa5ab8') + + ASSET_CPIO_R1 = Asset( + ('https://github.com/groeck/linux-build-test/' + 'raw/8584a59e/rootfs/mipsel64/' + 'rootfs.mipsel64r1.cpio.gz'), + '75ba10cd35fb44e32948eeb26974f061b703c81c4ba2fab1ebcacf1d1bec3b61') + + @skipUntrustedTest() + def test_replay_mips64el_malta_5KEc_cpio(self): + self.set_machine('malta') + self.cpu = '5KEc' + kernel_path = self.ASSET_KERNEL_3_19_3.fetch() + initrd_path = self.uncompress(self.ASSET_CPIO_R1) + + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'console=ttyS0 console=tty ' + 'rdinit=/sbin/init noreboot') + console_pattern = 'Boot successful.' + self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=5, + args=('-initrd', initrd_path)) + + +if __name__ == '__main__': + ReplayKernelBase.main() diff --git a/tests/functional/test_mips_malta.py b/tests/functional/test_mips_malta.py index 3b15038d89..eaf372255b 100755 --- a/tests/functional/test_mips_malta.py +++ b/tests/functional/test_mips_malta.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Functional tests for the little-endian 32-bit MIPS Malta board +# Functional tests for the big-endian 32-bit MIPS Malta board # # Copyright (c) Philippe Mathieu-Daudé <f4bug@amsat.org> # diff --git a/tests/functional/test_mips_replay.py b/tests/functional/test_mips_replay.py new file mode 100755 index 0000000000..eda031ccad --- /dev/null +++ b/tests/functional/test_mips_replay.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python3 +# +# Replay tests for the big-endian 32-bit MIPS Malta board +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import Asset, skipSlowTest, exec_command_and_wait_for_pattern +from replay_kernel import ReplayKernelBase + + +class MipsReplay(ReplayKernelBase): + + ASSET_KERNEL_2_63_2 = Asset( + ('http://snapshot.debian.org/archive/debian/' + '20130217T032700Z/pool/main/l/linux-2.6/' + 'linux-image-2.6.32-5-4kc-malta_2.6.32-48_mips.deb'), + '16ca524148afb0626f483163e5edf352bc1ab0e4fc7b9f9d473252762f2c7a43') + + def test_replay_mips_malta(self): + self.set_machine('malta') + kernel_path = self.archive_extract(self.ASSET_KERNEL_2_63_2, + member='boot/vmlinux-2.6.32-5-4kc-malta') + kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'console=ttyS0' + console_pattern = 'Kernel command line: %s' % kernel_command_line + self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=5) + + ASSET_KERNEL_4_5_0 = Asset( + ('http://snapshot.debian.org/archive/debian/' + '20160601T041800Z/pool/main/l/linux/' + 'linux-image-4.5.0-2-4kc-malta_4.5.5-1_mips.deb'), + '526b17d5889840888b76fc2c36a0ebde182c9b1410a3a1e68203c3b160eb2027') + + ASSET_INITRD = Asset( + ('https://github.com/groeck/linux-build-test/raw/' + '8584a59ed9e5eb5ee7ca91f6d74bbb06619205b8/rootfs/' + 'mips/rootfs.cpio.gz'), + 'dcfe3a7fe3200da3a00d176b95caaa086495eb158f2bff64afc67d7e1eb2cddc') + + @skipSlowTest() + def test_replay_mips_malta_cpio(self): + self.set_machine('malta') + kernel_path = self.archive_extract(self.ASSET_KERNEL_4_5_0, + member='boot/vmlinux-4.5.0-2-4kc-malta') + initrd_path = self.uncompress(self.ASSET_INITRD) + + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'console=ttyS0 console=tty ' + 'rdinit=/sbin/init noreboot') + console_pattern = 'Boot successful.' + self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=5, + args=('-initrd', initrd_path)) + + +if __name__ == '__main__': + ReplayKernelBase.main() diff --git a/tests/functional/test_mipsel_replay.py b/tests/functional/test_mipsel_replay.py new file mode 100644 index 0000000000..0a330de43f --- /dev/null +++ b/tests/functional/test_mipsel_replay.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python3 +# +# Replay tests for the little-endian 32-bit MIPS Malta board +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import Asset, wait_for_console_pattern, skipSlowTest +from replay_kernel import ReplayKernelBase + + +class MipselReplay(ReplayKernelBase): + + ASSET_KERNEL_4K = Asset( + ('http://mipsdistros.mips.com/LinuxDistro/nanomips/' + 'kernels/v4.15.18-432-gb2eb9a8b07a1-20180627102142/' + 'generic_nano32r6el_page4k.xz'), + '019e034094ac6cf3aa77df5e130fb023ce4dbc804b04bfcc560c6403e1ae6bdb') + ASSET_KERNEL_16K = Asset( + ('http://mipsdistros.mips.com/LinuxDistro/nanomips/' + 'kernels/v4.15.18-432-gb2eb9a8b07a1-20180627102142/' + 'generic_nano32r6el_page16k_up.xz'), + '3a54a10b3108c16a448dca9ea3db378733a27423befc2a45a5bdf990bd85e12c') + ASSET_KERNEL_64K = Asset( + ('http://mipsdistros.mips.com/LinuxDistro/nanomips/' + 'kernels/v4.15.18-432-gb2eb9a8b07a1-20180627102142/' + 'generic_nano32r6el_page64k_dbg.xz'), + 'ce21ff4b07a981ecb8a39db2876616f5a2473eb2ab459c6f67465b9914b0c6b6') + + def do_test_replay_mips_malta32el_nanomips(self, kernel_asset): + self.set_machine('malta') + self.cpu = 'I7200' + kernel_path = self.uncompress(kernel_asset) + + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'mem=256m@@0x0 ' + 'console=ttyS0') + console_pattern = 'Kernel command line: %s' % kernel_command_line + self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=5) + + @skipSlowTest() + def test_replay_mips_malta32el_nanomips_4k(self): + self.do_test_replay_mips_malta32el_nanomips(self.ASSET_KERNEL_4K) + + @skipSlowTest() + def test_replay_mips_malta32el_nanomips_16k_up(self): + self.do_test_replay_mips_malta32el_nanomips(self.ASSET_KERNEL_16K) + + @skipSlowTest() + def test_replay_mips_malta32el_nanomips_64k_dbg(self): + self.do_test_replay_mips_malta32el_nanomips(self.ASSET_KERNEL_64K) + + +if __name__ == '__main__': + ReplayKernelBase.main() diff --git a/tests/functional/test_ppc64_mac99.py b/tests/functional/test_ppc64_mac99.py new file mode 100755 index 0000000000..dfd9c01371 --- /dev/null +++ b/tests/functional/test_ppc64_mac99.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python3 +# +# Functional test that boots a mac99 machine with a PPC970 CPU +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import LinuxKernelTest, Asset +from qemu_test import exec_command_and_wait_for_pattern + +class mac99Test(LinuxKernelTest): + + ASSET_BR2_MAC99_LINUX = Asset( + 'https://github.com/legoater/qemu-ppc-boot/raw/refs/heads/main/buildroot/qemu_ppc64_mac99-2023.11-8-gdcd9f0f6eb-20240105/vmlinux', + 'd59307437e4365f2cced0bbd1b04949f7397b282ef349b7cafd894d74aadfbff') + + ASSET_BR2_MAC99_ROOTFS = Asset( + 'https://github.com/legoater/qemu-ppc-boot/raw/refs/heads/main//buildroot/qemu_ppc64_mac99-2023.11-8-gdcd9f0f6eb-20240105/rootfs.ext2', + 'bbd5fd8af62f580bc4e585f326fe584e22856572633a8333178ea6d4ed4955a4') + + def test_ppc64_mac99_buildroot(self): + self.set_machine('mac99') + + linux_path = self.ASSET_BR2_MAC99_LINUX.fetch() + rootfs_path = self.ASSET_BR2_MAC99_ROOTFS.fetch() + + self.vm.set_console() + + # Note: We need '-nographic' to get a serial console + self.vm.add_args('-kernel', linux_path, + '-append', 'root=/dev/sda', + '-drive', f'file={rootfs_path},format=raw', + '-snapshot', '-nographic') + self.vm.launch() + + self.wait_for_console_pattern('>> OpenBIOS') + self.wait_for_console_pattern('Linux version') + self.wait_for_console_pattern('/init as init process') + self.wait_for_console_pattern('gem 0000:f0:0e.0 eth0: Link is up at 100 Mbps') + self.wait_for_console_pattern('buildroot login:') + exec_command_and_wait_for_pattern(self, 'root', '#') + exec_command_and_wait_for_pattern(self, 'poweroff', 'Power down') + +if __name__ == '__main__': + LinuxKernelTest.main() diff --git a/tests/functional/test_ppc_40p.py b/tests/functional/test_ppc_40p.py index 7a74e0cca7..614972a7eb 100755 --- a/tests/functional/test_ppc_40p.py +++ b/tests/functional/test_ppc_40p.py @@ -9,6 +9,7 @@ from qemu_test import QemuSystemTest, Asset from qemu_test import wait_for_console_pattern, skipUntrustedTest +from qemu_test import exec_command_and_wait_for_pattern class IbmPrep40pMachine(QemuSystemTest): @@ -72,5 +73,22 @@ class IbmPrep40pMachine(QemuSystemTest): self.vm.launch() wait_for_console_pattern(self, 'NetBSD/prep BOOT, Revision 1.9') + ASSET_40P_SANDALFOOT = Asset( + 'http://www.juneau-lug.org/zImage.initrd.sandalfoot', + '749ab02f576c6dc8f33b9fb022ecb44bf6a35a0472f2ea6a5e9956bc15933901') + + def test_openbios_and_linux(self): + self.set_machine('40p') + self.require_accelerator("tcg") + drive_path = self.ASSET_40P_SANDALFOOT.fetch() + self.vm.set_console() + self.vm.add_args('-cdrom', drive_path, + '-boot', 'd') + + self.vm.launch() + wait_for_console_pattern(self, 'Please press Enter to activate this console.') + exec_command_and_wait_for_pattern(self, '\012', '#') + exec_command_and_wait_for_pattern(self, 'uname -a', 'Linux ppc 2.4.18') + if __name__ == '__main__': QemuSystemTest.main() diff --git a/tests/qemu-iotests/194.out b/tests/qemu-iotests/194.out index 376ed1d2e6..6940e809cd 100644 --- a/tests/qemu-iotests/194.out +++ b/tests/qemu-iotests/194.out @@ -14,6 +14,7 @@ Starting migration... {"return": {}} {"data": {"status": "setup"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} {"data": {"status": "active"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"data": {"status": "device"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} {"data": {"status": "completed"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} Gracefully ending the `drive-mirror` job on source... {"return": {}} diff --git a/tests/qemu-iotests/203.out b/tests/qemu-iotests/203.out index 9d4abba8c5..8e58705e51 100644 --- a/tests/qemu-iotests/203.out +++ b/tests/qemu-iotests/203.out @@ -8,4 +8,5 @@ Starting migration... {"return": {}} {"data": {"status": "setup"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} {"data": {"status": "active"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"data": {"status": "device"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} {"data": {"status": "completed"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} diff --git a/tests/qemu-iotests/234.out b/tests/qemu-iotests/234.out index ac8b64350c..be3e138b58 100644 --- a/tests/qemu-iotests/234.out +++ b/tests/qemu-iotests/234.out @@ -10,6 +10,7 @@ Starting migration to B... {"return": {}} {"data": {"status": "setup"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} {"data": {"status": "active"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"data": {"status": "device"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} {"data": {"status": "completed"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} {"data": {"status": "active"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} {"data": {"status": "completed"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} @@ -27,6 +28,7 @@ Starting migration back to A... {"return": {}} {"data": {"status": "setup"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} {"data": {"status": "active"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"data": {"status": "device"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} {"data": {"status": "completed"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} {"data": {"status": "active"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} {"data": {"status": "completed"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} diff --git a/tests/qemu-iotests/262.out b/tests/qemu-iotests/262.out index b8a2d3598d..bd7706b84b 100644 --- a/tests/qemu-iotests/262.out +++ b/tests/qemu-iotests/262.out @@ -8,6 +8,7 @@ Starting migration to B... {"return": {}} {"data": {"status": "setup"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} {"data": {"status": "active"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"data": {"status": "device"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} {"data": {"status": "completed"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} {"data": {"status": "active"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} {"data": {"status": "completed"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} diff --git a/tests/qemu-iotests/280.out b/tests/qemu-iotests/280.out index 546dbb4a68..37411144ca 100644 --- a/tests/qemu-iotests/280.out +++ b/tests/qemu-iotests/280.out @@ -7,6 +7,7 @@ Enabling migration QMP events on VM... {"return": {}} {"data": {"status": "setup"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} {"data": {"status": "active"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"data": {"status": "device"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} {"data": {"status": "completed"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} VM is now stopped: diff --git a/tests/qtest/libqos/libqos.c b/tests/qtest/libqos/libqos.c index 5c0fa1f7c5..28a0901a0a 100644 --- a/tests/qtest/libqos/libqos.c +++ b/tests/qtest/libqos/libqos.c @@ -117,13 +117,14 @@ void migrate(QOSState *from, QOSState *to, const char *uri) g_assert(qdict_haskey(sub, "status")); st = qdict_get_str(sub, "status"); - /* "setup", "active", "completed", "failed", "cancelled" */ + /* "setup", "active", "device", "completed", "failed", "cancelled" */ if (strcmp(st, "completed") == 0) { qobject_unref(rsp); break; } if ((strcmp(st, "setup") == 0) || (strcmp(st, "active") == 0) + || (strcmp(st, "device") == 0) || (strcmp(st, "wait-unplug") == 0)) { qobject_unref(rsp); g_usleep(5000); diff --git a/tests/qtest/libqos/virtio-scmi.c b/tests/qtest/libqos/virtio-scmi.c index ce8f4d5c06..6b5bd4db42 100644 --- a/tests/qtest/libqos/virtio-scmi.c +++ b/tests/qtest/libqos/virtio-scmi.c @@ -1,7 +1,7 @@ /* * virtio-scmi nodes for testing * - * SPDX-FileCopyrightText: Linaro Ltd + * Copyright (c) Linaro Ltd. * SPDX-FileCopyrightText: Red Hat, Inc. * SPDX-License-Identifier: GPL-2.0-or-later * diff --git a/tests/qtest/libqtest.c b/tests/qtest/libqtest.c index a1e105f27f..437b24fa2e 100644 --- a/tests/qtest/libqtest.c +++ b/tests/qtest/libqtest.c @@ -75,6 +75,8 @@ struct QTestState { int fd; int qmp_fd; + int sock; + int qmpsock; pid_t qemu_pid; /* our child QEMU process */ int wstatus; #ifdef _WIN32 @@ -458,18 +460,19 @@ static QTestState *G_GNUC_PRINTF(2, 3) qtest_spawn_qemu(const char *qemu_bin, return s; } +static char *qtest_socket_path(const char *suffix) +{ + return g_strdup_printf("%s/qtest-%d.%s", g_get_tmp_dir(), getpid(), suffix); +} + static QTestState *qtest_init_internal(const char *qemu_bin, - const char *extra_args) + const char *extra_args, + bool do_connect) { QTestState *s; int sock, qmpsock, i; - gchar *socket_path; - gchar *qmp_socket_path; - - socket_path = g_strdup_printf("%s/qtest-%d.sock", - g_get_tmp_dir(), getpid()); - qmp_socket_path = g_strdup_printf("%s/qtest-%d.qmp", - g_get_tmp_dir(), getpid()); + g_autofree gchar *socket_path = qtest_socket_path("sock"); + g_autofree gchar *qmp_socket_path = qtest_socket_path("qmp"); /* * It's possible that if an earlier test run crashed it might @@ -501,22 +504,19 @@ static QTestState *qtest_init_internal(const char *qemu_bin, qtest_client_set_rx_handler(s, qtest_client_socket_recv_line); qtest_client_set_tx_handler(s, qtest_client_socket_send); - s->fd = socket_accept(sock); - if (s->fd >= 0) { - s->qmp_fd = socket_accept(qmpsock); - } - unlink(socket_path); - unlink(qmp_socket_path); - g_free(socket_path); - g_free(qmp_socket_path); - - g_assert(s->fd >= 0 && s->qmp_fd >= 0); - s->rx = g_string_new(""); for (i = 0; i < MAX_IRQ; i++) { s->irq_level[i] = false; } + s->fd = -1; + s->qmp_fd = -1; + s->sock = sock; + s->qmpsock = qmpsock; + if (do_connect) { + qtest_connect(s); + } + /* * Stopping QEMU for debugging is not supported on Windows. * @@ -531,28 +531,38 @@ static QTestState *qtest_init_internal(const char *qemu_bin, } #endif - /* ask endianness of the target */ + return s; +} - s->big_endian = qtest_query_target_endianness(s); +void qtest_connect(QTestState *s) +{ + g_autofree gchar *socket_path = qtest_socket_path("sock"); + g_autofree gchar *qmp_socket_path = qtest_socket_path("qmp"); - return s; + g_assert(s->sock >= 0 && s->qmpsock >= 0); + s->fd = socket_accept(s->sock); + if (s->fd >= 0) { + s->qmp_fd = socket_accept(s->qmpsock); + } + unlink(socket_path); + unlink(qmp_socket_path); + g_assert(s->fd >= 0 && s->qmp_fd >= 0); + s->sock = s->qmpsock = -1; + /* ask endianness of the target */ + s->big_endian = qtest_query_target_endianness(s); } QTestState *qtest_init_without_qmp_handshake(const char *extra_args) { - return qtest_init_internal(qtest_qemu_binary(NULL), extra_args); + return qtest_init_internal(qtest_qemu_binary(NULL), extra_args, true); } -QTestState *qtest_init_with_env_and_capabilities(const char *var, - const char *extra_args, - QList *capabilities) +void qtest_qmp_handshake(QTestState *s, QList *capabilities) { - QTestState *s = qtest_init_internal(qtest_qemu_binary(var), extra_args); - QDict *greeting; - /* Read the QMP greeting and then do the handshake */ - greeting = qtest_qmp_receive(s); + QDict *greeting = qtest_qmp_receive(s); qobject_unref(greeting); + if (capabilities) { qtest_qmp_assert_success(s, "{ 'execute': 'qmp_capabilities', " @@ -561,18 +571,37 @@ QTestState *qtest_init_with_env_and_capabilities(const char *var, } else { qtest_qmp_assert_success(s, "{ 'execute': 'qmp_capabilities' }"); } +} + +QTestState *qtest_init_with_env_and_capabilities(const char *var, + const char *extra_args, + QList *capabilities, + bool do_connect) +{ + QTestState *s = qtest_init_internal(qtest_qemu_binary(var), extra_args, + do_connect); + if (do_connect) { + qtest_qmp_handshake(s, capabilities); + } else { + /* + * If the connection is delayed, the capabilities must be set + * at that moment. + */ + assert(!capabilities); + } return s; } -QTestState *qtest_init_with_env(const char *var, const char *extra_args) +QTestState *qtest_init_with_env(const char *var, const char *extra_args, + bool do_connect) { - return qtest_init_with_env_and_capabilities(var, extra_args, NULL); + return qtest_init_with_env_and_capabilities(var, extra_args, NULL, true); } QTestState *qtest_init(const char *extra_args) { - return qtest_init_with_env(NULL, extra_args); + return qtest_init_with_env(NULL, extra_args, true); } QTestState *qtest_vinitf(const char *fmt, va_list ap) @@ -782,6 +811,7 @@ QDict *qtest_qmp_receive(QTestState *s) QDict *qtest_qmp_receive_dict(QTestState *s) { + g_assert(s->qmp_fd >= 0); return qmp_fd_receive(s->qmp_fd); } @@ -809,12 +839,14 @@ int qtest_socket_server(const char *socket_path) void qtest_qmp_vsend_fds(QTestState *s, int *fds, size_t fds_num, const char *fmt, va_list ap) { + g_assert(s->qmp_fd >= 0); qmp_fd_vsend_fds(s->qmp_fd, fds, fds_num, fmt, ap); } #endif void qtest_qmp_vsend(QTestState *s, const char *fmt, va_list ap) { + g_assert(s->qmp_fd >= 0); qmp_fd_vsend(s->qmp_fd, fmt, ap); } @@ -875,6 +907,7 @@ void qtest_qmp_send_raw(QTestState *s, const char *fmt, ...) { va_list ap; + g_assert(s->qmp_fd >= 0); va_start(ap, fmt); qmp_fd_vsend_raw(s->qmp_fd, fmt, ap); va_end(ap); @@ -1580,7 +1613,7 @@ static struct MachInfo *qtest_get_machines(const char *var) silence_spawn_log = !g_test_verbose(); - qts = qtest_init_with_env(qemu_var, "-machine none"); + qts = qtest_init_with_env(qemu_var, "-machine none", true); response = qtest_qmp(qts, "{ 'execute': 'query-machines' }"); g_assert(response); list = qdict_get_qlist(response, "return"); @@ -1635,7 +1668,7 @@ static struct CpuModel *qtest_get_cpu_models(void) silence_spawn_log = !g_test_verbose(); - qts = qtest_init_with_env(NULL, "-machine none"); + qts = qtest_init_with_env(NULL, "-machine none", true); response = qtest_qmp(qts, "{ 'execute': 'query-cpu-definitions' }"); g_assert(response); list = qdict_get_qlist(response, "return"); diff --git a/tests/qtest/libqtest.h b/tests/qtest/libqtest.h index ce88d23eae..29f123e281 100644 --- a/tests/qtest/libqtest.h +++ b/tests/qtest/libqtest.h @@ -61,13 +61,15 @@ QTestState *qtest_init(const char *extra_args); * @var: Environment variable from where to take the QEMU binary * @extra_args: Other arguments to pass to QEMU. CAUTION: these * arguments are subject to word splitting and shell evaluation. + * @do_connect: connect to qemu monitor and qtest socket. * * Like qtest_init(), but use a different environment variable for the * QEMU binary. * * Returns: #QTestState instance. */ -QTestState *qtest_init_with_env(const char *var, const char *extra_args); +QTestState *qtest_init_with_env(const char *var, const char *extra_args, + bool do_connect); /** * qtest_init_with_env_and_capabilities: @@ -75,6 +77,7 @@ QTestState *qtest_init_with_env(const char *var, const char *extra_args); * @extra_args: Other arguments to pass to QEMU. CAUTION: these * arguments are subject to word splitting and shell evaluation. * @capabilities: list of QMP capabilities (strings) to enable + * @do_connect: connect to qemu monitor and qtest socket. * * Like qtest_init_with_env(), but enable specified capabilities during * hadshake. @@ -83,7 +86,8 @@ QTestState *qtest_init_with_env(const char *var, const char *extra_args); */ QTestState *qtest_init_with_env_and_capabilities(const char *var, const char *extra_args, - QList *capabilities); + QList *capabilities, + bool do_connect); /** * qtest_init_without_qmp_handshake: @@ -95,6 +99,22 @@ QTestState *qtest_init_with_env_and_capabilities(const char *var, QTestState *qtest_init_without_qmp_handshake(const char *extra_args); /** + * qtest_connect + * @s: #QTestState instance to connect + * Connect to qemu monitor and qtest socket, after skipping them in + * qtest_init_with_env. Does not handshake with the monitor. + */ +void qtest_connect(QTestState *s); + +/** + * qtest_qmp_handshake: + * @s: #QTestState instance to operate on. + * @capabilities: list of QMP capabilities (strings) to enable + * Perform handshake after connecting to qemu monitor. + */ +void qtest_qmp_handshake(QTestState *s, QList *capabilities); + +/** * qtest_init_with_serial: * @extra_args: other arguments to pass to QEMU. CAUTION: these * arguments are subject to word splitting and shell evaluation. diff --git a/tests/qtest/migration/cpr-tests.c b/tests/qtest/migration/cpr-tests.c index 44ce89aa5b..215b0df8c0 100644 --- a/tests/qtest/migration/cpr-tests.c +++ b/tests/qtest/migration/cpr-tests.c @@ -44,6 +44,62 @@ static void test_mode_reboot(void) test_file_common(&args, true); } +static void *test_mode_transfer_start(QTestState *from, QTestState *to) +{ + migrate_set_parameter_str(from, "mode", "cpr-transfer"); + return NULL; +} + +/* + * cpr-transfer mode cannot use the target monitor prior to starting the + * migration, and cannot connect synchronously to the monitor, so defer + * the target connection. + */ +static void test_mode_transfer_common(bool incoming_defer) +{ + g_autofree char *cpr_path = g_strdup_printf("%s/cpr.sock", tmpfs); + g_autofree char *mig_path = g_strdup_printf("%s/migsocket", tmpfs); + g_autofree char *uri = g_strdup_printf("unix:%s", mig_path); + + const char *opts = "-machine aux-ram-share=on -nodefaults"; + g_autofree const char *cpr_channel = g_strdup_printf( + "cpr,addr.transport=socket,addr.type=unix,addr.path=%s", + cpr_path); + g_autofree char *opts_target = g_strdup_printf("-incoming %s %s", + cpr_channel, opts); + + g_autofree char *connect_channels = g_strdup_printf( + "[ { 'channel-type': 'main'," + " 'addr': { 'transport': 'socket'," + " 'type': 'unix'," + " 'path': '%s' } } ]", + mig_path); + + MigrateCommon args = { + .start.opts_source = opts, + .start.opts_target = opts_target, + .start.defer_target_connect = true, + .start.memory_backend = "-object memory-backend-memfd,id=pc.ram,size=%s" + " -machine memory-backend=pc.ram", + .listen_uri = incoming_defer ? "defer" : uri, + .connect_channels = connect_channels, + .cpr_channel = cpr_channel, + .start_hook = test_mode_transfer_start, + }; + + test_precopy_common(&args); +} + +static void test_mode_transfer(void) +{ + test_mode_transfer_common(NULL); +} + +static void test_mode_transfer_defer(void) +{ + test_mode_transfer_common(true); +} + void migration_test_add_cpr(MigrationTestEnv *env) { tmpfs = env->tmpfs; @@ -55,4 +111,10 @@ void migration_test_add_cpr(MigrationTestEnv *env) if (getenv("QEMU_TEST_FLAKY_TESTS")) { migration_test_add("/migration/mode/reboot", test_mode_reboot); } + + if (env->has_kvm) { + migration_test_add("/migration/mode/transfer", test_mode_transfer); + migration_test_add("/migration/mode/transfer/defer", + test_mode_transfer_defer); + } } diff --git a/tests/qtest/migration/framework.c b/tests/qtest/migration/framework.c index 4550cda129..de65bfe40d 100644 --- a/tests/qtest/migration/framework.c +++ b/tests/qtest/migration/framework.c @@ -18,6 +18,8 @@ #include "migration/migration-qmp.h" #include "migration/migration-util.h" #include "ppc-util.h" +#include "qapi/error.h" +#include "qapi/qmp/qjson.h" #include "qapi/qmp/qlist.h" #include "qemu/module.h" #include "qemu/option.h" @@ -196,9 +198,10 @@ static void cleanup(const char *filename) static QList *migrate_start_get_qmp_capabilities(const MigrateStart *args) { - QList *capabilities = qlist_new(); + QList *capabilities = NULL; if (args->oob) { + capabilities = qlist_new(); qlist_append_str(capabilities, "oob"); } return capabilities; @@ -221,6 +224,8 @@ int migrate_start(QTestState **from, QTestState **to, const char *uri, g_autofree char *machine = NULL; const char *bootpath; g_autoptr(QList) capabilities = migrate_start_get_qmp_capabilities(args); + g_autofree char *memory_backend = NULL; + const char *events; if (args->use_shmem) { if (!g_file_test("/dev/shm", G_FILE_TEST_IS_DIR)) { @@ -296,6 +301,12 @@ int migrate_start(QTestState **from, QTestState **to, const char *uri, memory_size, shmem_path); } + if (args->memory_backend) { + memory_backend = g_strdup_printf(args->memory_backend, memory_size); + } else { + memory_backend = g_strdup_printf("-m %s ", memory_size); + } + if (args->use_dirty_ring) { kvm_opts = ",dirty-ring-size=4096"; } @@ -314,40 +325,48 @@ int migrate_start(QTestState **from, QTestState **to, const char *uri, cmd_source = g_strdup_printf("-accel kvm%s -accel tcg " "-machine %s,%s " "-name source,debug-threads=on " - "-m %s " + "%s " "-serial file:%s/src_serial " "%s %s %s %s", kvm_opts ? kvm_opts : "", machine, machine_opts, - memory_size, tmpfs, + memory_backend, tmpfs, arch_opts ? arch_opts : "", shmem_opts ? shmem_opts : "", args->opts_source ? args->opts_source : "", ignore_stderr); if (!args->only_target) { *from = qtest_init_with_env_and_capabilities(QEMU_ENV_SRC, cmd_source, - capabilities); + capabilities, true); qtest_qmp_set_event_callback(*from, migrate_watch_for_events, &src_state); } + /* + * If the monitor connection is deferred, enable events on the command line + * so none are missed. This is for testing only, do not set migration + * options like this in general. + */ + events = args->defer_target_connect ? "-global migration.x-events=on" : ""; + cmd_target = g_strdup_printf("-accel kvm%s -accel tcg " "-machine %s,%s " "-name target,debug-threads=on " - "-m %s " + "%s " "-serial file:%s/dest_serial " "-incoming %s " - "%s %s %s %s", + "%s %s %s %s %s", kvm_opts ? kvm_opts : "", machine, machine_opts, - memory_size, tmpfs, uri, + memory_backend, tmpfs, uri, + events, arch_opts ? arch_opts : "", shmem_opts ? shmem_opts : "", args->opts_target ? args->opts_target : "", ignore_stderr); *to = qtest_init_with_env_and_capabilities(QEMU_ENV_DST, cmd_target, - capabilities); + capabilities, !args->defer_target_connect); qtest_qmp_set_event_callback(*to, migrate_watch_for_events, &dst_state); @@ -365,7 +384,9 @@ int migrate_start(QTestState **from, QTestState **to, const char *uri, * to mimic as closer as that. */ migrate_set_capability(*from, "events", true); - migrate_set_capability(*to, "events", true); + if (!args->defer_target_connect) { + migrate_set_capability(*to, "events", true); + } return 0; } @@ -399,6 +420,7 @@ void migrate_end(QTestState *from, QTestState *to, bool test_dest) qtest_quit(to); cleanup("migsocket"); + cleanup("cpr.sock"); cleanup("src_serial"); cleanup("dest_serial"); cleanup(FILE_TEST_FILENAME); @@ -686,6 +708,10 @@ void test_precopy_common(MigrateCommon *args) { QTestState *from, *to; void *data_hook = NULL; + QObject *in_channels = NULL; + QObject *out_channels = NULL; + + g_assert(!args->cpr_channel || args->connect_channels); if (migrate_start(&from, &to, args->listen_uri, &args->start)) { return; @@ -718,12 +744,40 @@ void test_precopy_common(MigrateCommon *args) } } + /* + * The cpr channel must be included in outgoing channels, but not in + * migrate-incoming channels. + */ + if (args->connect_channels) { + if (args->start.defer_target_connect && + !strcmp(args->listen_uri, "defer")) { + in_channels = qobject_from_json(args->connect_channels, + &error_abort); + } + out_channels = qobject_from_json(args->connect_channels, &error_abort); + + if (args->cpr_channel) { + QList *channels_list = qobject_to(QList, out_channels); + QObject *obj = migrate_str_to_channel(args->cpr_channel); + + qlist_append(channels_list, obj); + } + } + if (args->result == MIG_TEST_QMP_ERROR) { - migrate_qmp_fail(from, args->connect_uri, args->connect_channels, "{}"); + migrate_qmp_fail(from, args->connect_uri, out_channels, "{}"); goto finish; } - migrate_qmp(from, to, args->connect_uri, args->connect_channels, "{}"); + migrate_qmp(from, to, args->connect_uri, out_channels, "{}"); + + if (args->start.defer_target_connect) { + qtest_connect(to); + qtest_qmp_handshake(to, NULL); + if (!strcmp(args->listen_uri, "defer")) { + migrate_incoming_qmp(to, args->connect_uri, in_channels, "{}"); + } + } if (args->result != MIG_TEST_SUCCEED) { bool allow_active = args->result == MIG_TEST_FAIL; @@ -868,7 +922,7 @@ void test_file_common(MigrateCommon *args, bool stop_src) * We need to wait for the source to finish before starting the * destination. */ - migrate_incoming_qmp(to, args->connect_uri, "{}"); + migrate_incoming_qmp(to, args->connect_uri, NULL, "{}"); wait_for_migration_complete(to); if (stop_src) { @@ -904,7 +958,7 @@ void *migrate_hook_start_precopy_tcp_multifd_common(QTestState *from, migrate_set_capability(to, "multifd", true); /* Start incoming migration from the 1st socket */ - migrate_incoming_qmp(to, "tcp:127.0.0.1:0", "{}"); + migrate_incoming_qmp(to, "tcp:127.0.0.1:0", NULL, "{}"); return NULL; } diff --git a/tests/qtest/migration/framework.h b/tests/qtest/migration/framework.h index 7991ee56b6..cb4a984700 100644 --- a/tests/qtest/migration/framework.h +++ b/tests/qtest/migration/framework.h @@ -111,6 +111,14 @@ typedef struct { bool suspend_me; /* enable OOB QMP capability */ bool oob; + /* + * Format string for the main memory backend, containing one %s where the + * size is plugged in. If omitted, "-m %s" is used. + */ + const char *memory_backend; + + /* Do not connect to target monitor and qtest sockets in qtest_init */ + bool defer_target_connect; } MigrateStart; typedef enum PostcopyRecoveryFailStage { @@ -146,6 +154,9 @@ typedef struct { */ const char *connect_channels; + /* Optional: the cpr migration channel, in JSON or dotted keys format */ + const char *cpr_channel; + /* Optional: callback to run at start to set migration parameters */ TestMigrateStartHook start_hook; /* Optional: callback to run at finish to cleanup */ diff --git a/tests/qtest/migration/migration-qmp.c b/tests/qtest/migration/migration-qmp.c index 9431d2beda..5610f6d15d 100644 --- a/tests/qtest/migration/migration-qmp.c +++ b/tests/qtest/migration/migration-qmp.c @@ -15,9 +15,13 @@ #include "migration-qmp.h" #include "migration-util.h" #include "qapi/error.h" +#include "qapi/qapi-types-migration.h" +#include "qapi/qapi-visit-migration.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qjson.h" #include "qapi/qmp/qlist.h" +#include "qapi/qobject-input-visitor.h" +#include "qapi/qobject-output-visitor.h" /* * Number of seconds we wait when looking for migration @@ -47,8 +51,33 @@ void migration_event_wait(QTestState *s, const char *target) } while (!found); } +/* + * Convert a string representing a single channel to an object. + * @str may be in JSON or dotted keys format. + */ +QObject *migrate_str_to_channel(const char *str) +{ + Visitor *v; + MigrationChannel *channel; + QObject *obj; + + /* Create the channel */ + v = qobject_input_visitor_new_str(str, "channel-type", &error_abort); + visit_type_MigrationChannel(v, NULL, &channel, &error_abort); + visit_free(v); + + /* Create the object */ + v = qobject_output_visitor_new(&obj); + visit_type_MigrationChannel(v, NULL, &channel, &error_abort); + visit_complete(v, &obj); + visit_free(v); + + qapi_free_MigrationChannel(channel); + return obj; +} + void migrate_qmp_fail(QTestState *who, const char *uri, - const char *channels, const char *fmt, ...) + QObject *channels, const char *fmt, ...) { va_list ap; QDict *args, *err; @@ -64,8 +93,7 @@ void migrate_qmp_fail(QTestState *who, const char *uri, g_assert(!qdict_haskey(args, "channels")); if (channels) { - QObject *channels_obj = qobject_from_json(channels, &error_abort); - qdict_put_obj(args, "channels", channels_obj); + qdict_put_obj(args, "channels", channels); } err = qtest_qmp_assert_failure_ref( @@ -82,7 +110,7 @@ void migrate_qmp_fail(QTestState *who, const char *uri, * qobject_from_jsonf_nofail()) with "uri": @uri spliced in. */ void migrate_qmp(QTestState *who, QTestState *to, const char *uri, - const char *channels, const char *fmt, ...) + QObject *channels, const char *fmt, ...) { va_list ap; QDict *args; @@ -102,10 +130,9 @@ void migrate_qmp(QTestState *who, QTestState *to, const char *uri, g_assert(!qdict_haskey(args, "channels")); if (channels) { - QObject *channels_obj = qobject_from_json(channels, &error_abort); - QList *channel_list = qobject_to(QList, channels_obj); + QList *channel_list = qobject_to(QList, channels); migrate_set_ports(to, channel_list); - qdict_put_obj(args, "channels", channels_obj); + qdict_put_obj(args, "channels", channels); } qtest_qmp_assert_success(who, @@ -123,7 +150,8 @@ void migrate_set_capability(QTestState *who, const char *capability, capability, value); } -void migrate_incoming_qmp(QTestState *to, const char *uri, const char *fmt, ...) +void migrate_incoming_qmp(QTestState *to, const char *uri, QObject *channels, + const char *fmt, ...) { va_list ap; QDict *args, *rsp; @@ -133,7 +161,14 @@ void migrate_incoming_qmp(QTestState *to, const char *uri, const char *fmt, ...) va_end(ap); g_assert(!qdict_haskey(args, "uri")); - qdict_put_str(args, "uri", uri); + if (uri) { + qdict_put_str(args, "uri", uri); + } + + g_assert(!qdict_haskey(args, "channels")); + if (channels) { + qdict_put_obj(args, "channels", channels); + } /* This function relies on the event to work, make sure it's enabled */ migrate_set_capability(to, "events", true); diff --git a/tests/qtest/migration/migration-qmp.h b/tests/qtest/migration/migration-qmp.h index caaa78722a..faa8181d91 100644 --- a/tests/qtest/migration/migration-qmp.h +++ b/tests/qtest/migration/migration-qmp.h @@ -4,17 +4,19 @@ #include "migration-util.h" +QObject *migrate_str_to_channel(const char *str); + G_GNUC_PRINTF(4, 5) void migrate_qmp_fail(QTestState *who, const char *uri, - const char *channels, const char *fmt, ...); + QObject *channels, const char *fmt, ...); G_GNUC_PRINTF(5, 6) void migrate_qmp(QTestState *who, QTestState *to, const char *uri, - const char *channels, const char *fmt, ...); + QObject *channels, const char *fmt, ...); -G_GNUC_PRINTF(3, 4) +G_GNUC_PRINTF(4, 5) void migrate_incoming_qmp(QTestState *who, const char *uri, - const char *fmt, ...); + QObject *channels, const char *fmt, ...); void migration_event_wait(QTestState *s, const char *target); void migrate_set_capability(QTestState *who, const char *capability, diff --git a/tests/qtest/migration/migration-util.c b/tests/qtest/migration/migration-util.c index 526bed74ea..0ce1413b6c 100644 --- a/tests/qtest/migration/migration-util.c +++ b/tests/qtest/migration/migration-util.c @@ -135,25 +135,32 @@ migrate_get_connect_qdict(QTestState *who) void migrate_set_ports(QTestState *to, QList *channel_list) { - QDict *addr; + g_autoptr(QDict) addr = NULL; QListEntry *entry; const char *addr_port = NULL; - addr = migrate_get_connect_qdict(to); - QLIST_FOREACH_ENTRY(channel_list, entry) { QDict *channel = qobject_to(QDict, qlist_entry_obj(entry)); QDict *addrdict = qdict_get_qdict(channel, "addr"); - if (qdict_haskey(addrdict, "port") && - qdict_haskey(addr, "port") && - (strcmp(qdict_get_str(addrdict, "port"), "0") == 0)) { + if (!qdict_haskey(addrdict, "port") || + strcmp(qdict_get_str(addrdict, "port"), "0")) { + continue; + } + + /* + * Fetch addr only if needed, so tests that are not yet connected to + * the monitor do not query it. Such tests cannot use port=0. + */ + if (!addr) { + addr = migrate_get_connect_qdict(to); + } + + if (qdict_haskey(addr, "port")) { addr_port = qdict_get_str(addr, "port"); qdict_put_str(addrdict, "port", addr_port); } } - - qobject_unref(addr); } bool migrate_watch_for_events(QTestState *who, const char *name, diff --git a/tests/qtest/migration/misc-tests.c b/tests/qtest/migration/misc-tests.c index 6173430748..dda3707cf3 100644 --- a/tests/qtest/migration/misc-tests.c +++ b/tests/qtest/migration/misc-tests.c @@ -11,6 +11,8 @@ */ #include "qemu/osdep.h" +#include "qapi/error.h" +#include "qapi/qmp/qjson.h" #include "libqtest.h" #include "migration/framework.h" #include "migration/migration-qmp.h" @@ -205,6 +207,7 @@ static void test_validate_uuid_dst_not_set(void) static void do_test_validate_uri_channel(MigrateCommon *args) { QTestState *from, *to; + QObject *channels; if (migrate_start(&from, &to, args->listen_uri, &args->start)) { return; @@ -217,7 +220,11 @@ static void do_test_validate_uri_channel(MigrateCommon *args) * 'uri' and 'channels' validation is checked even before the migration * starts. */ - migrate_qmp_fail(from, args->connect_uri, args->connect_channels, "{}"); + channels = args->connect_channels ? + qobject_from_json(args->connect_channels, &error_abort) : + NULL; + migrate_qmp_fail(from, args->connect_uri, channels, "{}"); + migrate_end(from, to, false); } diff --git a/tests/qtest/migration/precopy-tests.c b/tests/qtest/migration/precopy-tests.c index 23599b29ee..436dbd98e8 100644 --- a/tests/qtest/migration/precopy-tests.c +++ b/tests/qtest/migration/precopy-tests.c @@ -152,7 +152,7 @@ static void *migrate_hook_start_fd(QTestState *from, close(pair[0]); /* Start incoming migration from the 1st socket */ - migrate_incoming_qmp(to, "fd:fd-mig", "{}"); + migrate_incoming_qmp(to, "fd:fd-mig", NULL, "{}"); /* Send the 2nd socket to the target */ qtest_qmp_fds_assert_success(from, &pair[1], 1, @@ -479,7 +479,7 @@ static void test_multifd_tcp_cancel(void) migrate_set_capability(to, "multifd", true); /* Start incoming migration from the 1st socket */ - migrate_incoming_qmp(to, "tcp:127.0.0.1:0", "{}"); + migrate_incoming_qmp(to, "tcp:127.0.0.1:0", NULL, "{}"); /* Wait for the first serial output from the source */ wait_for_serial("src_serial"); @@ -518,7 +518,7 @@ static void test_multifd_tcp_cancel(void) migrate_set_capability(to2, "multifd", true); /* Start incoming migration from the 1st socket */ - migrate_incoming_qmp(to2, "tcp:127.0.0.1:0", "{}"); + migrate_incoming_qmp(to2, "tcp:127.0.0.1:0", NULL, "{}"); migrate_ensure_non_converge(from); diff --git a/tests/qtest/virtio-net-failover.c b/tests/qtest/virtio-net-failover.c index 08365ffa11..f04573f98c 100644 --- a/tests/qtest/virtio-net-failover.c +++ b/tests/qtest/virtio-net-failover.c @@ -773,7 +773,7 @@ static void test_migrate_in(gconstpointer opaque) check_one_card(qts, true, "standby0", MAC_STANDBY0); check_one_card(qts, false, "primary0", MAC_PRIMARY0); - migrate_incoming_qmp(qts, uri, "{}"); + migrate_incoming_qmp(qts, uri, NULL, "{}"); resp = get_failover_negociated_event(qts); g_assert_cmpstr(qdict_get_str(resp, "device-id"), ==, "standby0"); @@ -895,7 +895,7 @@ static void test_off_migrate_in(gconstpointer opaque) check_one_card(qts, true, "standby0", MAC_STANDBY0); check_one_card(qts, true, "primary0", MAC_PRIMARY0); - migrate_incoming_qmp(qts, uri, "{}"); + migrate_incoming_qmp(qts, uri, NULL, "{}"); check_one_card(qts, true, "standby0", MAC_STANDBY0); check_one_card(qts, true, "primary0", MAC_PRIMARY0); @@ -1022,7 +1022,7 @@ static void test_guest_off_migrate_in(gconstpointer opaque) check_one_card(qts, true, "standby0", MAC_STANDBY0); check_one_card(qts, false, "primary0", MAC_PRIMARY0); - migrate_incoming_qmp(qts, uri, "{}"); + migrate_incoming_qmp(qts, uri, NULL, "{}"); check_one_card(qts, true, "standby0", MAC_STANDBY0); check_one_card(qts, false, "primary0", MAC_PRIMARY0); @@ -1747,7 +1747,7 @@ static void test_multi_in(gconstpointer opaque) check_one_card(qts, true, "standby1", MAC_STANDBY1); check_one_card(qts, false, "primary1", MAC_PRIMARY1); - migrate_incoming_qmp(qts, uri, "{}"); + migrate_incoming_qmp(qts, uri, NULL, "{}"); resp = get_failover_negociated_event(qts); g_assert_cmpstr(qdict_get_str(resp, "device-id"), ==, "standby0"); diff --git a/tests/tcg/s390x/Makefile.softmmu-target b/tests/tcg/s390x/Makefile.softmmu-target index 7adde2fa08..8cd4667c63 100644 --- a/tests/tcg/s390x/Makefile.softmmu-target +++ b/tests/tcg/s390x/Makefile.softmmu-target @@ -42,6 +42,7 @@ $(ASM_TESTS): LDFLAGS += -Wl,-T$(LINK_SCRIPT) -Wl,--build-id=none $(ASM_TESTS): $(LINK_SCRIPT) TESTS += $(ASM_TESTS) +MULTIARCH_TESTS += mvc-smc S390X_MULTIARCH_RUNTIME_OBJS = head64.o console.o $(MINILIB_OBJS) $(MULTIARCH_TESTS): $(S390X_MULTIARCH_RUNTIME_OBJS) $(MULTIARCH_TESTS): LDFLAGS += $(S390X_MULTIARCH_RUNTIME_OBJS) diff --git a/tests/tcg/s390x/mvc-smc.c b/tests/tcg/s390x/mvc-smc.c new file mode 100644 index 0000000000..d68f60caa8 --- /dev/null +++ b/tests/tcg/s390x/mvc-smc.c @@ -0,0 +1,82 @@ +/* + * Test modifying code using the MVC instruction. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include <minilib.h> + +#define PAGE_SIZE 4096 +#define BR_14_SIZE 2 +#define RWX_OFFSET 2 + +static unsigned char rw[PAGE_SIZE + BR_14_SIZE]; +static unsigned char rwx[RWX_OFFSET + sizeof(rw)] + __attribute__((aligned(PAGE_SIZE))); + +typedef unsigned long (*function_t)(unsigned long); + +static int emit_function(unsigned char *p, int n) +{ + int i = 0, val = 0; + + while (i < n - 2) { + /* aghi %r2,1 */ + p[i++] = 0xa7; + p[i++] = 0x2b; + p[i++] = 0x00; + p[i++] = 0x01; + val++; + } + + /* br %r14 */ + p[i++] = 0x07; + p[i++] = 0xfe; + + return val; +} + +static void memcpy_mvc(void *dest, void *src, unsigned long n) +{ + while (n >= 256) { + asm("mvc 0(256,%[dest]),0(%[src])" + : + : [dest] "a" (dest) + , [src] "a" (src) + : "memory"); + dest += 256; + src += 256; + n -= 256; + } + asm("exrl %[n],0f\n" + "j 1f\n" + "0: mvc 0(1,%[dest]),0(%[src])\n" + "1:" + : + : [dest] "a" (dest) + , [src] "a" (src) + , [n] "a" (n) + : "memory"); +} + +int main(void) +{ + int expected, size; + + /* Create a TB. */ + size = sizeof(rwx) - RWX_OFFSET - 4; + expected = emit_function(rwx + RWX_OFFSET, size); + if (((function_t)(rwx + RWX_OFFSET))(0) != expected) { + return 1; + } + + /* Overwrite the TB. */ + size += 4; + expected = emit_function(rw, size); + memcpy_mvc(rwx + RWX_OFFSET, rw, size); + if (((function_t)(rwx + RWX_OFFSET))(0) != expected) { + return 2; + } + + return 0; +} |