diff options
Diffstat (limited to 'tests')
724 files changed, 27477 insertions, 21452 deletions
diff --git a/tests/Makefile.include b/tests/Makefile.include index 010369b..23fb722 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -18,7 +18,6 @@ ifneq ($(filter $(all-check-targets), check-softfloat),) @echo " $(MAKE) check-tcg Run TCG tests" @echo " $(MAKE) check-softfloat Run FPU emulation tests" endif - @echo " $(MAKE) check-avocado Run avocado (integration) tests for currently configured targets" @echo @echo " $(MAKE) check-report.junit.xml Generates an aggregated XML test report" @echo " $(MAKE) check-venv Creates a Python venv for tests" @@ -26,7 +25,6 @@ endif @echo @echo "The following are useful for CI builds" @echo " $(MAKE) check-build Build most test binaries" - @echo " $(MAKE) get-vm-images Downloads all images used by avocado tests, according to configured targets (~350 MB each, 1.5 GB max)" @echo @echo @echo "The variable SPEED can be set to control the gtester speed setting." @@ -86,26 +84,12 @@ distclean-tcg: $(DISTCLEAN_TCG_TARGET_RULES) # Python venv for running tests -.PHONY: check-venv check-avocado check-acceptance check-acceptance-deprecated-warning +.PHONY: check-venv # Build up our target list from the filtered list of ninja targets TARGETS=$(patsubst libqemu-%.a, %, $(filter libqemu-%.a, $(ninja-targets))) TESTS_VENV_TOKEN=$(BUILD_DIR)/pyvenv/tests.group -TESTS_RESULTS_DIR=$(BUILD_DIR)/tests/results -ifndef AVOCADO_TESTS - AVOCADO_TESTS=tests/avocado -endif -# Controls the output generated by Avocado when running tests. -# Any number of command separated loggers are accepted. For more -# information please refer to "avocado --help". -AVOCADO_SHOW?=app -ifndef AVOCADO_TAGS - AVOCADO_CMDLINE_TAGS=$(patsubst %-softmmu,-t arch:%, \ - $(filter %-softmmu,$(TARGETS))) -else - AVOCADO_CMDLINE_TAGS=$(addprefix -t , $(AVOCADO_TAGS)) -endif quiet-venv-pip = $(quiet-@)$(call quiet-command-run, \ $(PYTHON) -m pip -q --disable-pip-version-check $1, \ @@ -113,47 +97,11 @@ quiet-venv-pip = $(quiet-@)$(call quiet-command-run, \ $(TESTS_VENV_TOKEN): $(SRC_PATH)/pythondeps.toml $(call quiet-venv-pip,install -e "$(SRC_PATH)/python/") - $(MKVENV_ENSUREGROUP) $< avocado + $(MKVENV_ENSUREGROUP) $< testdeps $(call quiet-command, touch $@) -$(TESTS_RESULTS_DIR): - $(call quiet-command, mkdir -p $@, \ - MKDIR, $@) - check-venv: $(TESTS_VENV_TOKEN) -FEDORA_31_ARCHES_TARGETS=$(patsubst %-softmmu,%, $(filter %-softmmu,$(TARGETS))) -FEDORA_31_ARCHES_CANDIDATES=$(patsubst ppc64,ppc64le,$(FEDORA_31_ARCHES_TARGETS)) -FEDORA_31_ARCHES := x86_64 aarch64 ppc64le s390x -FEDORA_31_DOWNLOAD=$(filter $(FEDORA_31_ARCHES),$(FEDORA_31_ARCHES_CANDIDATES)) - -# download one specific Fedora 31 image -get-vm-image-fedora-31-%: check-venv - $(call quiet-command, \ - $(PYTHON) -m avocado vmimage get \ - --distro=fedora --distro-version=31 --arch=$*, \ - "AVOCADO", "Downloading avocado tests VM image for $*") - -# download all vm images, according to defined targets -get-vm-images: check-venv $(patsubst %,get-vm-image-fedora-31-%, $(FEDORA_31_DOWNLOAD)) - -check-avocado: check-venv $(TESTS_RESULTS_DIR) get-vm-images - $(call quiet-command, \ - $(PYTHON) -m avocado \ - --show=$(AVOCADO_SHOW) run --job-results-dir=$(TESTS_RESULTS_DIR) \ - $(if $(AVOCADO_TAGS),, --filter-by-tags-include-empty \ - --filter-by-tags-include-empty-key) \ - $(AVOCADO_CMDLINE_TAGS) --max-parallel-tasks=1 \ - $(if $(GITLAB_CI),,--failfast) $(AVOCADO_TESTS), \ - "AVOCADO", "tests/avocado") - -check-acceptance-deprecated-warning: - @echo - @echo "Note '$(MAKE) check-acceptance' is deprecated, use '$(MAKE) check-avocado' instead." - @echo - -check-acceptance: check-acceptance-deprecated-warning | check-avocado - FUNCTIONAL_TARGETS=$(patsubst %-softmmu,check-functional-%, $(filter %-softmmu,$(TARGETS))) .PHONY: $(FUNCTIONAL_TARGETS) $(FUNCTIONAL_TARGETS): @@ -166,13 +114,13 @@ check-functional: # Consolidated targets -.PHONY: check check-clean get-vm-images +.PHONY: check check-clean check: check-build: run-ninja check-clean: - rm -rf $(TESTS_RESULTS_DIR) + rm -rf $(BUILD_DIR)/tests/functional clean: check-clean clean-tcg distclean: distclean-tcg diff --git a/tests/avocado/README.rst b/tests/avocado/README.rst deleted file mode 100644 index 9448837..0000000 --- a/tests/avocado/README.rst +++ /dev/null @@ -1,10 +0,0 @@ -============================================= -Integration tests using the Avocado Framework -============================================= - -This directory contains integration tests. They're usually higher -level, and may interact with external resources and with various -guest operating systems. - -For more information, please refer to ``docs/devel/testing.rst``, -section "Integration tests using the Avocado Framework". diff --git a/tests/avocado/avocado_qemu/__init__.py b/tests/avocado/avocado_qemu/__init__.py deleted file mode 100644 index 0e4ecea..0000000 --- a/tests/avocado/avocado_qemu/__init__.py +++ /dev/null @@ -1,434 +0,0 @@ -# Test class and utilities for functional tests -# -# Copyright (c) 2018 Red Hat, Inc. -# -# Author: -# Cleber Rosa <crosa@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 logging -import os -import subprocess -import sys -import tempfile -import time -import uuid - -import avocado -from avocado.utils import ssh -from avocado.utils.path import find_command - -from qemu.machine import QEMUMachine -from qemu.utils import (get_info_usernet_hostfwd_port, kvm_available, - tcg_available) - - -#: The QEMU build root directory. It may also be the source directory -#: if building from the source dir, but it's safer to use BUILD_DIR for -#: that purpose. Be aware that if this code is moved outside of a source -#: and build tree, it will not be accurate. -BUILD_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(__file__)))) - - -def has_cmd(name, args=None): - """ - This function is for use in a @avocado.skipUnless decorator, e.g.: - - @skipUnless(*has_cmd('sudo -n', ('sudo', '-n', 'true'))) - def test_something_that_needs_sudo(self): - ... - """ - - if args is None: - args = ('which', name) - - try: - _, stderr, exitcode = run_cmd(args) - except Exception as e: - exitcode = -1 - stderr = str(e) - - if exitcode != 0: - cmd_line = ' '.join(args) - err = f'{name} required, but "{cmd_line}" failed: {stderr.strip()}' - return (False, err) - else: - return (True, '') - -def has_cmds(*cmds): - """ - This function is for use in a @avocado.skipUnless decorator and - allows checking for the availability of multiple commands, e.g.: - - @skipUnless(*has_cmds(('cmd1', ('cmd1', '--some-parameter')), - 'cmd2', 'cmd3')) - def test_something_that_needs_cmd1_and_cmd2(self): - ... - """ - - for cmd in cmds: - if isinstance(cmd, str): - cmd = (cmd,) - - ok, errstr = has_cmd(*cmd) - if not ok: - return (False, errstr) - - return (True, '') - -def run_cmd(args): - subp = subprocess.Popen(args, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - universal_newlines=True) - stdout, stderr = subp.communicate() - ret = subp.returncode - - return (stdout, stderr, ret) - -def is_readable_executable_file(path): - return os.path.isfile(path) and os.access(path, os.R_OK | os.X_OK) - - -def pick_default_qemu_bin(bin_prefix='qemu-system-', arch=None): - """ - Picks the path of a QEMU binary, starting either in the current working - directory or in the source tree root directory. - - :param arch: the arch to use when looking for a QEMU binary (the target - will match the arch given). If None (the default), arch - will be the current host system arch (as given by - :func:`os.uname`). - :type arch: str - :returns: the path to the default QEMU binary or None if one could not - be found - :rtype: str or None - """ - if arch is None: - arch = os.uname()[4] - # qemu binary path does not match arch for powerpc, handle it - if 'ppc64le' in arch: - arch = 'ppc64' - qemu_bin_name = bin_prefix + arch - qemu_bin_paths = [ - os.path.join(".", qemu_bin_name), - os.path.join(BUILD_DIR, qemu_bin_name), - os.path.join(BUILD_DIR, "build", qemu_bin_name), - ] - for path in qemu_bin_paths: - if is_readable_executable_file(path): - return path - return None - - -def _console_interaction(test, success_message, failure_message, - send_string, keep_sending=False, vm=None): - assert not keep_sending or send_string - if vm is None: - vm = test.vm - console = vm.console_file - console_logger = logging.getLogger('console') - while True: - if send_string: - vm.console_socket.sendall(send_string.encode()) - if not keep_sending: - send_string = None # send only once - - # Only consume console output if waiting for something - if success_message is None and failure_message is None: - if send_string is None: - break - continue - - try: - msg = console.readline().decode().strip() - except UnicodeDecodeError: - msg = None - if not msg: - continue - console_logger.debug(msg) - if success_message is None or success_message in msg: - break - if failure_message and failure_message in msg: - console.close() - fail = 'Failure message found in console: "%s". Expected: "%s"' % \ - (failure_message, success_message) - test.fail(fail) - -def interrupt_interactive_console_until_pattern(test, success_message, - failure_message=None, - interrupt_string='\r'): - """ - Keep sending a string to interrupt a console prompt, while logging the - console output. Typical use case is to break a boot loader prompt, such: - - Press a key within 5 seconds to interrupt boot process. - 5 - 4 - 3 - 2 - 1 - Booting default image... - - :param test: an Avocado test containing a VM that will have its console - read and probed for a success or failure message - :type test: :class:`avocado_qemu.QemuSystemTest` - :param success_message: if this message appears, test succeeds - :param failure_message: if this message appears, test fails - :param interrupt_string: a string to send to the console before trying - to read a new line - """ - _console_interaction(test, success_message, failure_message, - interrupt_string, True) - -def wait_for_console_pattern(test, success_message, failure_message=None, - vm=None): - """ - Waits for messages to appear on the console, while logging the content - - :param test: an Avocado test containing a VM that will have its console - read and probed for a success or failure message - :type test: :class:`avocado_qemu.QemuSystemTest` - :param success_message: if this message appears, test succeeds - :param failure_message: if this message appears, test fails - """ - _console_interaction(test, success_message, failure_message, None, vm=vm) - -def exec_command(test, command): - """ - Send a command to a console (appending CRLF characters), while logging - the content. - - :param test: an Avocado test containing a VM. - :type test: :class:`avocado_qemu.QemuSystemTest` - :param command: the command to send - :type command: str - """ - _console_interaction(test, None, None, command + '\r') - -def exec_command_and_wait_for_pattern(test, command, - success_message, failure_message=None): - """ - Send a command to a console (appending CRLF characters), then wait - for success_message to appear on the console, while logging the. - content. Mark the test as failed if failure_message is found instead. - - :param test: an Avocado test containing a VM that will have its console - read and probed for a success or failure message - :type test: :class:`avocado_qemu.QemuSystemTest` - :param command: the command to send - :param success_message: if this message appears, test succeeds - :param failure_message: if this message appears, test fails - """ - _console_interaction(test, success_message, failure_message, command + '\r') - -class QemuBaseTest(avocado.Test): - - # default timeout for all tests, can be overridden - timeout = 120 - - def _get_unique_tag_val(self, tag_name): - """ - Gets a tag value, if unique for a key - """ - vals = self.tags.get(tag_name, []) - if len(vals) == 1: - return vals.pop() - return None - - def setUp(self, bin_prefix): - self.arch = self.params.get('arch', - default=self._get_unique_tag_val('arch')) - - self.cpu = self.params.get('cpu', - default=self._get_unique_tag_val('cpu')) - - default_qemu_bin = pick_default_qemu_bin(bin_prefix, arch=self.arch) - self.qemu_bin = self.params.get('qemu_bin', - default=default_qemu_bin) - if self.qemu_bin is None: - self.cancel("No QEMU binary defined or found in the build tree") - - def fetch_asset(self, name, - asset_hash, algorithm=None, - locations=None, expire=None, - find_only=False, cancel_on_missing=True): - return super().fetch_asset(name, - asset_hash=asset_hash, - algorithm=algorithm, - locations=locations, - expire=expire, - find_only=find_only, - cancel_on_missing=cancel_on_missing) - - -class QemuSystemTest(QemuBaseTest): - """Facilitates system emulation tests.""" - - def setUp(self): - self._vms = {} - - super().setUp('qemu-system-') - - accel_required = self._get_unique_tag_val('accel') - if accel_required: - self.require_accelerator(accel_required) - - self.machine = self.params.get('machine', - default=self._get_unique_tag_val('machine')) - - def require_accelerator(self, accelerator): - """ - Requires an accelerator to be available for the test to continue - - It takes into account the currently set qemu binary. - - If the check fails, the test is canceled. If the check itself - for the given accelerator is not available, the test is also - canceled. - - :param accelerator: name of the accelerator, such as "kvm" or "tcg" - :type accelerator: str - """ - checker = {'tcg': tcg_available, - 'kvm': kvm_available}.get(accelerator) - if checker is None: - self.cancel("Don't know how to check for the presence " - "of accelerator %s" % accelerator) - if not checker(qemu_bin=self.qemu_bin): - self.cancel("%s accelerator does not seem to be " - "available" % accelerator) - - def require_netdev(self, netdevname): - netdevhelp = run_cmd([self.qemu_bin, - '-M', 'none', '-netdev', 'help'])[0]; - if netdevhelp.find('\n' + netdevname + '\n') < 0: - self.cancel('no support for user networking') - - def require_multiprocess(self): - """ - Test for the presence of the x-pci-proxy-dev which is required - to support multiprocess. - """ - devhelp = run_cmd([self.qemu_bin, - '-M', 'none', '-device', 'help'])[0]; - if devhelp.find('x-pci-proxy-dev') < 0: - self.cancel('no support for multiprocess device emulation') - - def _new_vm(self, name, *args): - self._sd = tempfile.TemporaryDirectory(prefix="qemu_") - vm = QEMUMachine(self.qemu_bin, base_temp_dir=self.workdir, - log_dir=self.logdir) - self.log.debug('QEMUMachine "%s" created', name) - self.log.debug('QEMUMachine "%s" temp_dir: %s', name, vm.temp_dir) - self.log.debug('QEMUMachine "%s" log_dir: %s', name, vm.log_dir) - if args: - vm.add_args(*args) - return vm - - def get_qemu_img(self): - self.log.debug('Looking for and selecting a qemu-img binary') - - # If qemu-img has been built, use it, otherwise the system wide one - # will be used. - qemu_img = os.path.join(BUILD_DIR, 'qemu-img') - if not os.path.exists(qemu_img): - qemu_img = find_command('qemu-img', False) - if qemu_img is False: - self.cancel('Could not find "qemu-img"') - - return qemu_img - - @property - def vm(self): - return self.get_vm(name='default') - - def get_vm(self, *args, name=None): - if not name: - name = str(uuid.uuid4()) - if self._vms.get(name) is None: - self._vms[name] = self._new_vm(name, *args) - if self.cpu is not None: - self._vms[name].add_args('-cpu', self.cpu) - if self.machine is not None: - self._vms[name].set_machine(self.machine) - return self._vms[name] - - def set_vm_arg(self, arg, value): - """ - Set an argument to list of extra arguments to be given to the QEMU - binary. If the argument already exists then its value is replaced. - - :param arg: the QEMU argument, such as "-cpu" in "-cpu host" - :type arg: str - :param value: the argument value, such as "host" in "-cpu host" - :type value: str - """ - if not arg or not value: - return - if arg not in self.vm.args: - self.vm.args.extend([arg, value]) - else: - idx = self.vm.args.index(arg) + 1 - if idx < len(self.vm.args): - self.vm.args[idx] = value - else: - self.vm.args.append(value) - - def tearDown(self): - for vm in self._vms.values(): - vm.shutdown() - self._sd = None - super().tearDown() - - -class LinuxSSHMixIn: - """Contains utility methods for interacting with a guest via SSH.""" - - def ssh_connect(self, username, credential, credential_is_key=True): - self.ssh_logger = logging.getLogger('ssh') - res = self.vm.cmd('human-monitor-command', - command_line='info usernet') - port = get_info_usernet_hostfwd_port(res) - self.assertIsNotNone(port) - self.assertGreater(port, 0) - self.log.debug('sshd listening on port: %d', port) - if credential_is_key: - self.ssh_session = ssh.Session('127.0.0.1', port=port, - user=username, key=credential) - else: - self.ssh_session = ssh.Session('127.0.0.1', port=port, - user=username, password=credential) - for i in range(10): - try: - self.ssh_session.connect() - return - except: - time.sleep(i) - self.fail('ssh connection timeout') - - def ssh_command(self, command): - self.ssh_logger.info(command) - result = self.ssh_session.cmd(command) - stdout_lines = [line.rstrip() for line - in result.stdout_text.splitlines()] - for line in stdout_lines: - self.ssh_logger.info(line) - stderr_lines = [line.rstrip() for line - in result.stderr_text.splitlines()] - for line in stderr_lines: - self.ssh_logger.warning(line) - - self.assertEqual(result.exit_status, 0, - f'Guest command failed: {command}') - return stdout_lines, stderr_lines - - def ssh_command_output_contains(self, cmd, exp): - stdout, _ = self.ssh_command(cmd) - for line in stdout: - if exp in line: - break - else: - self.fail('"%s" output does not contain "%s"' % (cmd, exp)) diff --git a/tests/avocado/avocado_qemu/linuxtest.py b/tests/avocado/avocado_qemu/linuxtest.py deleted file mode 100644 index 66fb9f1..0000000 --- a/tests/avocado/avocado_qemu/linuxtest.py +++ /dev/null @@ -1,253 +0,0 @@ -# Test class and utilities for functional Linux-based tests -# -# Copyright (c) 2018 Red Hat, Inc. -# -# Author: -# Cleber Rosa <crosa@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 os -import shutil - -from avocado.utils import cloudinit, datadrainer, process, vmimage - -from avocado_qemu import LinuxSSHMixIn -from avocado_qemu import QemuSystemTest - -if os.path.islink(os.path.dirname(os.path.dirname(__file__))): - # The link to the avocado tests dir in the source code directory - lnk = os.path.dirname(os.path.dirname(__file__)) - #: The QEMU root source directory - SOURCE_DIR = os.path.dirname(os.path.dirname(os.readlink(lnk))) -else: - SOURCE_DIR = BUILD_DIR - -class LinuxDistro: - """Represents a Linux distribution - - Holds information of known distros. - """ - #: A collection of known distros and their respective image checksum - KNOWN_DISTROS = { - 'fedora': { - '31': { - 'x86_64': - {'checksum': ('e3c1b309d9203604922d6e255c2c5d09' - '8a309c2d46215d8fc026954f3c5c27a0'), - 'pxeboot_url': ('https://archives.fedoraproject.org/' - 'pub/archive/fedora/linux/releases/31/' - 'Everything/x86_64/os/images/pxeboot/'), - 'kernel_params': ('root=UUID=b1438b9b-2cab-4065-a99a-' - '08a96687f73c ro no_timer_check ' - 'net.ifnames=0 console=tty1 ' - 'console=ttyS0,115200n8'), - }, - 'aarch64': - {'checksum': ('1e18d9c0cf734940c4b5d5ec592facae' - 'd2af0ad0329383d5639c997fdf16fe49'), - 'pxeboot_url': 'https://archives.fedoraproject.org/' - 'pub/archive/fedora/linux/releases/31/' - 'Everything/aarch64/os/images/pxeboot/', - 'kernel_params': ('root=UUID=b6950a44-9f3c-4076-a9c2-' - '355e8475b0a7 ro earlyprintk=pl011,0x9000000' - ' ignore_loglevel no_timer_check' - ' printk.time=1 rd_NO_PLYMOUTH' - ' console=ttyAMA0'), - }, - 'ppc64': - {'checksum': ('7c3528b85a3df4b2306e892199a9e1e4' - '3f991c506f2cc390dc4efa2026ad2f58')}, - 's390x': - {'checksum': ('4caaab5a434fd4d1079149a072fdc789' - '1e354f834d355069ca982fdcaf5a122d')}, - }, - '32': { - 'aarch64': - {'checksum': ('b367755c664a2d7a26955bbfff985855' - 'adfa2ca15e908baf15b4b176d68d3967'), - 'pxeboot_url': ('http://dl.fedoraproject.org/pub/fedora/linux/' - 'releases/32/Server/aarch64/os/images/' - 'pxeboot/'), - 'kernel_params': ('root=UUID=3df75b65-be8d-4db4-8655-' - '14d95c0e90c5 ro no_timer_check net.ifnames=0' - ' console=tty1 console=ttyS0,115200n8'), - }, - }, - '33': { - 'aarch64': - {'checksum': ('e7f75cdfd523fe5ac2ca9eeece68edc1' - 'a81f386a17f969c1d1c7c87031008a6b'), - 'pxeboot_url': ('http://dl.fedoraproject.org/pub/fedora/linux/' - 'releases/33/Server/aarch64/os/images/' - 'pxeboot/'), - 'kernel_params': ('root=UUID=d20b3ffa-6397-4a63-a734-' - '1126a0208f8a ro no_timer_check net.ifnames=0' - ' console=tty1 console=ttyS0,115200n8' - ' console=tty0'), - }, - }, - } - } - - def __init__(self, name, version, arch): - self.name = name - self.version = version - self.arch = arch - try: - info = self.KNOWN_DISTROS.get(name).get(version).get(arch) - except AttributeError: - # Unknown distro - info = None - self._info = info or {} - - @property - def checksum(self): - """Gets the cloud-image file checksum""" - return self._info.get('checksum', None) - - @checksum.setter - def checksum(self, value): - self._info['checksum'] = value - - @property - def pxeboot_url(self): - """Gets the repository url where pxeboot files can be found""" - return self._info.get('pxeboot_url', None) - - @property - def default_kernel_params(self): - """Gets the default kernel parameters""" - return self._info.get('kernel_params', None) - - -class LinuxTest(LinuxSSHMixIn, QemuSystemTest): - """Facilitates having a cloud-image Linux based available. - - For tests that intend to interact with guests, this is a better choice - to start with than the more vanilla `QemuSystemTest` class. - """ - - distro = None - username = 'root' - password = 'password' - smp = '2' - memory = '1024' - - def _set_distro(self): - distro_name = self.params.get( - 'distro', - default=self._get_unique_tag_val('distro')) - if not distro_name: - distro_name = 'fedora' - - distro_version = self.params.get( - 'distro_version', - default=self._get_unique_tag_val('distro_version')) - if not distro_version: - distro_version = '31' - - self.distro = LinuxDistro(distro_name, distro_version, self.arch) - - # The distro checksum behaves differently than distro name and - # version. First, it does not respect a tag with the same - # name, given that it's not expected to be used for filtering - # (distro name versions are the natural choice). Second, the - # order of precedence is: parameter, attribute and then value - # from KNOWN_DISTROS. - distro_checksum = self.params.get('distro_checksum', - default=None) - if distro_checksum: - self.distro.checksum = distro_checksum - - def setUp(self, ssh_pubkey=None, network_device_type='virtio-net'): - super().setUp() - self.require_netdev('user') - self._set_distro() - self.vm.add_args('-smp', self.smp) - self.vm.add_args('-m', self.memory) - # The following network device allows for SSH connections - self.vm.add_args('-netdev', 'user,id=vnet,hostfwd=:127.0.0.1:0-:22', - '-device', '%s,netdev=vnet' % network_device_type) - self.set_up_boot() - if ssh_pubkey is None: - ssh_pubkey, self.ssh_key = self.set_up_existing_ssh_keys() - self.set_up_cloudinit(ssh_pubkey) - - def set_up_existing_ssh_keys(self): - ssh_public_key = os.path.join(SOURCE_DIR, 'tests', 'keys', 'id_rsa.pub') - source_private_key = os.path.join(SOURCE_DIR, 'tests', 'keys', 'id_rsa') - ssh_dir = os.path.join(self.workdir, '.ssh') - os.mkdir(ssh_dir, mode=0o700) - ssh_private_key = os.path.join(ssh_dir, - os.path.basename(source_private_key)) - shutil.copyfile(source_private_key, ssh_private_key) - os.chmod(ssh_private_key, 0o600) - return (ssh_public_key, ssh_private_key) - - def download_boot(self): - # Set the qemu-img binary. - # If none is available, the test will cancel. - vmimage.QEMU_IMG = super().get_qemu_img() - - self.log.info('Downloading/preparing boot image') - # Fedora 31 only provides ppc64le images - image_arch = self.arch - if self.distro.name == 'fedora': - if image_arch == 'ppc64': - image_arch = 'ppc64le' - - try: - boot = vmimage.get( - self.distro.name, arch=image_arch, version=self.distro.version, - checksum=self.distro.checksum, - algorithm='sha256', - cache_dir=self.cache_dirs[0], - snapshot_dir=self.workdir) - except: - self.cancel('Failed to download/prepare boot image') - return boot.path - - def prepare_cloudinit(self, ssh_pubkey=None): - self.log.info('Preparing cloudinit image') - try: - cloudinit_iso = os.path.join(self.workdir, 'cloudinit.iso') - pubkey_content = None - if ssh_pubkey: - with open(ssh_pubkey) as pubkey: - pubkey_content = pubkey.read() - cloudinit.iso(cloudinit_iso, self.name, - username=self.username, - password=self.password, - # QEMU's hard coded usermode router address - phone_home_host='10.0.2.2', - phone_home_port=self.phone_server.server_port, - authorized_key=pubkey_content) - except Exception: - self.cancel('Failed to prepare the cloudinit image') - return cloudinit_iso - - def set_up_boot(self): - path = self.download_boot() - self.vm.add_args('-drive', 'file=%s' % path) - - def set_up_cloudinit(self, ssh_pubkey=None): - self.phone_server = cloudinit.PhoneHomeServer(('0.0.0.0', 0), - self.name) - cloudinit_iso = self.prepare_cloudinit(ssh_pubkey) - self.vm.add_args('-drive', 'file=%s,format=raw' % cloudinit_iso) - - def launch_and_wait(self, set_up_ssh_connection=True): - self.vm.set_console() - self.vm.launch() - console_drainer = datadrainer.LineLogger(self.vm.console_socket.fileno(), - logger=self.log.getChild('console')) - console_drainer.start() - self.log.info('VM launched, waiting for boot confirmation from guest') - while not self.phone_server.instance_phoned_back: - self.phone_server.handle_request() - - if set_up_ssh_connection: - self.log.info('Setting up the SSH connection') - self.ssh_connect(self.username, self.ssh_key) diff --git a/tests/avocado/boot_linux.py b/tests/avocado/boot_linux.py deleted file mode 100644 index a029ef4..0000000 --- a/tests/avocado/boot_linux.py +++ /dev/null @@ -1,132 +0,0 @@ -# Functional test that boots a complete Linux system via a cloud image -# -# Copyright (c) 2018-2020 Red Hat, Inc. -# -# Author: -# Cleber Rosa <crosa@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 os - -from avocado_qemu.linuxtest import LinuxTest -from avocado_qemu import BUILD_DIR - -from avocado import skipUnless - - -class BootLinuxX8664(LinuxTest): - """ - :avocado: tags=arch:x86_64 - """ - timeout = 480 - - def test_pc_i440fx_tcg(self): - """ - :avocado: tags=machine:pc - :avocado: tags=accel:tcg - """ - self.require_accelerator("tcg") - self.vm.add_args("-accel", "tcg") - self.launch_and_wait(set_up_ssh_connection=False) - - def test_pc_i440fx_kvm(self): - """ - :avocado: tags=machine:pc - :avocado: tags=accel:kvm - """ - self.require_accelerator("kvm") - self.vm.add_args("-accel", "kvm") - self.launch_and_wait(set_up_ssh_connection=False) - - def test_pc_q35_tcg(self): - """ - :avocado: tags=machine:q35 - :avocado: tags=accel:tcg - """ - self.require_accelerator("tcg") - self.vm.add_args("-accel", "tcg") - self.launch_and_wait(set_up_ssh_connection=False) - - def test_pc_q35_kvm(self): - """ - :avocado: tags=machine:q35 - :avocado: tags=accel:kvm - """ - self.require_accelerator("kvm") - self.vm.add_args("-accel", "kvm") - self.launch_and_wait(set_up_ssh_connection=False) - - -# For Aarch64 we only boot KVM tests in CI as booting the current -# Fedora OS in TCG tests is very heavyweight. There are lighter weight -# distros which we use in the machine_aarch64_virt.py tests. -class BootLinuxAarch64(LinuxTest): - """ - :avocado: tags=arch:aarch64 - :avocado: tags=machine:virt - """ - timeout = 720 - - def test_virt_kvm(self): - """ - :avocado: tags=accel:kvm - :avocado: tags=cpu:host - """ - self.require_accelerator("kvm") - self.vm.add_args("-accel", "kvm") - self.vm.add_args("-machine", "virt,gic-version=host") - self.vm.add_args('-bios', - os.path.join(BUILD_DIR, 'pc-bios', - 'edk2-aarch64-code.fd')) - self.vm.add_args('-device', 'virtio-rng-pci,rng=rng0') - self.vm.add_args('-object', 'rng-random,id=rng0,filename=/dev/urandom') - self.launch_and_wait(set_up_ssh_connection=False) - - -# See the tux_baseline.py tests for almost the same coverage in a lot -# less time. -class BootLinuxPPC64(LinuxTest): - """ - :avocado: tags=arch:ppc64 - """ - - timeout = 360 - - @skipUnless(os.getenv('SPEED') == 'slow', 'runtime limited') - def test_pseries_tcg(self): - """ - :avocado: tags=machine:pseries - :avocado: tags=accel:tcg - """ - self.require_accelerator("tcg") - self.vm.add_args("-accel", "tcg") - self.launch_and_wait(set_up_ssh_connection=False) - - def test_pseries_kvm(self): - """ - :avocado: tags=machine:pseries - :avocado: tags=accel:kvm - """ - self.require_accelerator("kvm") - self.vm.add_args("-accel", "kvm") - self.vm.add_args("-machine", "cap-ccf-assist=off") - self.launch_and_wait(set_up_ssh_connection=False) - -class BootLinuxS390X(LinuxTest): - """ - :avocado: tags=arch:s390x - """ - - timeout = 240 - - @skipUnless(os.getenv('SPEED') == 'slow', 'runtime limited') - def test_s390_ccw_virtio_tcg(self): - """ - :avocado: tags=machine:s390-ccw-virtio - :avocado: tags=accel:tcg - """ - self.require_accelerator("tcg") - self.vm.add_args("-accel", "tcg") - self.launch_and_wait(set_up_ssh_connection=False) diff --git a/tests/avocado/boot_linux_console.py b/tests/avocado/boot_linux_console.py deleted file mode 100644 index 18c69d6..0000000 --- a/tests/avocado/boot_linux_console.py +++ /dev/null @@ -1,1530 +0,0 @@ -# Functional test that boots a Linux kernel and checks the console -# -# Copyright (c) 2018 Red Hat, Inc. -# -# Author: -# Cleber Rosa <crosa@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 os -import lzma -import gzip -import shutil - -from avocado import skip -from avocado import skipUnless -from avocado import skipUnless -from avocado_qemu import QemuSystemTest -from avocado_qemu import exec_command -from avocado_qemu import exec_command_and_wait_for_pattern -from avocado_qemu import interrupt_interactive_console_until_pattern -from avocado_qemu import wait_for_console_pattern -from avocado.utils import process -from avocado.utils import archive - -""" -Round up to next power of 2 -""" -def pow2ceil(x): - return 1 if x == 0 else 2**(x - 1).bit_length() - -def file_truncate(path, size): - if size != os.path.getsize(path): - with open(path, 'ab+') as fd: - fd.truncate(size) - -""" -Expand file size to next power of 2 -""" -def image_pow2ceil_expand(path): - size = os.path.getsize(path) - size_aligned = pow2ceil(size) - if size != size_aligned: - with open(path, 'ab+') as fd: - fd.truncate(size_aligned) - -class LinuxKernelTest(QemuSystemTest): - KERNEL_COMMON_COMMAND_LINE = 'printk.time=0 ' - - def wait_for_console_pattern(self, success_message, vm=None): - wait_for_console_pattern(self, success_message, - failure_message='Kernel panic - not syncing', - vm=vm) - - def extract_from_deb(self, deb, path): - """ - Extracts a file from a deb package into the test workdir - - :param deb: path to the deb archive - :param path: path within the deb archive of the file to be extracted - :returns: path of the extracted file - """ - cwd = os.getcwd() - os.chdir(self.workdir) - file_path = process.run("ar t %s" % deb).stdout_text.split()[2] - process.run("ar x %s %s" % (deb, file_path)) - archive.extract(file_path, self.workdir) - os.chdir(cwd) - # Return complete path to extracted file. Because callers to - # extract_from_deb() specify 'path' with a leading slash, it is - # necessary to use os.path.relpath() as otherwise os.path.join() - # interprets it as an absolute path and drops the self.workdir part. - return os.path.normpath(os.path.join(self.workdir, - os.path.relpath(path, '/'))) - - def extract_from_rpm(self, rpm, path): - """ - Extracts a file from an RPM package into the test workdir. - - :param rpm: path to the rpm archive - :param path: path within the rpm archive of the file to be extracted - needs to be a relative path (starting with './') because - cpio(1), which is used to extract the file, expects that. - :returns: path of the extracted file - """ - cwd = os.getcwd() - os.chdir(self.workdir) - process.run("rpm2cpio %s | cpio -id %s" % (rpm, path), shell=True) - os.chdir(cwd) - return os.path.normpath(os.path.join(self.workdir, path)) - -class BootLinuxConsole(LinuxKernelTest): - """ - Boots a Linux kernel and checks that the console is operational and the - kernel command line is properly passed from QEMU to the kernel - """ - timeout = 90 - - def test_x86_64_pc(self): - """ - :avocado: tags=arch:x86_64 - :avocado: tags=machine:pc - """ - kernel_url = ('https://archives.fedoraproject.org/pub/archive/fedora' - '/linux/releases/29/Everything/x86_64/os/images/pxeboot' - '/vmlinuz') - kernel_hash = '23bebd2680757891cf7adedb033532163a792495' - kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) - - self.vm.set_console() - kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'console=ttyS0' - self.vm.add_args('-kernel', kernel_path, - '-append', kernel_command_line) - self.vm.launch() - console_pattern = 'Kernel command line: %s' % kernel_command_line - self.wait_for_console_pattern(console_pattern) - - 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') - - self.vm.set_console() - kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'console=ttyS0' - self.vm.add_args('-kernel', kernel_path, - '-append', kernel_command_line) - self.vm.launch() - console_pattern = 'Kernel command line: %s' % kernel_command_line - self.wait_for_console_pattern(console_pattern) - - 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') - - self.vm.set_console() - kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'console=ttyS0' - self.vm.add_args('-kernel', kernel_path, - '-append', kernel_command_line) - self.vm.launch() - console_pattern = 'Kernel command line: %s' % kernel_command_line - self.wait_for_console_pattern(console_pattern) - - def test_mips64el_fuloong2e(self): - """ - :avocado: tags=arch:mips64el - :avocado: tags=machine:fuloong2e - :avocado: tags=endian:little - """ - deb_url = ('http://archive.debian.org/debian/pool/main/l/linux/' - 'linux-image-3.16.0-6-loongson-2e_3.16.56-1+deb8u1_mipsel.deb') - deb_hash = 'd04d446045deecf7b755ef576551de0c4184dd44' - deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash) - kernel_path = self.extract_from_deb(deb_path, - '/boot/vmlinux-3.16.0-6-loongson-2e') - - self.vm.set_console() - kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'console=ttyS0' - self.vm.add_args('-kernel', kernel_path, - '-append', kernel_command_line) - self.vm.launch() - console_pattern = 'Kernel command line: %s' % kernel_command_line - self.wait_for_console_pattern(console_pattern) - - def test_mips_malta_cpio(self): - """ - :avocado: tags=arch:mips - :avocado: tags=machine:malta - :avocado: tags=endian:big - """ - 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) - - self.vm.set_console() - kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE - + 'console=ttyS0 console=tty ' - + 'rdinit=/sbin/init noreboot') - self.vm.add_args('-kernel', kernel_path, - '-initrd', initrd_path, - '-append', kernel_command_line, - '-no-reboot') - self.vm.launch() - self.wait_for_console_pattern('Boot successful.') - - exec_command_and_wait_for_pattern(self, 'cat /proc/cpuinfo', - 'BogoMIPS') - exec_command_and_wait_for_pattern(self, 'uname -a', - 'Debian') - exec_command_and_wait_for_pattern(self, 'reboot', - 'reboot: Restarting system') - # Wait for VM to shut down gracefully - self.vm.wait() - - @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=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) - - self.vm.set_console() - kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE - + 'console=ttyS0 console=tty ' - + 'rdinit=/sbin/init noreboot') - self.vm.add_args('-kernel', kernel_path, - '-initrd', initrd_path, - '-append', kernel_command_line, - '-no-reboot') - self.vm.launch() - wait_for_console_pattern(self, 'Boot successful.') - - exec_command_and_wait_for_pattern(self, 'cat /proc/cpuinfo', - 'MIPS 5KE') - exec_command_and_wait_for_pattern(self, 'uname -a', - '3.19.3.mtoman.20150408') - exec_command_and_wait_for_pattern(self, 'reboot', - 'reboot: Restarting system') - # Wait for VM to shut down gracefully - self.vm.wait() - - 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) - - self.vm.set_console() - kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE - + 'mem=256m@@0x0 ' - + 'console=ttyS0') - self.vm.add_args('-no-reboot', - '-kernel', kernel_path, - '-append', kernel_command_line) - self.vm.launch() - console_pattern = 'Kernel command line: %s' % kernel_command_line - self.wait_for_console_pattern(console_pattern) - - 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) - - def test_aarch64_xlnx_versal_virt(self): - """ - :avocado: tags=arch:aarch64 - :avocado: tags=machine:xlnx-versal-virt - :avocado: tags=device:pl011 - :avocado: tags=device:arm_gicv3 - :avocado: tags=accel:tcg - """ - images_url = ('http://ports.ubuntu.com/ubuntu-ports/dists/' - 'bionic-updates/main/installer-arm64/' - '20101020ubuntu543.19/images/') - kernel_url = images_url + 'netboot/ubuntu-installer/arm64/linux' - kernel_hash = 'e167757620640eb26de0972f578741924abb3a82' - kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) - - initrd_url = images_url + 'netboot/ubuntu-installer/arm64/initrd.gz' - initrd_hash = 'cab5cb3fcefca8408aa5aae57f24574bfce8bdb9' - initrd_path = self.fetch_asset(initrd_url, asset_hash=initrd_hash) - - self.vm.set_console() - self.vm.add_args('-m', '2G', - '-accel', 'tcg', - '-kernel', kernel_path, - '-initrd', initrd_path) - self.vm.launch() - self.wait_for_console_pattern('Checked W+X mappings: passed') - - def test_arm_virt(self): - """ - :avocado: tags=arch:arm - :avocado: tags=machine:virt - :avocado: tags=accel:tcg - """ - kernel_url = ('https://archives.fedoraproject.org/pub/archive/fedora' - '/linux/releases/29/Everything/armhfp/os/images/pxeboot' - '/vmlinuz') - kernel_hash = 'e9826d741b4fb04cadba8d4824d1ed3b7fb8b4d4' - kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) - - self.vm.set_console() - kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + - 'console=ttyAMA0') - self.vm.add_args('-kernel', kernel_path, - '-append', kernel_command_line) - self.vm.launch() - console_pattern = 'Kernel command line: %s' % kernel_command_line - self.wait_for_console_pattern(console_pattern) - - def test_arm_emcraft_sf2(self): - """ - :avocado: tags=arch:arm - :avocado: tags=machine:emcraft-sf2 - :avocado: tags=endian:little - :avocado: tags=u-boot - :avocado: tags=accel:tcg - """ - self.require_netdev('user') - - uboot_url = ('https://raw.githubusercontent.com/' - 'Subbaraya-Sundeep/qemu-test-binaries/' - 'fe371d32e50ca682391e1e70ab98c2942aeffb01/u-boot') - uboot_hash = 'cbb8cbab970f594bf6523b9855be209c08374ae2' - uboot_path = self.fetch_asset(uboot_url, asset_hash=uboot_hash) - spi_url = ('https://raw.githubusercontent.com/' - 'Subbaraya-Sundeep/qemu-test-binaries/' - 'fe371d32e50ca682391e1e70ab98c2942aeffb01/spi.bin') - spi_hash = '65523a1835949b6f4553be96dec1b6a38fb05501' - spi_path = self.fetch_asset(spi_url, asset_hash=spi_hash) - spi_path_rw = os.path.join(self.workdir, os.path.basename(spi_path)) - shutil.copy(spi_path, spi_path_rw) - - file_truncate(spi_path_rw, 16 << 20) # Spansion S25FL128SDPBHICO is 16 MiB - - self.vm.set_console() - kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE - self.vm.add_args('-kernel', uboot_path, - '-append', kernel_command_line, - '-drive', 'file=' + spi_path_rw + ',if=mtd,format=raw', - '-no-reboot') - self.vm.launch() - self.wait_for_console_pattern('Enter \'help\' for a list') - - exec_command_and_wait_for_pattern(self, 'ifconfig eth0 10.0.2.15', - 'eth0: link becomes ready') - exec_command_and_wait_for_pattern(self, 'ping -c 3 10.0.2.2', - '3 packets transmitted, 3 packets received, 0% packet loss') - - def do_test_arm_raspi2(self, uart_id): - """ - :avocado: tags=accel:tcg - - The kernel can be rebuilt using the kernel source referenced - and following the instructions on the on: - https://www.raspberrypi.org/documentation/linux/kernel/building.md - """ - serial_kernel_cmdline = { - 0: 'earlycon=pl011,0x3f201000 console=ttyAMA0', - } - deb_url = ('http://archive.raspberrypi.org/debian/' - 'pool/main/r/raspberrypi-firmware/' - 'raspberrypi-kernel_1.20190215-1_armhf.deb') - deb_hash = 'cd284220b32128c5084037553db3c482426f3972' - deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash) - kernel_path = self.extract_from_deb(deb_path, '/boot/kernel7.img') - dtb_path = self.extract_from_deb(deb_path, '/boot/bcm2709-rpi-2-b.dtb') - - self.vm.set_console() - kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + - serial_kernel_cmdline[uart_id] + - ' root=/dev/mmcblk0p2 rootwait ' + - 'dwc_otg.fiq_fsm_enable=0') - self.vm.add_args('-kernel', kernel_path, - '-dtb', dtb_path, - '-append', kernel_command_line, - '-device', 'usb-kbd') - self.vm.launch() - console_pattern = 'Kernel command line: %s' % kernel_command_line - self.wait_for_console_pattern(console_pattern) - console_pattern = 'Product: QEMU USB Keyboard' - self.wait_for_console_pattern(console_pattern) - - def test_arm_raspi2_uart0(self): - """ - :avocado: tags=arch:arm - :avocado: tags=machine:raspi2b - :avocado: tags=device:pl011 - :avocado: tags=accel:tcg - """ - self.do_test_arm_raspi2(0) - - def test_arm_raspi2_initrd(self): - """ - :avocado: tags=arch:arm - :avocado: tags=machine:raspi2b - """ - deb_url = ('http://archive.raspberrypi.org/debian/' - 'pool/main/r/raspberrypi-firmware/' - 'raspberrypi-kernel_1.20190215-1_armhf.deb') - deb_hash = 'cd284220b32128c5084037553db3c482426f3972' - deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash) - kernel_path = self.extract_from_deb(deb_path, '/boot/kernel7.img') - dtb_path = self.extract_from_deb(deb_path, '/boot/bcm2709-rpi-2-b.dtb') - - initrd_url = ('https://github.com/groeck/linux-build-test/raw/' - '2eb0a73b5d5a28df3170c546ddaaa9757e1e0848/rootfs/' - 'arm/rootfs-armv7a.cpio.gz') - initrd_hash = '604b2e45cdf35045846b8bbfbf2129b1891bdc9c' - initrd_path_gz = self.fetch_asset(initrd_url, asset_hash=initrd_hash) - initrd_path = os.path.join(self.workdir, 'rootfs.cpio') - archive.gzip_uncompress(initrd_path_gz, initrd_path) - - self.vm.set_console() - kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + - 'earlycon=pl011,0x3f201000 console=ttyAMA0 ' - 'panic=-1 noreboot ' + - 'dwc_otg.fiq_fsm_enable=0') - self.vm.add_args('-kernel', kernel_path, - '-dtb', dtb_path, - '-initrd', initrd_path, - '-append', kernel_command_line, - '-no-reboot') - self.vm.launch() - self.wait_for_console_pattern('Boot successful.') - - exec_command_and_wait_for_pattern(self, 'cat /proc/cpuinfo', - 'BCM2835') - exec_command_and_wait_for_pattern(self, 'cat /proc/iomem', - '/soc/cprman@7e101000') - exec_command_and_wait_for_pattern(self, 'halt', 'reboot: System halted') - # Wait for VM to shut down gracefully - self.vm.wait() - - def test_arm_raspi4(self): - """ - :avocado: tags=arch:aarch64 - :avocado: tags=machine:raspi4b - :avocado: tags=device:pl011 - :avocado: tags=accel:tcg - :avocado: tags=rpi4b - - The kernel can be rebuilt using the kernel source referenced - and following the instructions on the on: - https://www.raspberrypi.org/documentation/linux/kernel/building.md - """ - - deb_url = ('http://archive.raspberrypi.org/debian/' - 'pool/main/r/raspberrypi-firmware/' - 'raspberrypi-kernel_1.20230106-1_arm64.deb') - deb_hash = '08dc55696535b18a6d4fe6fa10d4c0d905cbb2ed' - deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash) - kernel_path = self.extract_from_deb(deb_path, '/boot/kernel8.img') - dtb_path = self.extract_from_deb(deb_path, '/boot/bcm2711-rpi-4-b.dtb') - - self.vm.set_console() - kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + - 'earlycon=pl011,mmio32,0xfe201000 ' + - 'console=ttyAMA0,115200 ' + - 'root=/dev/mmcblk1p2 rootwait ' + - 'dwc_otg.fiq_fsm_enable=0') - self.vm.add_args('-kernel', kernel_path, - '-dtb', dtb_path, - '-append', kernel_command_line) - # When PCI is supported we can add a USB controller: - # '-device', 'qemu-xhci,bus=pcie.1,id=xhci', - # '-device', 'usb-kbd,bus=xhci.0', - self.vm.launch() - console_pattern = 'Kernel command line: %s' % kernel_command_line - self.wait_for_console_pattern(console_pattern) - # When USB is enabled we can look for this - # console_pattern = 'Product: QEMU USB Keyboard' - # self.wait_for_console_pattern(console_pattern) - console_pattern = 'Waiting for root device' - self.wait_for_console_pattern(console_pattern) - - - def test_arm_raspi4_initrd(self): - """ - :avocado: tags=arch:aarch64 - :avocado: tags=machine:raspi4b - :avocado: tags=device:pl011 - :avocado: tags=accel:tcg - :avocado: tags=rpi4b - - The kernel can be rebuilt using the kernel source referenced - and following the instructions on the on: - https://www.raspberrypi.org/documentation/linux/kernel/building.md - """ - deb_url = ('http://archive.raspberrypi.org/debian/' - 'pool/main/r/raspberrypi-firmware/' - 'raspberrypi-kernel_1.20230106-1_arm64.deb') - deb_hash = '08dc55696535b18a6d4fe6fa10d4c0d905cbb2ed' - deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash) - kernel_path = self.extract_from_deb(deb_path, '/boot/kernel8.img') - dtb_path = self.extract_from_deb(deb_path, '/boot/bcm2711-rpi-4-b.dtb') - - initrd_url = ('https://github.com/groeck/linux-build-test/raw/' - '86b2be1384d41c8c388e63078a847f1e1c4cb1de/rootfs/' - 'arm64/rootfs.cpio.gz') - initrd_hash = 'f3d4f9fa92a49aa542f1b44d34be77bbf8ca5b9d' - initrd_path_gz = self.fetch_asset(initrd_url, asset_hash=initrd_hash) - initrd_path = os.path.join(self.workdir, 'rootfs.cpio') - archive.gzip_uncompress(initrd_path_gz, initrd_path) - - self.vm.set_console() - kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + - 'earlycon=pl011,mmio32,0xfe201000 ' + - 'console=ttyAMA0,115200 ' + - 'panic=-1 noreboot ' + - 'dwc_otg.fiq_fsm_enable=0') - self.vm.add_args('-kernel', kernel_path, - '-dtb', dtb_path, - '-initrd', initrd_path, - '-append', kernel_command_line, - '-no-reboot') - # When PCI is supported we can add a USB controller: - # '-device', 'qemu-xhci,bus=pcie.1,id=xhci', - # '-device', 'usb-kbd,bus=xhci.0', - self.vm.launch() - self.wait_for_console_pattern('Boot successful.') - - exec_command_and_wait_for_pattern(self, 'cat /proc/cpuinfo', - 'BCM2835') - exec_command_and_wait_for_pattern(self, 'cat /proc/iomem', - 'cprman@7e101000') - exec_command_and_wait_for_pattern(self, 'halt', 'reboot: System halted') - # TODO: Raspberry Pi4 doesn't shut down properly with recent kernels - # Wait for VM to shut down gracefully - #self.vm.wait() - - def test_arm_exynos4210_initrd(self): - """ - :avocado: tags=arch:arm - :avocado: tags=machine:smdkc210 - :avocado: tags=accel:tcg - """ - deb_url = ('https://snapshot.debian.org/archive/debian/' - '20190928T224601Z/pool/main/l/linux/' - 'linux-image-4.19.0-6-armmp_4.19.67-2+deb10u1_armhf.deb') - deb_hash = 'fa9df4a0d38936cb50084838f2cb933f570d7d82' - deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash) - kernel_path = self.extract_from_deb(deb_path, - '/boot/vmlinuz-4.19.0-6-armmp') - dtb_path = '/usr/lib/linux-image-4.19.0-6-armmp/exynos4210-smdkv310.dtb' - dtb_path = self.extract_from_deb(deb_path, dtb_path) - - initrd_url = ('https://github.com/groeck/linux-build-test/raw/' - '2eb0a73b5d5a28df3170c546ddaaa9757e1e0848/rootfs/' - 'arm/rootfs-armv5.cpio.gz') - initrd_hash = '2b50f1873e113523967806f4da2afe385462ff9b' - initrd_path_gz = self.fetch_asset(initrd_url, asset_hash=initrd_hash) - initrd_path = os.path.join(self.workdir, 'rootfs.cpio') - archive.gzip_uncompress(initrd_path_gz, initrd_path) - - self.vm.set_console() - kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + - 'earlycon=exynos4210,0x13800000 earlyprintk ' + - 'console=ttySAC0,115200n8 ' + - 'random.trust_cpu=off cryptomgr.notests ' + - 'cpuidle.off=1 panic=-1 noreboot') - - self.vm.add_args('-kernel', kernel_path, - '-dtb', dtb_path, - '-initrd', initrd_path, - '-append', kernel_command_line, - '-no-reboot') - self.vm.launch() - - self.wait_for_console_pattern('Boot successful.') - # TODO user command, for now the uart is stuck - - def test_arm_cubieboard_initrd(self): - """ - :avocado: tags=arch:arm - :avocado: tags=machine:cubieboard - :avocado: tags=accel:tcg - """ - deb_url = ('https://apt.armbian.com/pool/main/l/' - 'linux-6.6.16/linux-image-current-sunxi_24.2.1_armhf__6.6.16-Seb3e-D6b4a-P2359-Ce96bHfe66-HK01ba-V014b-B067e-R448a.deb') - deb_hash = 'f7c3c8c5432f765445dc6e7eab02f3bbe668256b' - deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash) - kernel_path = self.extract_from_deb(deb_path, - '/boot/vmlinuz-6.6.16-current-sunxi') - dtb_path = '/usr/lib/linux-image-6.6.16-current-sunxi/sun4i-a10-cubieboard.dtb' - dtb_path = self.extract_from_deb(deb_path, dtb_path) - initrd_url = ('https://github.com/groeck/linux-build-test/raw/' - '2eb0a73b5d5a28df3170c546ddaaa9757e1e0848/rootfs/' - 'arm/rootfs-armv5.cpio.gz') - initrd_hash = '2b50f1873e113523967806f4da2afe385462ff9b' - initrd_path_gz = self.fetch_asset(initrd_url, asset_hash=initrd_hash) - initrd_path = os.path.join(self.workdir, 'rootfs.cpio') - archive.gzip_uncompress(initrd_path_gz, initrd_path) - - self.vm.set_console() - kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + - 'console=ttyS0,115200 ' - 'usbcore.nousb ' - 'panic=-1 noreboot') - self.vm.add_args('-kernel', kernel_path, - '-dtb', dtb_path, - '-initrd', initrd_path, - '-append', kernel_command_line, - '-no-reboot') - self.vm.launch() - self.wait_for_console_pattern('Boot successful.') - - exec_command_and_wait_for_pattern(self, 'cat /proc/cpuinfo', - 'Allwinner sun4i/sun5i') - exec_command_and_wait_for_pattern(self, 'cat /proc/iomem', - 'system-control@1c00000') - exec_command_and_wait_for_pattern(self, 'reboot', - 'reboot: Restarting system') - # Wait for VM to shut down gracefully - self.vm.wait() - - def test_arm_cubieboard_sata(self): - """ - :avocado: tags=arch:arm - :avocado: tags=machine:cubieboard - :avocado: tags=accel:tcg - """ - deb_url = ('https://apt.armbian.com/pool/main/l/' - 'linux-6.6.16/linux-image-current-sunxi_24.2.1_armhf__6.6.16-Seb3e-D6b4a-P2359-Ce96bHfe66-HK01ba-V014b-B067e-R448a.deb') - deb_hash = 'f7c3c8c5432f765445dc6e7eab02f3bbe668256b' - deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash) - kernel_path = self.extract_from_deb(deb_path, - '/boot/vmlinuz-6.6.16-current-sunxi') - dtb_path = '/usr/lib/linux-image-6.6.16-current-sunxi/sun4i-a10-cubieboard.dtb' - dtb_path = self.extract_from_deb(deb_path, dtb_path) - rootfs_url = ('https://github.com/groeck/linux-build-test/raw/' - '2eb0a73b5d5a28df3170c546ddaaa9757e1e0848/rootfs/' - 'arm/rootfs-armv5.ext2.gz') - rootfs_hash = '093e89d2b4d982234bf528bc9fb2f2f17a9d1f93' - rootfs_path_gz = self.fetch_asset(rootfs_url, asset_hash=rootfs_hash) - rootfs_path = os.path.join(self.workdir, 'rootfs.cpio') - archive.gzip_uncompress(rootfs_path_gz, rootfs_path) - - self.vm.set_console() - kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + - 'console=ttyS0,115200 ' - 'usbcore.nousb ' - 'root=/dev/sda ro ' - 'panic=-1 noreboot') - self.vm.add_args('-kernel', kernel_path, - '-dtb', dtb_path, - '-drive', 'if=none,format=raw,id=disk0,file=' - + rootfs_path, - '-device', 'ide-hd,bus=ide.0,drive=disk0', - '-append', kernel_command_line, - '-no-reboot') - self.vm.launch() - self.wait_for_console_pattern('Boot successful.') - - exec_command_and_wait_for_pattern(self, 'cat /proc/cpuinfo', - 'Allwinner sun4i/sun5i') - exec_command_and_wait_for_pattern(self, 'cat /proc/partitions', - 'sda') - exec_command_and_wait_for_pattern(self, 'reboot', - 'reboot: Restarting system') - # Wait for VM to shut down gracefully - self.vm.wait() - - @skipUnless(os.getenv('AVOCADO_ALLOW_LARGE_STORAGE'), 'storage limited') - def test_arm_cubieboard_openwrt_22_03_2(self): - """ - :avocado: tags=arch:arm - :avocado: tags=machine:cubieboard - :avocado: tags=device:sd - """ - - # This test download a 7.5 MiB compressed image and expand it - # to 126 MiB. - image_url = ('https://downloads.openwrt.org/releases/22.03.2/targets/' - 'sunxi/cortexa8/openwrt-22.03.2-sunxi-cortexa8-' - 'cubietech_a10-cubieboard-ext4-sdcard.img.gz') - image_hash = ('94b5ecbfbc0b3b56276e5146b899eafa' - '2ac5dc2d08733d6705af9f144f39f554') - image_path_gz = self.fetch_asset(image_url, asset_hash=image_hash, - algorithm='sha256') - image_path = archive.extract(image_path_gz, self.workdir) - image_pow2ceil_expand(image_path) - - self.vm.set_console() - self.vm.add_args('-drive', 'file=' + image_path + ',if=sd,format=raw', - '-nic', 'user', - '-no-reboot') - self.vm.launch() - - kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + - 'usbcore.nousb ' - 'noreboot') - - self.wait_for_console_pattern('U-Boot SPL') - - interrupt_interactive_console_until_pattern( - self, 'Hit any key to stop autoboot:', '=>') - exec_command_and_wait_for_pattern(self, "setenv extraargs '" + - kernel_command_line + "'", '=>') - exec_command_and_wait_for_pattern(self, 'boot', 'Starting kernel ...'); - - self.wait_for_console_pattern( - 'Please press Enter to activate this console.') - - exec_command_and_wait_for_pattern(self, ' ', 'root@') - - exec_command_and_wait_for_pattern(self, 'cat /proc/cpuinfo', - 'Allwinner sun4i/sun5i') - exec_command_and_wait_for_pattern(self, 'reboot', - 'reboot: Restarting system') - # Wait for VM to shut down gracefully - self.vm.wait() - - @skipUnless(os.getenv('AVOCADO_TIMEOUT_EXPECTED'), 'Test might timeout') - def test_arm_quanta_gsj(self): - """ - :avocado: tags=arch:arm - :avocado: tags=machine:quanta-gsj - :avocado: tags=accel:tcg - """ - # 25 MiB compressed, 32 MiB uncompressed. - image_url = ( - 'https://github.com/hskinnemoen/openbmc/releases/download/' - '20200711-gsj-qemu-0/obmc-phosphor-image-gsj.static.mtd.gz') - image_hash = '14895e634923345cb5c8776037ff7876df96f6b1' - image_path_gz = self.fetch_asset(image_url, asset_hash=image_hash) - image_name = 'obmc.mtd' - image_path = os.path.join(self.workdir, image_name) - archive.gzip_uncompress(image_path_gz, image_path) - - self.vm.set_console() - drive_args = 'file=' + image_path + ',if=mtd,bus=0,unit=0' - self.vm.add_args('-drive', drive_args) - self.vm.launch() - - # Disable drivers and services that stall for a long time during boot, - # to avoid running past the 90-second timeout. These may be removed - # as the corresponding device support is added. - kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + ( - 'console=${console} ' - 'mem=${mem} ' - 'initcall_blacklist=npcm_i2c_bus_driver_init ' - 'systemd.mask=systemd-random-seed.service ' - 'systemd.mask=dropbearkey.service ' - ) - - self.wait_for_console_pattern('> BootBlock by Nuvoton') - self.wait_for_console_pattern('>Device: Poleg BMC NPCM730') - self.wait_for_console_pattern('>Skip DDR init.') - self.wait_for_console_pattern('U-Boot ') - interrupt_interactive_console_until_pattern( - self, 'Hit any key to stop autoboot:', 'U-Boot>') - exec_command_and_wait_for_pattern( - self, "setenv bootargs ${bootargs} " + kernel_command_line, - 'U-Boot>') - exec_command_and_wait_for_pattern( - self, 'run romboot', 'Booting Kernel from flash') - self.wait_for_console_pattern('Booting Linux on physical CPU 0x0') - self.wait_for_console_pattern('CPU1: thread -1, cpu 1, socket 0') - self.wait_for_console_pattern('OpenBMC Project Reference Distro') - self.wait_for_console_pattern('gsj login:') - - def test_arm_quanta_gsj_initrd(self): - """ - :avocado: tags=arch:arm - :avocado: tags=machine:quanta-gsj - :avocado: tags=accel:tcg - """ - initrd_url = ( - 'https://github.com/hskinnemoen/openbmc/releases/download/' - '20200711-gsj-qemu-0/obmc-phosphor-initramfs-gsj.cpio.xz') - initrd_hash = '98fefe5d7e56727b1eb17d5c00311b1b5c945300' - initrd_path = self.fetch_asset(initrd_url, asset_hash=initrd_hash) - kernel_url = ( - 'https://github.com/hskinnemoen/openbmc/releases/download/' - '20200711-gsj-qemu-0/uImage-gsj.bin') - kernel_hash = 'fa67b2f141d56d39b3c54305c0e8a899c99eb2c7' - kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) - dtb_url = ( - 'https://github.com/hskinnemoen/openbmc/releases/download/' - '20200711-gsj-qemu-0/nuvoton-npcm730-gsj.dtb') - dtb_hash = '18315f7006d7b688d8312d5c727eecd819aa36a4' - dtb_path = self.fetch_asset(dtb_url, asset_hash=dtb_hash) - - self.vm.set_console() - kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + - 'console=ttyS0,115200n8 ' - 'earlycon=uart8250,mmio32,0xf0001000') - self.vm.add_args('-kernel', kernel_path, - '-initrd', initrd_path, - '-dtb', dtb_path, - '-append', kernel_command_line) - self.vm.launch() - - self.wait_for_console_pattern('Booting Linux on physical CPU 0x0') - self.wait_for_console_pattern('CPU1: thread -1, cpu 1, socket 0') - self.wait_for_console_pattern( - 'Give root password for system maintenance') - - def test_arm_bpim2u(self): - """ - :avocado: tags=arch:arm - :avocado: tags=machine:bpim2u - :avocado: tags=accel:tcg - """ - deb_url = ('https://apt.armbian.com/pool/main/l/' - 'linux-6.6.16/linux-image-current-sunxi_24.2.1_armhf__6.6.16-Seb3e-D6b4a-P2359-Ce96bHfe66-HK01ba-V014b-B067e-R448a.deb') - deb_hash = 'f7c3c8c5432f765445dc6e7eab02f3bbe668256b' - deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash) - kernel_path = self.extract_from_deb(deb_path, - '/boot/vmlinuz-6.6.16-current-sunxi') - dtb_path = ('/usr/lib/linux-image-6.6.16-current-sunxi/' - 'sun8i-r40-bananapi-m2-ultra.dtb') - dtb_path = self.extract_from_deb(deb_path, dtb_path) - - self.vm.set_console() - kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + - 'console=ttyS0,115200n8 ' - 'earlycon=uart,mmio32,0x1c28000') - self.vm.add_args('-kernel', kernel_path, - '-dtb', dtb_path, - '-append', kernel_command_line) - self.vm.launch() - console_pattern = 'Kernel command line: %s' % kernel_command_line - self.wait_for_console_pattern(console_pattern) - - def test_arm_bpim2u_initrd(self): - """ - :avocado: tags=arch:arm - :avocado: tags=accel:tcg - :avocado: tags=machine:bpim2u - """ - deb_url = ('https://apt.armbian.com/pool/main/l/' - 'linux-6.6.16/linux-image-current-sunxi_24.2.1_armhf__6.6.16-Seb3e-D6b4a-P2359-Ce96bHfe66-HK01ba-V014b-B067e-R448a.deb') - deb_hash = 'f7c3c8c5432f765445dc6e7eab02f3bbe668256b' - deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash) - kernel_path = self.extract_from_deb(deb_path, - '/boot/vmlinuz-6.6.16-current-sunxi') - dtb_path = ('/usr/lib/linux-image-6.6.16-current-sunxi/' - 'sun8i-r40-bananapi-m2-ultra.dtb') - dtb_path = self.extract_from_deb(deb_path, dtb_path) - initrd_url = ('https://github.com/groeck/linux-build-test/raw/' - '2eb0a73b5d5a28df3170c546ddaaa9757e1e0848/rootfs/' - 'arm/rootfs-armv7a.cpio.gz') - initrd_hash = '604b2e45cdf35045846b8bbfbf2129b1891bdc9c' - initrd_path_gz = self.fetch_asset(initrd_url, asset_hash=initrd_hash) - initrd_path = os.path.join(self.workdir, 'rootfs.cpio') - archive.gzip_uncompress(initrd_path_gz, initrd_path) - - self.vm.set_console() - kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + - 'console=ttyS0,115200 ' - 'panic=-1 noreboot') - self.vm.add_args('-kernel', kernel_path, - '-dtb', dtb_path, - '-initrd', initrd_path, - '-append', kernel_command_line, - '-no-reboot') - self.vm.launch() - self.wait_for_console_pattern('Boot successful.') - - exec_command_and_wait_for_pattern(self, 'cat /proc/cpuinfo', - 'Allwinner sun8i Family') - exec_command_and_wait_for_pattern(self, 'cat /proc/iomem', - 'system-control@1c00000') - exec_command_and_wait_for_pattern(self, 'reboot', - 'reboot: Restarting system') - # Wait for VM to shut down gracefully - self.vm.wait() - - def test_arm_bpim2u_gmac(self): - """ - :avocado: tags=arch:arm - :avocado: tags=accel:tcg - :avocado: tags=machine:bpim2u - :avocado: tags=device:sd - """ - self.require_netdev('user') - - deb_url = ('https://apt.armbian.com/pool/main/l/' - 'linux-6.6.16/linux-image-current-sunxi_24.2.1_armhf__6.6.16-Seb3e-D6b4a-P2359-Ce96bHfe66-HK01ba-V014b-B067e-R448a.deb') - deb_hash = 'f7c3c8c5432f765445dc6e7eab02f3bbe668256b' - deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash) - kernel_path = self.extract_from_deb(deb_path, - '/boot/vmlinuz-6.6.16-current-sunxi') - dtb_path = ('/usr/lib/linux-image-6.6.16-current-sunxi/' - 'sun8i-r40-bananapi-m2-ultra.dtb') - dtb_path = self.extract_from_deb(deb_path, dtb_path) - rootfs_url = ('http://storage.kernelci.org/images/rootfs/buildroot/' - 'buildroot-baseline/20221116.0/armel/rootfs.ext2.xz') - rootfs_hash = 'fae32f337c7b87547b10f42599acf109da8b6d9a' - rootfs_path_xz = self.fetch_asset(rootfs_url, asset_hash=rootfs_hash) - rootfs_path = os.path.join(self.workdir, 'rootfs.cpio') - archive.lzma_uncompress(rootfs_path_xz, rootfs_path) - image_pow2ceil_expand(rootfs_path) - - self.vm.set_console() - kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + - 'console=ttyS0,115200 ' - 'root=b300 rootwait rw ' - 'panic=-1 noreboot') - self.vm.add_args('-kernel', kernel_path, - '-dtb', dtb_path, - '-drive', 'file=' + rootfs_path + ',if=sd,format=raw', - '-net', 'nic,model=gmac,netdev=host_gmac', - '-netdev', 'user,id=host_gmac', - '-append', kernel_command_line, - '-no-reboot') - self.vm.launch() - shell_ready = "/bin/sh: can't access tty; job control turned off" - self.wait_for_console_pattern(shell_ready) - - exec_command_and_wait_for_pattern(self, 'cat /proc/cpuinfo', - 'Allwinner sun8i Family') - exec_command_and_wait_for_pattern(self, 'cat /proc/partitions', - 'mmcblk') - exec_command_and_wait_for_pattern(self, 'ifconfig eth0 up', - 'eth0: Link is Up') - exec_command_and_wait_for_pattern(self, 'udhcpc eth0', - 'udhcpc: lease of 10.0.2.15 obtained') - exec_command_and_wait_for_pattern(self, 'ping -c 3 10.0.2.2', - '3 packets transmitted, 3 packets received, 0% packet loss') - exec_command_and_wait_for_pattern(self, 'reboot', - 'reboot: Restarting system') - # Wait for VM to shut down gracefully - self.vm.wait() - - @skipUnless(os.getenv('AVOCADO_ALLOW_LARGE_STORAGE'), 'storage limited') - def test_arm_bpim2u_openwrt_22_03_3(self): - """ - :avocado: tags=arch:arm - :avocado: tags=machine:bpim2u - :avocado: tags=device:sd - """ - - # This test download a 8.9 MiB compressed image and expand it - # to 127 MiB. - image_url = ('https://downloads.openwrt.org/releases/22.03.3/targets/' - 'sunxi/cortexa7/openwrt-22.03.3-sunxi-cortexa7-' - 'sinovoip_bananapi-m2-ultra-ext4-sdcard.img.gz') - image_hash = ('5b41b4e11423e562c6011640f9a7cd3b' - 'dd0a3d42b83430f7caa70a432e6cd82c') - image_path_gz = self.fetch_asset(image_url, asset_hash=image_hash, - algorithm='sha256') - image_path = archive.extract(image_path_gz, self.workdir) - image_pow2ceil_expand(image_path) - - self.vm.set_console() - self.vm.add_args('-drive', 'file=' + image_path + ',if=sd,format=raw', - '-nic', 'user', - '-no-reboot') - self.vm.launch() - - kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + - 'usbcore.nousb ' - 'noreboot') - - self.wait_for_console_pattern('U-Boot SPL') - - interrupt_interactive_console_until_pattern( - self, 'Hit any key to stop autoboot:', '=>') - exec_command_and_wait_for_pattern(self, "setenv extraargs '" + - kernel_command_line + "'", '=>') - exec_command_and_wait_for_pattern(self, 'boot', 'Starting kernel ...'); - - self.wait_for_console_pattern( - 'Please press Enter to activate this console.') - - exec_command_and_wait_for_pattern(self, ' ', 'root@') - - exec_command_and_wait_for_pattern(self, 'cat /proc/cpuinfo', - 'Allwinner sun8i Family') - exec_command_and_wait_for_pattern(self, 'cat /proc/iomem', - 'system-control@1c00000') - - def test_arm_orangepi(self): - """ - :avocado: tags=arch:arm - :avocado: tags=machine:orangepi-pc - :avocado: tags=accel:tcg - """ - deb_url = ('https://apt.armbian.com/pool/main/l/' - 'linux-6.6.16/linux-image-current-sunxi_24.2.1_armhf__6.6.16-Seb3e-D6b4a-P2359-Ce96bHfe66-HK01ba-V014b-B067e-R448a.deb') - deb_hash = 'f7c3c8c5432f765445dc6e7eab02f3bbe668256b' - deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash) - kernel_path = self.extract_from_deb(deb_path, - '/boot/vmlinuz-6.6.16-current-sunxi') - dtb_path = '/usr/lib/linux-image-6.6.16-current-sunxi/sun8i-h3-orangepi-pc.dtb' - dtb_path = self.extract_from_deb(deb_path, dtb_path) - - self.vm.set_console() - kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + - 'console=ttyS0,115200n8 ' - 'earlycon=uart,mmio32,0x1c28000') - self.vm.add_args('-kernel', kernel_path, - '-dtb', dtb_path, - '-append', kernel_command_line) - self.vm.launch() - console_pattern = 'Kernel command line: %s' % kernel_command_line - self.wait_for_console_pattern(console_pattern) - - def test_arm_orangepi_initrd(self): - """ - :avocado: tags=arch:arm - :avocado: tags=accel:tcg - :avocado: tags=machine:orangepi-pc - """ - deb_url = ('https://apt.armbian.com/pool/main/l/' - 'linux-6.6.16/linux-image-current-sunxi_24.2.1_armhf__6.6.16-Seb3e-D6b4a-P2359-Ce96bHfe66-HK01ba-V014b-B067e-R448a.deb') - deb_hash = 'f7c3c8c5432f765445dc6e7eab02f3bbe668256b' - deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash) - kernel_path = self.extract_from_deb(deb_path, - '/boot/vmlinuz-6.6.16-current-sunxi') - dtb_path = '/usr/lib/linux-image-6.6.16-current-sunxi/sun8i-h3-orangepi-pc.dtb' - dtb_path = self.extract_from_deb(deb_path, dtb_path) - initrd_url = ('https://github.com/groeck/linux-build-test/raw/' - '2eb0a73b5d5a28df3170c546ddaaa9757e1e0848/rootfs/' - 'arm/rootfs-armv7a.cpio.gz') - initrd_hash = '604b2e45cdf35045846b8bbfbf2129b1891bdc9c' - initrd_path_gz = self.fetch_asset(initrd_url, asset_hash=initrd_hash) - initrd_path = os.path.join(self.workdir, 'rootfs.cpio') - archive.gzip_uncompress(initrd_path_gz, initrd_path) - - self.vm.set_console() - kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + - 'console=ttyS0,115200 ' - 'panic=-1 noreboot') - self.vm.add_args('-kernel', kernel_path, - '-dtb', dtb_path, - '-initrd', initrd_path, - '-append', kernel_command_line, - '-no-reboot') - self.vm.launch() - self.wait_for_console_pattern('Boot successful.') - - exec_command_and_wait_for_pattern(self, 'cat /proc/cpuinfo', - 'Allwinner sun8i Family') - exec_command_and_wait_for_pattern(self, 'cat /proc/iomem', - 'system-control@1c00000') - exec_command_and_wait_for_pattern(self, 'reboot', - 'reboot: Restarting system') - # Wait for VM to shut down gracefully - self.vm.wait() - - def test_arm_orangepi_sd(self): - """ - :avocado: tags=arch:arm - :avocado: tags=accel:tcg - :avocado: tags=machine:orangepi-pc - :avocado: tags=device:sd - """ - self.require_netdev('user') - - deb_url = ('https://apt.armbian.com/pool/main/l/' - 'linux-6.6.16/linux-image-current-sunxi_24.2.1_armhf__6.6.16-Seb3e-D6b4a-P2359-Ce96bHfe66-HK01ba-V014b-B067e-R448a.deb') - deb_hash = 'f7c3c8c5432f765445dc6e7eab02f3bbe668256b' - deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash) - kernel_path = self.extract_from_deb(deb_path, - '/boot/vmlinuz-6.6.16-current-sunxi') - dtb_path = '/usr/lib/linux-image-6.6.16-current-sunxi/sun8i-h3-orangepi-pc.dtb' - dtb_path = self.extract_from_deb(deb_path, dtb_path) - rootfs_url = ('http://storage.kernelci.org/images/rootfs/buildroot/' - 'buildroot-baseline/20221116.0/armel/rootfs.ext2.xz') - rootfs_hash = 'fae32f337c7b87547b10f42599acf109da8b6d9a' - rootfs_path_xz = self.fetch_asset(rootfs_url, asset_hash=rootfs_hash) - rootfs_path = os.path.join(self.workdir, 'rootfs.cpio') - archive.lzma_uncompress(rootfs_path_xz, rootfs_path) - image_pow2ceil_expand(rootfs_path) - - self.vm.set_console() - kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + - 'console=ttyS0,115200 ' - 'root=/dev/mmcblk0 rootwait rw ' - 'panic=-1 noreboot') - self.vm.add_args('-kernel', kernel_path, - '-dtb', dtb_path, - '-drive', 'file=' + rootfs_path + ',if=sd,format=raw', - '-append', kernel_command_line, - '-no-reboot') - self.vm.launch() - shell_ready = "/bin/sh: can't access tty; job control turned off" - self.wait_for_console_pattern(shell_ready) - - exec_command_and_wait_for_pattern(self, 'cat /proc/cpuinfo', - 'Allwinner sun8i Family') - exec_command_and_wait_for_pattern(self, 'cat /proc/partitions', - 'mmcblk0') - exec_command_and_wait_for_pattern(self, 'ifconfig eth0 up', - 'eth0: Link is Up') - exec_command_and_wait_for_pattern(self, 'udhcpc eth0', - 'udhcpc: lease of 10.0.2.15 obtained') - exec_command_and_wait_for_pattern(self, 'ping -c 3 10.0.2.2', - '3 packets transmitted, 3 packets received, 0% packet loss') - exec_command_and_wait_for_pattern(self, 'reboot', - 'reboot: Restarting system') - # Wait for VM to shut down gracefully - self.vm.wait() - - @skipUnless(os.getenv('AVOCADO_ALLOW_LARGE_STORAGE'), 'storage limited') - def test_arm_orangepi_bionic_20_08(self): - """ - :avocado: tags=arch:arm - :avocado: tags=machine:orangepi-pc - :avocado: tags=device:sd - """ - - # This test download a 275 MiB compressed image and expand it - # to 1036 MiB, but the underlying filesystem is 1552 MiB... - # As we expand it to 2 GiB we are safe. - - image_url = ('https://archive.armbian.com/orangepipc/archive/' - 'Armbian_20.08.1_Orangepipc_bionic_current_5.8.5.img.xz') - image_hash = ('b4d6775f5673486329e45a0586bf06b6' - 'dbe792199fd182ac6b9c7bb6c7d3e6dd') - image_path_xz = self.fetch_asset(image_url, asset_hash=image_hash, - algorithm='sha256') - image_path = archive.extract(image_path_xz, self.workdir) - image_pow2ceil_expand(image_path) - - self.vm.set_console() - self.vm.add_args('-drive', 'file=' + image_path + ',if=sd,format=raw', - '-nic', 'user', - '-no-reboot') - self.vm.launch() - - kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + - 'console=ttyS0,115200 ' - 'loglevel=7 ' - 'nosmp ' - 'systemd.default_timeout_start_sec=9000 ' - 'systemd.mask=armbian-zram-config.service ' - 'systemd.mask=armbian-ramlog.service') - - self.wait_for_console_pattern('U-Boot SPL') - self.wait_for_console_pattern('Autoboot in ') - exec_command_and_wait_for_pattern(self, ' ', '=>') - exec_command_and_wait_for_pattern(self, "setenv extraargs '" + - kernel_command_line + "'", '=>') - exec_command_and_wait_for_pattern(self, 'boot', 'Starting kernel ...'); - - self.wait_for_console_pattern('systemd[1]: Set hostname ' + - 'to <orangepipc>') - self.wait_for_console_pattern('Starting Load Kernel Modules...') - - @skipUnless(os.getenv('AVOCADO_ALLOW_LARGE_STORAGE'), 'storage limited') - def test_arm_orangepi_uboot_netbsd9(self): - """ - :avocado: tags=arch:arm - :avocado: tags=machine:orangepi-pc - :avocado: tags=device:sd - :avocado: tags=os:netbsd - """ - # This test download a 304MB compressed image and expand it to 2GB - deb_url = ('http://snapshot.debian.org/archive/debian/' - '20200108T145233Z/pool/main/u/u-boot/' - 'u-boot-sunxi_2020.01%2Bdfsg-1_armhf.deb') - deb_hash = 'f67f404a80753ca3d1258f13e38f2b060e13db99' - deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash) - # We use the common OrangePi PC 'plus' build of U-Boot for our secondary - # program loader (SPL). We will then set the path to the more specific - # OrangePi "PC" device tree blob with 'setenv fdtfile' in U-Boot prompt, - # before to boot NetBSD. - uboot_path = '/usr/lib/u-boot/orangepi_plus/u-boot-sunxi-with-spl.bin' - uboot_path = self.extract_from_deb(deb_path, uboot_path) - image_url = ('https://cdn.netbsd.org/pub/NetBSD/NetBSD-9.0/' - 'evbarm-earmv7hf/binary/gzimg/armv7.img.gz') - image_hash = '2babb29d36d8360adcb39c09e31060945259917a' - image_path_gz = self.fetch_asset(image_url, asset_hash=image_hash) - image_path = os.path.join(self.workdir, 'armv7.img') - archive.gzip_uncompress(image_path_gz, image_path) - image_pow2ceil_expand(image_path) - image_drive_args = 'if=sd,format=raw,snapshot=on,file=' + image_path - - # dd if=u-boot-sunxi-with-spl.bin of=armv7.img bs=1K seek=8 conv=notrunc - with open(uboot_path, 'rb') as f_in: - with open(image_path, 'r+b') as f_out: - f_out.seek(8 * 1024) - shutil.copyfileobj(f_in, f_out) - - self.vm.set_console() - self.vm.add_args('-nic', 'user', - '-drive', image_drive_args, - '-global', 'allwinner-rtc.base-year=2000', - '-no-reboot') - self.vm.launch() - wait_for_console_pattern(self, 'U-Boot 2020.01+dfsg-1') - interrupt_interactive_console_until_pattern(self, - 'Hit any key to stop autoboot:', - 'switch to partitions #0, OK') - - exec_command_and_wait_for_pattern(self, '', '=>') - cmd = 'setenv bootargs root=ld0a' - exec_command_and_wait_for_pattern(self, cmd, '=>') - cmd = 'setenv kernel netbsd-GENERIC.ub' - exec_command_and_wait_for_pattern(self, cmd, '=>') - cmd = 'setenv fdtfile dtb/sun8i-h3-orangepi-pc.dtb' - exec_command_and_wait_for_pattern(self, cmd, '=>') - cmd = ("setenv bootcmd 'fatload mmc 0:1 ${kernel_addr_r} ${kernel}; " - "fatload mmc 0:1 ${fdt_addr_r} ${fdtfile}; " - "fdt addr ${fdt_addr_r}; " - "bootm ${kernel_addr_r} - ${fdt_addr_r}'") - exec_command_and_wait_for_pattern(self, cmd, '=>') - - exec_command_and_wait_for_pattern(self, 'boot', - 'Booting kernel from Legacy Image') - wait_for_console_pattern(self, 'Starting kernel ...') - wait_for_console_pattern(self, 'NetBSD 9.0 (GENERIC)') - # Wait for user-space - wait_for_console_pattern(self, 'Starting root file system check') - - def test_aarch64_raspi3_atf(self): - """ - :avocado: tags=accel:tcg - :avocado: tags=arch:aarch64 - :avocado: tags=machine:raspi3b - :avocado: tags=cpu:cortex-a53 - :avocado: tags=device:pl011 - :avocado: tags=atf - """ - zip_url = ('https://github.com/pbatard/RPi3/releases/download/' - 'v1.15/RPi3_UEFI_Firmware_v1.15.zip') - zip_hash = '74b3bd0de92683cadb14e008a7575e1d0c3cafb9' - zip_path = self.fetch_asset(zip_url, asset_hash=zip_hash) - - archive.extract(zip_path, self.workdir) - efi_fd = os.path.join(self.workdir, 'RPI_EFI.fd') - - self.vm.set_console(console_index=1) - self.vm.add_args('-nodefaults', - '-device', 'loader,file=%s,force-raw=true' % efi_fd) - self.vm.launch() - self.wait_for_console_pattern('version UEFI Firmware v1.15') - - def test_alpha_clipper(self): - """ - :avocado: tags=arch:alpha - :avocado: tags=machine:clipper - """ - kernel_url = ('http://archive.debian.org/debian/dists/lenny/main/' - 'installer-alpha/20090123lenny10/images/cdrom/vmlinuz') - kernel_hash = '3a943149335529e2ed3e74d0d787b85fb5671ba3' - kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) - - uncompressed_kernel = archive.uncompress(kernel_path, self.workdir) - - self.vm.set_console() - kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'console=ttyS0' - self.vm.add_args('-nodefaults', - '-kernel', uncompressed_kernel, - '-append', kernel_command_line) - self.vm.launch() - console_pattern = 'Kernel command line: %s' % kernel_command_line - self.wait_for_console_pattern(console_pattern) - - def test_m68k_q800(self): - """ - :avocado: tags=arch:m68k - :avocado: tags=machine:q800 - """ - deb_url = ('https://snapshot.debian.org/archive/debian-ports' - '/20191021T083923Z/pool-m68k/main' - '/l/linux/kernel-image-5.3.0-1-m68k-di_5.3.7-1_m68k.udeb') - deb_hash = '044954bb9be4160a3ce81f8bc1b5e856b75cccd1' - deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash) - kernel_path = self.extract_from_deb(deb_path, - '/boot/vmlinux-5.3.0-1-m68k') - - self.vm.set_console() - kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + - 'console=ttyS0 vga=off') - self.vm.add_args('-kernel', kernel_path, - '-append', kernel_command_line) - self.vm.launch() - console_pattern = 'Kernel command line: %s' % kernel_command_line - self.wait_for_console_pattern(console_pattern) - console_pattern = 'No filesystem could mount root' - self.wait_for_console_pattern(console_pattern) - - def do_test_advcal_2018(self, day, tar_hash, kernel_name, console=0): - tar_url = ('https://qemu-advcal.gitlab.io' - '/qac-best-of-multiarch/download/day' + day + '.tar.xz') - file_path = self.fetch_asset(tar_url, asset_hash=tar_hash) - archive.extract(file_path, self.workdir) - self.vm.set_console(console_index=console) - self.vm.add_args('-kernel', - self.workdir + '/day' + day + '/' + kernel_name) - self.vm.launch() - self.wait_for_console_pattern('QEMU advent calendar') - - def test_arm_vexpressa9(self): - """ - :avocado: tags=arch:arm - :avocado: tags=machine:vexpress-a9 - """ - tar_hash = '32b7677ce8b6f1471fb0059865f451169934245b' - self.vm.add_args('-dtb', self.workdir + '/day16/vexpress-v2p-ca9.dtb') - self.do_test_advcal_2018('16', tar_hash, 'winter.zImage') - - def test_arm_ast2600_debian(self): - """ - :avocado: tags=arch:arm - :avocado: tags=machine:rainier-bmc - """ - deb_url = ('http://snapshot.debian.org/archive/debian/' - '20220606T211338Z/' - 'pool/main/l/linux/' - 'linux-image-5.17.0-2-armmp_5.17.6-1%2Bb1_armhf.deb') - deb_hash = '8acb2b4439faedc2f3ed4bdb2847ad4f6e0491f73debaeb7f660c8abe4dcdc0e' - deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash, - algorithm='sha256') - kernel_path = self.extract_from_deb(deb_path, '/boot/vmlinuz-5.17.0-2-armmp') - dtb_path = self.extract_from_deb(deb_path, - '/usr/lib/linux-image-5.17.0-2-armmp/aspeed-bmc-ibm-rainier.dtb') - - self.vm.set_console() - self.vm.add_args('-kernel', kernel_path, - '-dtb', dtb_path, - '-net', 'nic') - self.vm.launch() - self.wait_for_console_pattern("Booting Linux on physical CPU 0xf00") - self.wait_for_console_pattern("SMP: Total of 2 processors activated") - self.wait_for_console_pattern("No filesystem could mount root") - - def test_m68k_mcf5208evb(self): - """ - :avocado: tags=arch:m68k - :avocado: tags=machine:mcf5208evb - """ - tar_hash = 'ac688fd00561a2b6ce1359f9ff6aa2b98c9a570c' - self.do_test_advcal_2018('07', tar_hash, 'sanity-clause.elf') - - def test_or1k_sim(self): - """ - :avocado: tags=arch:or1k - :avocado: tags=machine:or1k-sim - """ - tar_hash = '20334cdaf386108c530ff0badaecc955693027dd' - self.do_test_advcal_2018('20', tar_hash, 'vmlinux') - - def test_ppc64_e500(self): - """ - :avocado: tags=arch:ppc64 - :avocado: tags=machine:ppce500 - :avocado: tags=cpu:e5500 - :avocado: tags=accel:tcg - """ - self.require_accelerator("tcg") - tar_hash = '6951d86d644b302898da2fd701739c9406527fe1' - self.do_test_advcal_2018('19', tar_hash, 'uImage') - - def do_test_ppc64_powernv(self, proc): - self.require_accelerator("tcg") - images_url = ('https://github.com/open-power/op-build/releases/download/v2.7/') - - kernel_url = images_url + 'zImage.epapr' - kernel_hash = '0ab237df661727e5392cee97460e8674057a883c5f74381a128fa772588d45cd' - kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash, - algorithm='sha256') - self.vm.set_console() - self.vm.add_args('-kernel', kernel_path, - '-append', 'console=tty0 console=hvc0', - '-device', 'pcie-pci-bridge,id=bridge1,bus=pcie.1,addr=0x0', - '-device', 'nvme,bus=pcie.2,addr=0x0,serial=1234', - '-device', 'e1000e,bus=bridge1,addr=0x3', - '-device', 'nec-usb-xhci,bus=bridge1,addr=0x2') - self.vm.launch() - - self.wait_for_console_pattern("CPU: " + proc + " generation processor") - self.wait_for_console_pattern("zImage starting: loaded") - self.wait_for_console_pattern("Run /init as init process") - # Device detection output driven by udev probing is sometimes cut off - # from console output, suspect S14silence-console init script. - - def test_ppc_powernv8(self): - """ - :avocado: tags=arch:ppc64 - :avocado: tags=machine:powernv8 - :avocado: tags=accel:tcg - """ - self.do_test_ppc64_powernv('P8') - - def test_ppc_powernv9(self): - """ - :avocado: tags=arch:ppc64 - :avocado: tags=machine:powernv9 - :avocado: tags=accel:tcg - """ - self.do_test_ppc64_powernv('P9') - - def test_ppc_powernv10(self): - """ - :avocado: tags=arch:ppc64 - :avocado: tags=machine:powernv10 - :avocado: tags=accel:tcg - """ - self.do_test_ppc64_powernv('P10') - - def test_ppc_g3beige(self): - """ - :avocado: tags=arch:ppc - :avocado: tags=machine:g3beige - :avocado: tags=accel:tcg - """ - # TODO: g3beige works with kvm_pr but we don't have a - # reliable way ATM (e.g. looking at /proc/modules) to detect - # whether we're running kvm_hv or kvm_pr. For now let's - # disable this test if we don't have TCG support. - self.require_accelerator("tcg") - tar_hash = 'e0b872a5eb8fdc5bed19bd43ffe863900ebcedfc' - self.vm.add_args('-M', 'graphics=off') - self.do_test_advcal_2018('15', tar_hash, 'invaders.elf') - - def test_ppc_mac99(self): - """ - :avocado: tags=arch:ppc - :avocado: tags=machine:mac99 - :avocado: tags=accel:tcg - """ - # TODO: mac99 works with kvm_pr but we don't have a - # reliable way ATM (e.g. looking at /proc/modules) to detect - # whether we're running kvm_hv or kvm_pr. For now let's - # disable this test if we don't have TCG support. - self.require_accelerator("tcg") - tar_hash = 'e0b872a5eb8fdc5bed19bd43ffe863900ebcedfc' - self.vm.add_args('-M', 'graphics=off') - self.do_test_advcal_2018('15', tar_hash, 'invaders.elf') - - # This test has a 6-10% failure rate on various hosts that look - # like issues with a buggy kernel. As a result we don't want it - # gating releases on Gitlab. - @skipUnless(os.getenv('QEMU_TEST_FLAKY_TESTS'), 'Test is unstable on GitLab') - def test_sh4_r2d(self): - """ - :avocado: tags=arch:sh4 - :avocado: tags=machine:r2d - :avocado: tags=flaky - """ - tar_hash = 'fe06a4fd8ccbf2e27928d64472939d47829d4c7e' - self.vm.add_args('-append', 'console=ttySC1') - self.do_test_advcal_2018('09', tar_hash, 'zImage', console=1) - - def test_sparc_ss20(self): - """ - :avocado: tags=arch:sparc - :avocado: tags=machine:SS-20 - """ - tar_hash = 'b18550d5d61c7615d989a06edace051017726a9f' - self.do_test_advcal_2018('11', tar_hash, 'zImage.elf') - - def test_xtensa_lx60(self): - """ - :avocado: tags=arch:xtensa - :avocado: tags=machine:lx60 - :avocado: tags=cpu:dc233c - """ - tar_hash = '49e88d9933742f0164b60839886c9739cb7a0d34' - self.do_test_advcal_2018('02', tar_hash, 'santas-sleigh-ride.elf') diff --git a/tests/avocado/boot_xen.py b/tests/avocado/boot_xen.py deleted file mode 100644 index 490a127..0000000 --- a/tests/avocado/boot_xen.py +++ /dev/null @@ -1,95 +0,0 @@ -# Functional test that boots a Xen hypervisor with a domU kernel and -# checks the console output is vaguely sane . -# -# Copyright (c) 2020 Linaro -# -# Author: -# Alex Bennée <alex.bennee@linaro.org> -# -# SPDX-License-Identifier: GPL-2.0-or-later -# -# 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 - -from avocado_qemu import wait_for_console_pattern -from boot_linux_console import LinuxKernelTest - - -class BootXen(LinuxKernelTest): - """ - Boots a Xen hypervisor with a Linux DomU kernel. - - :avocado: tags=arch:aarch64 - :avocado: tags=accel:tcg - :avocado: tags=cpu:cortex-a57 - :avocado: tags=machine:virt - """ - - timeout = 90 - XEN_COMMON_COMMAND_LINE = 'dom0_mem=128M loglvl=all guest_loglvl=all' - - def setUp(self): - super(BootXen, self).setUp() - - # Using my own built kernel - which works - kernel_url = ('https://fileserver.linaro.org/' - 's/JSsewXGZ6mqxPr5/download?path=%2F&files=' - 'linux-5.9.9-arm64-ajb') - kernel_sha1 = '4f92bc4b9f88d5ab792fa7a43a68555d344e1b83' - self.kernel_path = self.fetch_asset(kernel_url, - asset_hash=kernel_sha1) - - def launch_xen(self, xen_path): - """ - Launch Xen with a dom0 guest kernel - """ - self.log.info("launch with xen_path: %s", xen_path) - - self.vm.set_console() - - self.vm.add_args('-machine', 'virtualization=on', - '-m', '768', - '-kernel', xen_path, - '-append', self.XEN_COMMON_COMMAND_LINE, - '-device', - 'guest-loader,addr=0x47000000,kernel=%s,bootargs=console=hvc0' - % (self.kernel_path)) - - self.vm.launch() - - console_pattern = 'VFS: Cannot open root device' - wait_for_console_pattern(self, console_pattern, "Panic on CPU 0:") - - def test_arm64_xen_411_and_dom0(self): - # archive of file from https://deb.debian.org/debian/pool/main/x/xen/ - xen_url = ('https://fileserver.linaro.org/s/JSsewXGZ6mqxPr5/' - 'download?path=%2F&files=' - 'xen-hypervisor-4.11-arm64_4.11.4%2B37-g3263f257ca-1_arm64.deb') - xen_sha1 = '034e634d4416adbad1212d59b62bccdcda63e62a' - xen_deb = self.fetch_asset(xen_url, asset_hash=xen_sha1) - xen_path = self.extract_from_deb(xen_deb, "/boot/xen-4.11-arm64") - - self.launch_xen(xen_path) - - def test_arm64_xen_414_and_dom0(self): - # archive of file from https://deb.debian.org/debian/pool/main/x/xen/ - xen_url = ('https://fileserver.linaro.org/s/JSsewXGZ6mqxPr5/' - 'download?path=%2F&files=' - 'xen-hypervisor-4.14-arm64_4.14.0%2B80-gd101b417b7-1_arm64.deb') - xen_sha1 = 'b9d209dd689ed2b393e625303a225badefec1160' - xen_deb = self.fetch_asset(xen_url, asset_hash=xen_sha1) - xen_path = self.extract_from_deb(xen_deb, "/boot/xen-4.14-arm64") - - self.launch_xen(xen_path) - - def test_arm64_xen_415_and_dom0(self): - xen_url = ('https://fileserver.linaro.org/' - 's/JSsewXGZ6mqxPr5/download' - '?path=%2F&files=xen-upstream-4.15-unstable.deb') - xen_sha1 = 'fc191172b85cf355abb95d275a24cc0f6d6579d8' - xen_deb = self.fetch_asset(xen_url, asset_hash=xen_sha1) - xen_path = self.extract_from_deb(xen_deb, "/boot/xen-4.15-unstable") - - self.launch_xen(xen_path) diff --git a/tests/avocado/hotplug_blk.py b/tests/avocado/hotplug_blk.py deleted file mode 100644 index d55ded1..0000000 --- a/tests/avocado/hotplug_blk.py +++ /dev/null @@ -1,69 +0,0 @@ -# Functional test that hotplugs a virtio blk disk and checks it on a Linux -# guest -# -# Copyright (c) 2021 Red Hat, Inc. -# Copyright (c) Yandex -# -# 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 time - -from avocado_qemu.linuxtest import LinuxTest - - -class HotPlug(LinuxTest): - def blockdev_add(self) -> None: - self.vm.cmd('blockdev-add', **{ - 'driver': 'null-co', - 'size': 1073741824, - 'node-name': 'disk' - }) - - def assert_vda(self) -> None: - self.ssh_command('test -e /sys/block/vda') - - def assert_no_vda(self) -> None: - with self.assertRaises(AssertionError): - self.assert_vda() - - def plug(self) -> None: - args = { - 'driver': 'virtio-blk-pci', - 'drive': 'disk', - 'id': 'virtio-disk0', - 'bus': 'pci.1', - 'addr': 1 - } - - self.assert_no_vda() - self.vm.cmd('device_add', args) - try: - self.assert_vda() - except AssertionError: - time.sleep(1) - self.assert_vda() - - def unplug(self) -> None: - self.vm.cmd('device_del', id='virtio-disk0') - - self.vm.event_wait('DEVICE_DELETED', 1.0, - match={'data': {'device': 'virtio-disk0'}}) - - self.assert_no_vda() - - def test(self) -> None: - """ - :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('-device', 'pcie-pci-bridge,id=pci.1,bus=pcie.0') - - self.launch_and_wait() - self.blockdev_add() - - self.plug() - self.unplug() diff --git a/tests/avocado/hotplug_cpu.py b/tests/avocado/hotplug_cpu.py deleted file mode 100644 index 342c838..0000000 --- a/tests/avocado/hotplug_cpu.py +++ /dev/null @@ -1,37 +0,0 @@ -# Functional test that hotplugs a CPU and checks it on a Linux guest -# -# Copyright (c) 2021 Red Hat, Inc. -# -# Author: -# Cleber Rosa <crosa@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. - -from avocado_qemu.linuxtest import LinuxTest - - -class HotPlugCPU(LinuxTest): - - 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.ssh_command('test -e /sys/devices/system/cpu/cpu0') - with self.assertRaises(AssertionError): - self.ssh_command('test -e /sys/devices/system/cpu/cpu1') - - self.vm.cmd('device_add', - driver='Haswell-x86_64-cpu', - socket_id=0, - core_id=1, - thread_id=0) - self.ssh_command('test -e /sys/devices/system/cpu/cpu1') diff --git a/tests/avocado/intel_iommu.py b/tests/avocado/intel_iommu.py deleted file mode 100644 index 992583f..0000000 --- a/tests/avocado/intel_iommu.py +++ /dev/null @@ -1,122 +0,0 @@ -# INTEL_IOMMU Functional tests -# -# Copyright (c) 2021 Red Hat, Inc. -# -# Author: -# Eric Auger <eric.auger@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 os - -from avocado import skipUnless -from avocado_qemu.linuxtest import LinuxTest - -@skipUnless(os.getenv('QEMU_TEST_FLAKY_TESTS'), 'Test is unstable on GitLab') -class IntelIOMMU(LinuxTest): - """ - :avocado: tags=arch:x86_64 - :avocado: tags=distro:fedora - :avocado: tags=distro_version:31 - :avocado: tags=machine:q35 - :avocado: tags=accel:kvm - :avocado: tags=intel_iommu - :avocado: tags=flaky - """ - - IOMMU_ADDON = ',iommu_platform=on,disable-modern=off,disable-legacy=on' - kernel_path = None - initrd_path = None - kernel_params = None - - def set_up_boot(self): - path = self.download_boot() - self.vm.add_args('-device', 'virtio-blk-pci,bus=pcie.0,' + - 'drive=drv0,id=virtio-disk0,bootindex=1,' - 'werror=stop,rerror=stop' + self.IOMMU_ADDON) - self.vm.add_args('-device', 'virtio-gpu-pci' + self.IOMMU_ADDON) - self.vm.add_args('-drive', - 'file=%s,if=none,cache=writethrough,id=drv0' % path) - - def setUp(self): - super(IntelIOMMU, self).setUp(None, 'virtio-net-pci' + self.IOMMU_ADDON) - - def add_common_args(self): - self.vm.add_args('-device', 'virtio-rng-pci,rng=rng0') - self.vm.add_args('-object', - 'rng-random,id=rng0,filename=/dev/urandom') - - def common_vm_setup(self, custom_kernel=None): - self.require_accelerator("kvm") - self.add_common_args() - self.vm.add_args("-accel", "kvm") - - if custom_kernel is None: - return - - kernel_url = self.distro.pxeboot_url + 'vmlinuz' - kernel_hash = '5b6f6876e1b5bda314f93893271da0d5777b1f3c' - initrd_url = self.distro.pxeboot_url + 'initrd.img' - initrd_hash = 'dd0340a1b39bd28f88532babd4581c67649ec5b1' - self.kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) - self.initrd_path = self.fetch_asset(initrd_url, asset_hash=initrd_hash) - - def run_and_check(self): - if self.kernel_path: - self.vm.add_args('-kernel', self.kernel_path, - '-append', self.kernel_params, - '-initrd', self.initrd_path) - self.launch_and_wait() - self.ssh_command('cat /proc/cmdline') - self.ssh_command('dmesg | grep -e DMAR -e IOMMU') - self.ssh_command('find /sys/kernel/iommu_groups/ -type l') - self.ssh_command('dnf -y install numactl-devel') - - def test_intel_iommu(self): - """ - :avocado: tags=intel_iommu_intremap - """ - - self.common_vm_setup(True) - self.vm.add_args('-device', 'intel-iommu,intremap=on') - self.vm.add_args('-machine', 'kernel_irqchip=split') - - self.kernel_params = (self.distro.default_kernel_params + - ' quiet intel_iommu=on') - self.run_and_check() - - def test_intel_iommu_strict(self): - """ - :avocado: tags=intel_iommu_strict - """ - - self.common_vm_setup(True) - self.vm.add_args('-device', 'intel-iommu,intremap=on') - self.vm.add_args('-machine', 'kernel_irqchip=split') - self.kernel_params = (self.distro.default_kernel_params + - ' quiet intel_iommu=on,strict') - self.run_and_check() - - def test_intel_iommu_strict_cm(self): - """ - :avocado: tags=intel_iommu_strict_cm - """ - - self.common_vm_setup(True) - self.vm.add_args('-device', 'intel-iommu,intremap=on,caching-mode=on') - self.vm.add_args('-machine', 'kernel_irqchip=split') - self.kernel_params = (self.distro.default_kernel_params + - ' quiet intel_iommu=on,strict') - self.run_and_check() - - def test_intel_iommu_pt(self): - """ - :avocado: tags=intel_iommu_pt - """ - - self.common_vm_setup(True) - self.vm.add_args('-device', 'intel-iommu,intremap=on') - self.vm.add_args('-machine', 'kernel_irqchip=split') - self.kernel_params = (self.distro.default_kernel_params + - ' quiet intel_iommu=on iommu=pt') - self.run_and_check() diff --git a/tests/avocado/linux_ssh_mips_malta.py b/tests/avocado/linux_ssh_mips_malta.py deleted file mode 100644 index d9bb525..0000000 --- a/tests/avocado/linux_ssh_mips_malta.py +++ /dev/null @@ -1,205 +0,0 @@ -# Functional test that boots a VM and run commands via a SSH session -# -# Copyright (c) Philippe Mathieu-Daudé <f4bug@amsat.org> -# -# 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 re -import base64 -import logging -import time - -from avocado import skipUnless -from avocado_qemu import LinuxSSHMixIn -from avocado_qemu import QemuSystemTest -from avocado_qemu import wait_for_console_pattern -from avocado.utils import process -from avocado.utils import archive -from avocado.utils import ssh - - -@skipUnless(os.getenv('AVOCADO_TIMEOUT_EXPECTED'), 'Test might timeout') -@skipUnless(ssh.SSH_CLIENT_BINARY, 'No SSH client available') -class LinuxSSH(QemuSystemTest, LinuxSSHMixIn): - """ - :avocado: tags=accel:tcg - """ - - timeout = 150 # Not for 'configure --enable-debug --enable-debug-tcg' - - KERNEL_COMMON_COMMAND_LINE = 'printk.time=0 ' - VM_IP = '127.0.0.1' - - BASE_URL = 'https://people.debian.org/~aurel32/qemu/' - IMAGE_INFO = { - 'be': {'base_url': 'mips', - 'image_name': 'debian_wheezy_mips_standard.qcow2', - 'image_hash': '8987a63270df67345b2135a6b7a4885a35e392d5', - 'kernel_hash': { - 32: '592e384a4edc16dade52a6cd5c785c637bcbc9ad', - 64: 'db6eea7de35d36c77d8c165b6bcb222e16eb91db'} - }, - 'le': {'base_url': 'mipsel', - 'image_name': 'debian_wheezy_mipsel_standard.qcow2', - 'image_hash': '7866764d9de3ef536ffca24c9fb9f04ffdb45802', - 'kernel_hash': { - 32: 'a66bea5a8adaa2cb3d36a1d4e0ccdb01be8f6c2a', - 64: '6a7f77245acf231415a0e8b725d91ed2f3487794'} - } - } - CPU_INFO = { - 32: {'cpu': 'MIPS 24Kc', 'kernel_release': '3.2.0-4-4kc-malta'}, - 64: {'cpu': 'MIPS 20Kc', 'kernel_release': '3.2.0-4-5kc-malta'} - } - - def get_url(self, endianess, path=''): - qkey = {'le': 'el', 'be': ''} - return '%s/mips%s/%s' % (self.BASE_URL, qkey[endianess], path) - - def get_image_info(self, endianess): - dinfo = self.IMAGE_INFO[endianess] - image_url = self.get_url(endianess, dinfo['image_name']) - image_hash = dinfo['image_hash'] - return (image_url, image_hash) - - def get_kernel_info(self, endianess, wordsize): - minfo = self.CPU_INFO[wordsize] - kernel_url = self.get_url(endianess, - 'vmlinux-%s' % minfo['kernel_release']) - kernel_hash = self.IMAGE_INFO[endianess]['kernel_hash'][wordsize] - return kernel_url, kernel_hash - - def ssh_disconnect_vm(self): - self.ssh_session.quit() - - def boot_debian_wheezy_image_and_ssh_login(self, endianess, kernel_path): - image_url, image_hash = self.get_image_info(endianess) - image_path = self.fetch_asset(image_url, asset_hash=image_hash) - - self.vm.set_console() - kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE - + 'console=ttyS0 root=/dev/sda1') - self.vm.add_args('-no-reboot', - '-kernel', kernel_path, - '-append', kernel_command_line, - '-drive', 'file=%s,snapshot=on' % image_path, - '-netdev', 'user,id=vnet,hostfwd=:127.0.0.1:0-:22', - '-device', 'pcnet,netdev=vnet') - self.vm.launch() - - self.log.info('VM launched, waiting for sshd') - console_pattern = 'Starting OpenBSD Secure Shell server: sshd' - wait_for_console_pattern(self, console_pattern, 'Oops') - self.log.info('sshd ready') - - self.ssh_connect('root', 'root', False) - - def shutdown_via_ssh(self): - self.ssh_command('poweroff') - self.ssh_disconnect_vm() - wait_for_console_pattern(self, 'Power down', 'Oops') - - def run_common_commands(self, wordsize): - self.ssh_command_output_contains( - 'cat /proc/cpuinfo', - self.CPU_INFO[wordsize]['cpu']) - self.ssh_command_output_contains( - 'uname -m', - 'mips') - self.ssh_command_output_contains( - 'uname -r', - self.CPU_INFO[wordsize]['kernel_release']) - self.ssh_command_output_contains( - 'cat /proc/interrupts', - 'XT-PIC timer') - self.ssh_command_output_contains( - 'cat /proc/interrupts', - 'XT-PIC i8042') - self.ssh_command_output_contains( - 'cat /proc/interrupts', - 'XT-PIC serial') - self.ssh_command_output_contains( - 'cat /proc/interrupts', - 'XT-PIC ata_piix') - self.ssh_command_output_contains( - 'cat /proc/interrupts', - 'XT-PIC eth0') - self.ssh_command_output_contains( - 'cat /proc/devices', - 'input') - self.ssh_command_output_contains( - 'cat /proc/devices', - 'usb') - self.ssh_command_output_contains( - 'cat /proc/devices', - 'fb') - self.ssh_command_output_contains( - 'cat /proc/ioports', - ' : serial') - self.ssh_command_output_contains( - 'cat /proc/ioports', - ' : ata_piix') - self.ssh_command_output_contains( - 'cat /proc/ioports', - ' : piix4_smbus') - self.ssh_command_output_contains( - 'lspci -d 11ab:4620', - 'GT-64120') - self.ssh_command_output_contains( - 'cat /sys/bus/i2c/devices/i2c-0/name', - 'SMBus PIIX4 adapter') - self.ssh_command_output_contains( - 'cat /proc/mtd', - 'YAMON') - # Empty 'Board Config' (64KB) - self.ssh_command_output_contains( - 'md5sum /dev/mtd2ro', - '0dfbe8aa4c20b52e1b8bf3cb6cbdf193') - - def check_mips_malta(self, uname_m, endianess): - wordsize = 64 if '64' in uname_m else 32 - kernel_url, kernel_hash = self.get_kernel_info(endianess, wordsize) - kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) - self.boot_debian_wheezy_image_and_ssh_login(endianess, kernel_path) - - stdout, _ = self.ssh_command('uname -a') - self.assertIn(True, [uname_m + " GNU/Linux" in line for line in stdout]) - - self.run_common_commands(wordsize) - self.shutdown_via_ssh() - # Wait for VM to shut down gracefully - self.vm.wait() - - def test_mips_malta32eb_kernel3_2_0(self): - """ - :avocado: tags=arch:mips - :avocado: tags=endian:big - :avocado: tags=device:pcnet32 - """ - self.check_mips_malta('mips', 'be') - - def test_mips_malta32el_kernel3_2_0(self): - """ - :avocado: tags=arch:mipsel - :avocado: tags=endian:little - :avocado: tags=device:pcnet32 - """ - self.check_mips_malta('mips', 'le') - - def test_mips_malta64eb_kernel3_2_0(self): - """ - :avocado: tags=arch:mips64 - :avocado: tags=endian:big - :avocado: tags=device:pcnet32 - """ - self.check_mips_malta('mips64', 'be') - - def test_mips_malta64el_kernel3_2_0(self): - """ - :avocado: tags=arch:mips64el - :avocado: tags=endian:little - :avocado: tags=device:pcnet32 - """ - self.check_mips_malta('mips64', 'le') diff --git a/tests/avocado/machine_arm_n8x0.py b/tests/avocado/machine_arm_n8x0.py deleted file mode 100755 index 12e9a68..0000000 --- a/tests/avocado/machine_arm_n8x0.py +++ /dev/null @@ -1,49 +0,0 @@ -# Functional test that boots a Linux kernel and checks the console -# -# Copyright (c) 2020 Red Hat, Inc. -# -# Author: -# Thomas Huth <thuth@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 os - -from avocado import skipUnless -from avocado_qemu import QemuSystemTest -from avocado_qemu import wait_for_console_pattern - -class N8x0Machine(QemuSystemTest): - """Boots the Linux kernel and checks that the console is operational""" - - timeout = 90 - - def __do_test_n8x0(self): - kernel_url = ('http://stskeeps.subnetmask.net/meego-n8x0/' - 'meego-arm-n8x0-1.0.80.20100712.1431-' - 'vmlinuz-2.6.35~rc4-129.1-n8x0') - kernel_hash = 'e9d5ab8d7548923a0061b6fbf601465e479ed269' - kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) - - self.vm.set_console(console_index=1) - self.vm.add_args('-kernel', kernel_path, - '-append', 'printk.time=0 console=ttyS1') - self.vm.launch() - wait_for_console_pattern(self, 'TSC2005 driver initializing') - - @skipUnless(os.getenv('AVOCADO_ALLOW_UNTRUSTED_CODE'), 'untrusted code') - def test_n800(self): - """ - :avocado: tags=arch:arm - :avocado: tags=machine:n800 - """ - self.__do_test_n8x0() - - @skipUnless(os.getenv('AVOCADO_ALLOW_UNTRUSTED_CODE'), 'untrusted code') - def test_n810(self): - """ - :avocado: tags=arch:arm - :avocado: tags=machine:n810 - """ - self.__do_test_n8x0() diff --git a/tests/avocado/machine_aspeed.py b/tests/avocado/machine_aspeed.py deleted file mode 100644 index c0b01e8..0000000 --- a/tests/avocado/machine_aspeed.py +++ /dev/null @@ -1,478 +0,0 @@ -# Functional test that boots the ASPEED SoCs with firmware -# -# Copyright (C) 2022 ASPEED Technology Inc -# -# 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 time -import os -import tempfile -import subprocess - -from avocado_qemu import LinuxSSHMixIn -from avocado_qemu import QemuSystemTest -from avocado_qemu import wait_for_console_pattern -from avocado_qemu import exec_command -from avocado_qemu import exec_command_and_wait_for_pattern -from avocado_qemu import interrupt_interactive_console_until_pattern -from avocado_qemu import has_cmd -from avocado.utils import archive -from avocado import skipUnless -from avocado import skipUnless - - -class AST1030Machine(QemuSystemTest): - """Boots the zephyr os and checks that the console is operational""" - - timeout = 10 - - def test_ast1030_zephyros_1_04(self): - """ - :avocado: tags=arch:arm - :avocado: tags=machine:ast1030-evb - :avocado: tags=os:zephyr - """ - tar_url = ('https://github.com/AspeedTech-BMC' - '/zephyr/releases/download/v00.01.04/ast1030-evb-demo.zip') - tar_hash = '4c6a8ce3a8ba76ef1a65dae419ae3409343c4b20' - tar_path = self.fetch_asset(tar_url, asset_hash=tar_hash) - archive.extract(tar_path, self.workdir) - kernel_file = self.workdir + "/ast1030-evb-demo/zephyr.elf" - self.vm.set_console() - self.vm.add_args('-kernel', kernel_file, - '-nographic') - self.vm.launch() - wait_for_console_pattern(self, "Booting Zephyr OS") - exec_command_and_wait_for_pattern(self, "help", - "Available commands") - - def test_ast1030_zephyros_1_07(self): - """ - :avocado: tags=arch:arm - :avocado: tags=machine:ast1030-evb - :avocado: tags=os:zephyr - """ - tar_url = ('https://github.com/AspeedTech-BMC' - '/zephyr/releases/download/v00.01.07/ast1030-evb-demo.zip') - tar_hash = '40ac87eabdcd3b3454ce5aad11fedc72a33ecda2' - tar_path = self.fetch_asset(tar_url, asset_hash=tar_hash) - archive.extract(tar_path, self.workdir) - kernel_file = self.workdir + "/ast1030-evb-demo/zephyr.bin" - self.vm.set_console() - self.vm.add_args('-kernel', kernel_file, - '-nographic') - self.vm.launch() - wait_for_console_pattern(self, "Booting Zephyr OS") - for shell_cmd in [ - 'kernel stacks', - 'otp info conf', - 'otp info scu', - 'hwinfo devid', - 'crypto aes256_cbc_vault', - 'random get', - 'jtag JTAG1 sw_xfer high TMS', - 'adc ADC0 resolution 12', - 'adc ADC0 read 42', - 'adc ADC1 read 69', - 'i2c scan I2C_0', - 'i3c attach I3C_0', - 'hash test', - 'kernel uptime', - 'kernel reboot warm', - 'kernel uptime', - 'kernel reboot cold', - 'kernel uptime', - ]: exec_command_and_wait_for_pattern(self, shell_cmd, "uart:~$") - -class AST2x00Machine(QemuSystemTest): - - timeout = 180 - - def wait_for_console_pattern(self, success_message, vm=None): - wait_for_console_pattern(self, success_message, - failure_message='Kernel panic - not syncing', - vm=vm) - - def do_test_arm_aspeed(self, image): - self.vm.set_console() - self.vm.add_args('-drive', 'file=' + image + ',if=mtd,format=raw', - '-net', 'nic') - self.vm.launch() - - self.wait_for_console_pattern("U-Boot 2016.07") - self.wait_for_console_pattern("## Loading kernel from FIT Image at 20080000") - self.wait_for_console_pattern("Starting kernel ...") - self.wait_for_console_pattern("Booting Linux on physical CPU 0x0") - wait_for_console_pattern(self, - "aspeed-smc 1e620000.spi: read control register: 203b0641") - self.wait_for_console_pattern("ftgmac100 1e660000.ethernet eth0: irq ") - self.wait_for_console_pattern("systemd[1]: Set hostname to") - - def test_arm_ast2400_palmetto_openbmc_v2_9_0(self): - """ - :avocado: tags=arch:arm - :avocado: tags=machine:palmetto-bmc - """ - - image_url = ('https://github.com/openbmc/openbmc/releases/download/2.9.0/' - 'obmc-phosphor-image-palmetto.static.mtd') - image_hash = ('3e13bbbc28e424865dc42f35ad672b10f2e82cdb11846bb28fa625b48beafd0d') - image_path = self.fetch_asset(image_url, asset_hash=image_hash, - algorithm='sha256') - - self.do_test_arm_aspeed(image_path) - - def test_arm_ast2500_romulus_openbmc_v2_9_0(self): - """ - :avocado: tags=arch:arm - :avocado: tags=machine:romulus-bmc - """ - - image_url = ('https://github.com/openbmc/openbmc/releases/download/2.9.0/' - 'obmc-phosphor-image-romulus.static.mtd') - image_hash = ('820341076803f1955bc31e647a512c79f9add4f5233d0697678bab4604c7bb25') - image_path = self.fetch_asset(image_url, asset_hash=image_hash, - algorithm='sha256') - - self.do_test_arm_aspeed(image_path) - - def do_test_arm_aspeed_buildroot_start(self, image, cpu_id, pattern='Aspeed EVB'): - self.require_netdev('user') - - self.vm.set_console() - self.vm.add_args('-drive', 'file=' + image + ',if=mtd,format=raw', - '-net', 'nic', '-net', 'user') - self.vm.launch() - - self.wait_for_console_pattern('U-Boot 2019.04') - self.wait_for_console_pattern('## Loading kernel from FIT Image') - self.wait_for_console_pattern('Starting kernel ...') - self.wait_for_console_pattern('Booting Linux on physical CPU ' + cpu_id) - self.wait_for_console_pattern('lease of 10.0.2.15') - # the line before login: - self.wait_for_console_pattern(pattern) - time.sleep(0.1) - exec_command(self, 'root') - time.sleep(0.1) - exec_command(self, "passw0rd") - - def do_test_arm_aspeed_buildroot_poweroff(self): - exec_command_and_wait_for_pattern(self, 'poweroff', - 'reboot: System halted'); - - def test_arm_ast2500_evb_buildroot(self): - """ - :avocado: tags=arch:arm - :avocado: tags=machine:ast2500-evb - """ - - image_url = ('https://github.com/legoater/qemu-aspeed-boot/raw/master/' - 'images/ast2500-evb/buildroot-2023.11/flash.img') - image_hash = ('c23db6160cf77d0258397eb2051162c8473a56c441417c52a91ba217186e715f') - image_path = self.fetch_asset(image_url, asset_hash=image_hash, - algorithm='sha256') - - self.vm.add_args('-device', - 'tmp105,bus=aspeed.i2c.bus.3,address=0x4d,id=tmp-test'); - self.do_test_arm_aspeed_buildroot_start(image_path, '0x0', 'Aspeed AST2500 EVB') - - exec_command_and_wait_for_pattern(self, - 'echo lm75 0x4d > /sys/class/i2c-dev/i2c-3/device/new_device', - 'i2c i2c-3: new_device: Instantiated device lm75 at 0x4d'); - exec_command_and_wait_for_pattern(self, - 'cat /sys/class/hwmon/hwmon1/temp1_input', '0') - self.vm.cmd('qom-set', path='/machine/peripheral/tmp-test', - property='temperature', value=18000); - exec_command_and_wait_for_pattern(self, - 'cat /sys/class/hwmon/hwmon1/temp1_input', '18000') - - self.do_test_arm_aspeed_buildroot_poweroff() - - def test_arm_ast2600_evb_buildroot(self): - """ - :avocado: tags=arch:arm - :avocado: tags=machine:ast2600-evb - """ - - image_url = ('https://github.com/legoater/qemu-aspeed-boot/raw/master/' - 'images/ast2600-evb/buildroot-2023.11/flash.img') - image_hash = ('b62808daef48b438d0728ee07662290490ecfa65987bb91294cafb1bb7ad1a68') - image_path = self.fetch_asset(image_url, asset_hash=image_hash, - algorithm='sha256') - - self.vm.add_args('-device', - 'tmp105,bus=aspeed.i2c.bus.3,address=0x4d,id=tmp-test'); - self.vm.add_args('-device', - 'ds1338,bus=aspeed.i2c.bus.3,address=0x32'); - self.vm.add_args('-device', - 'i2c-echo,bus=aspeed.i2c.bus.3,address=0x42'); - self.do_test_arm_aspeed_buildroot_start(image_path, '0xf00', 'Aspeed AST2600 EVB') - - exec_command_and_wait_for_pattern(self, - 'echo lm75 0x4d > /sys/class/i2c-dev/i2c-3/device/new_device', - 'i2c i2c-3: new_device: Instantiated device lm75 at 0x4d'); - exec_command_and_wait_for_pattern(self, - 'cat /sys/class/hwmon/hwmon1/temp1_input', '0') - self.vm.cmd('qom-set', path='/machine/peripheral/tmp-test', - property='temperature', value=18000); - exec_command_and_wait_for_pattern(self, - 'cat /sys/class/hwmon/hwmon1/temp1_input', '18000') - - exec_command_and_wait_for_pattern(self, - 'echo ds1307 0x32 > /sys/class/i2c-dev/i2c-3/device/new_device', - 'i2c i2c-3: new_device: Instantiated device ds1307 at 0x32'); - year = time.strftime("%Y") - exec_command_and_wait_for_pattern(self, 'hwclock -f /dev/rtc1', year); - - exec_command_and_wait_for_pattern(self, - 'echo slave-24c02 0x1064 > /sys/bus/i2c/devices/i2c-3/new_device', - 'i2c i2c-3: new_device: Instantiated device slave-24c02 at 0x64'); - exec_command(self, 'i2cset -y 3 0x42 0x64 0x00 0xaa i'); - time.sleep(0.1) - exec_command_and_wait_for_pattern(self, - 'hexdump /sys/bus/i2c/devices/3-1064/slave-eeprom', - '0000000 ffaa ffff ffff ffff ffff ffff ffff ffff'); - self.do_test_arm_aspeed_buildroot_poweroff() - - @skipUnless(*has_cmd('swtpm')) - def test_arm_ast2600_evb_buildroot_tpm(self): - """ - :avocado: tags=arch:arm - :avocado: tags=machine:ast2600-evb - """ - - image_url = ('https://github.com/legoater/qemu-aspeed-boot/raw/master/' - 'images/ast2600-evb/buildroot-2023.02-tpm/flash.img') - image_hash = ('a46009ae8a5403a0826d607215e731a8c68d27c14c41e55331706b8f9c7bd997') - image_path = self.fetch_asset(image_url, asset_hash=image_hash, - algorithm='sha256') - - # force creation of VM object, which also defines self._sd - vm = self.vm - - socket = os.path.join(self._sd.name, 'swtpm-socket') - - subprocess.run(['swtpm', 'socket', '-d', '--tpm2', - '--tpmstate', f'dir={self.vm.temp_dir}', - '--ctrl', f'type=unixio,path={socket}']) - - self.vm.add_args('-chardev', f'socket,id=chrtpm,path={socket}') - self.vm.add_args('-tpmdev', 'emulator,id=tpm0,chardev=chrtpm') - self.vm.add_args('-device', - 'tpm-tis-i2c,tpmdev=tpm0,bus=aspeed.i2c.bus.12,address=0x2e') - self.do_test_arm_aspeed_buildroot_start(image_path, '0xf00', 'Aspeed AST2600 EVB') - - exec_command_and_wait_for_pattern(self, - 'echo tpm_tis_i2c 0x2e > /sys/bus/i2c/devices/i2c-12/new_device', - 'tpm_tis_i2c 12-002e: 2.0 TPM (device-id 0x1, rev-id 1)'); - exec_command_and_wait_for_pattern(self, - 'cat /sys/class/tpm/tpm0/pcr-sha256/0', - 'B804724EA13F52A9072BA87FE8FDCC497DFC9DF9AA15B9088694639C431688E0'); - - self.do_test_arm_aspeed_buildroot_poweroff() - -class AST2x00MachineSDK(QemuSystemTest, LinuxSSHMixIn): - - EXTRA_BOOTARGS = ( - 'quiet ' - 'systemd.mask=org.openbmc.HostIpmi.service ' - 'systemd.mask=xyz.openbmc_project.Chassis.Control.Power@0.service ' - 'systemd.mask=modprobe@fuse.service ' - 'systemd.mask=rngd.service ' - 'systemd.mask=obmc-console@ttyS2.service ' - ) - - # FIXME: Although these tests boot a whole distro they are still - # slower than comparable machine models. There may be some - # optimisations which bring down the runtime. In the meantime they - # have generous timeouts and are disable for CI which aims for all - # tests to run in less than 60 seconds. - timeout = 240 - - def wait_for_console_pattern(self, success_message, vm=None): - wait_for_console_pattern(self, success_message, - failure_message='Kernel panic - not syncing', - vm=vm) - - def do_test_arm_aspeed_sdk_start(self, image): - self.require_netdev('user') - self.vm.set_console() - self.vm.add_args('-drive', 'file=' + image + ',if=mtd,format=raw', - '-net', 'nic', '-net', 'user,hostfwd=:127.0.0.1:0-:22') - self.vm.launch() - - self.wait_for_console_pattern('U-Boot 2019.04') - interrupt_interactive_console_until_pattern( - self, 'Hit any key to stop autoboot:', 'ast#') - exec_command_and_wait_for_pattern( - self, 'setenv bootargs ${bootargs} ' + self.EXTRA_BOOTARGS, 'ast#') - exec_command_and_wait_for_pattern( - self, 'boot', '## Loading kernel from FIT Image') - self.wait_for_console_pattern('Starting kernel ...') - - def do_test_aarch64_aspeed_sdk_start(self, image): - self.vm.set_console() - self.vm.add_args('-drive', 'file=' + image + ',if=mtd,format=raw', - '-net', 'nic', '-net', 'user,hostfwd=:127.0.0.1:0-:22') - - self.vm.launch() - - self.wait_for_console_pattern('U-Boot 2023.10') - self.wait_for_console_pattern('## Loading kernel from FIT Image') - self.wait_for_console_pattern('Starting kernel ...') - - @skipUnless(os.getenv('QEMU_TEST_FLAKY_TESTS'), 'Test is unstable on GitLab') - def test_arm_ast2500_evb_sdk(self): - """ - :avocado: tags=arch:arm - :avocado: tags=machine:ast2500-evb - :avocado: tags=flaky - """ - - image_url = ('https://github.com/AspeedTech-BMC/openbmc/releases/' - 'download/v08.06/ast2500-default-obmc.tar.gz') - image_hash = ('e1755f3cadff69190438c688d52dd0f0d399b70a1e14b1d3d5540fc4851d38ca') - image_path = self.fetch_asset(image_url, asset_hash=image_hash, - algorithm='sha256') - archive.extract(image_path, self.workdir) - - self.do_test_arm_aspeed_sdk_start( - self.workdir + '/ast2500-default/image-bmc') - self.wait_for_console_pattern('nodistro.0 ast2500-default ttyS4') - - @skipUnless(os.getenv('QEMU_TEST_FLAKY_TESTS'), 'Test is unstable on GitLab') - def test_arm_ast2600_evb_sdk(self): - """ - :avocado: tags=arch:arm - :avocado: tags=machine:ast2600-evb - :avocado: tags=flaky - """ - - image_url = ('https://github.com/AspeedTech-BMC/openbmc/releases/' - 'download/v08.06/ast2600-a2-obmc.tar.gz') - image_hash = ('9083506135f622d5e7351fcf7d4e1c7125cee5ba16141220c0ba88931f3681a4') - image_path = self.fetch_asset(image_url, asset_hash=image_hash, - algorithm='sha256') - archive.extract(image_path, self.workdir) - - self.vm.add_args('-device', - 'tmp105,bus=aspeed.i2c.bus.5,address=0x4d,id=tmp-test'); - self.vm.add_args('-device', - 'ds1338,bus=aspeed.i2c.bus.5,address=0x32'); - self.do_test_arm_aspeed_sdk_start( - self.workdir + '/ast2600-a2/image-bmc') - self.wait_for_console_pattern('nodistro.0 ast2600-a2 ttyS4') - - self.ssh_connect('root', '0penBmc', False) - self.ssh_command('dmesg -c > /dev/null') - - self.ssh_command_output_contains( - 'echo lm75 0x4d > /sys/class/i2c-dev/i2c-5/device/new_device ; ' - 'dmesg -c', - 'i2c i2c-5: new_device: Instantiated device lm75 at 0x4d'); - self.ssh_command_output_contains( - 'cat /sys/class/hwmon/hwmon19/temp1_input', '0') - self.vm.cmd('qom-set', path='/machine/peripheral/tmp-test', - property='temperature', value=18000); - self.ssh_command_output_contains( - 'cat /sys/class/hwmon/hwmon19/temp1_input', '18000') - - self.ssh_command_output_contains( - 'echo ds1307 0x32 > /sys/class/i2c-dev/i2c-5/device/new_device ; ' - 'dmesg -c', - 'i2c i2c-5: new_device: Instantiated device ds1307 at 0x32'); - year = time.strftime("%Y") - self.ssh_command_output_contains('/sbin/hwclock -f /dev/rtc1', year); - - def test_aarch64_ast2700_evb_sdk_v09_02(self): - """ - :avocado: tags=arch:aarch64 - :avocado: tags=machine:ast2700-evb - """ - - image_url = ('https://github.com/AspeedTech-BMC/openbmc/releases/' - 'download/v09.02/ast2700-default-obmc.tar.gz') - image_hash = 'ac969c2602f4e6bdb69562ff466b89ae3fe1d86e1f6797bb7969d787f82116a7' - image_path = self.fetch_asset(image_url, asset_hash=image_hash, - algorithm='sha256') - archive.extract(image_path, self.workdir) - - num_cpu = 4 - image_dir = self.workdir + '/ast2700-default/' - uboot_size = os.path.getsize(image_dir + 'u-boot-nodtb.bin') - uboot_dtb_load_addr = hex(0x400000000 + uboot_size) - - load_images_list = [ - { - 'addr': '0x400000000', - 'file': image_dir + 'u-boot-nodtb.bin' - }, - { - 'addr': str(uboot_dtb_load_addr), - 'file': image_dir + 'u-boot.dtb' - }, - { - 'addr': '0x430000000', - 'file': image_dir + 'bl31.bin' - }, - { - 'addr': '0x430080000', - 'file': image_dir + 'optee/tee-raw.bin' - } - ] - - for load_image in load_images_list: - addr = load_image['addr'] - file = load_image['file'] - self.vm.add_args('-device', - f'loader,force-raw=on,addr={addr},file={file}') - - for i in range(num_cpu): - self.vm.add_args('-device', - f'loader,addr=0x430000000,cpu-num={i}') - - self.vm.add_args('-smp', str(num_cpu)) - self.do_test_aarch64_aspeed_sdk_start(image_dir + 'image-bmc') - self.wait_for_console_pattern('nodistro.0 ast2700-default ttyS12') - self.ssh_connect('root', '0penBmc', False) - -class AST2x00MachineMMC(QemuSystemTest): - - timeout = 240 - - def wait_for_console_pattern(self, success_message, vm=None): - wait_for_console_pattern(self, success_message, - failure_message='Kernel panic - not syncing', - vm=vm) - - def test_arm_aspeed_emmc_boot(self): - """ - :avocado: tags=arch:arm - :avocado: tags=machine:rainier-bmc - :avocado: tags=device:emmc - """ - - image_url = ('https://fileserver.linaro.org/s/B6pJTwWEkzSDi36/download/' - 'mmc-p10bmc-20240617.qcow2') - image_hash = ('d523fb478d2b84d5adc5658d08502bc64b1486955683814f89c6137518acd90b') - image_path = self.fetch_asset(image_url, asset_hash=image_hash, - algorithm='sha256') - - self.require_netdev('user') - - self.vm.set_console() - self.vm.add_args('-drive', - 'file=' + image_path + ',if=sd,id=sd2,index=2', - '-net', 'nic', '-net', 'user') - self.vm.launch() - - self.wait_for_console_pattern('U-Boot SPL 2019.04') - self.wait_for_console_pattern('Trying to boot from MMC1') - self.wait_for_console_pattern('U-Boot 2019.04') - self.wait_for_console_pattern('eMMC 2nd Boot') - self.wait_for_console_pattern('## Loading kernel from FIT Image') - self.wait_for_console_pattern('Starting kernel ...') - self.wait_for_console_pattern('Booting Linux on physical CPU 0xf00') - self.wait_for_console_pattern('mmcblk0: p1 p2 p3 p4 p5 p6 p7') - self.wait_for_console_pattern('IBM eBMC (OpenBMC for IBM Enterprise') diff --git a/tests/avocado/machine_mips_malta.py b/tests/avocado/machine_mips_malta.py deleted file mode 100644 index 07a8063..0000000 --- a/tests/avocado/machine_mips_malta.py +++ /dev/null @@ -1,162 +0,0 @@ -# Functional tests for the MIPS Malta board -# -# Copyright (c) Philippe Mathieu-Daudé <f4bug@amsat.org> -# -# This work is licensed under the terms of the GNU GPL, version 2 or later. -# See the COPYING file in the top-level directory. -# -# SPDX-License-Identifier: GPL-2.0-or-later - -import os -import gzip -import logging - -from avocado import skipUnless -from avocado import skipUnless -from avocado.utils import archive -from avocado_qemu import QemuSystemTest -from avocado_qemu import exec_command_and_wait_for_pattern -from avocado_qemu import interrupt_interactive_console_until_pattern -from avocado_qemu import wait_for_console_pattern - - -NUMPY_AVAILABLE = True -try: - import numpy as np -except ImportError: - NUMPY_AVAILABLE = False - -CV2_AVAILABLE = True -try: - import cv2 -except ImportError: - CV2_AVAILABLE = False - - -@skipUnless(NUMPY_AVAILABLE, 'Python NumPy not installed') -@skipUnless(CV2_AVAILABLE, 'Python OpenCV not installed') -class MaltaMachineFramebuffer(QemuSystemTest): - - timeout = 30 - - KERNEL_COMMON_COMMAND_LINE = 'printk.time=0 ' - - def do_test_i6400_framebuffer_logo(self, cpu_cores_count): - """ - Boot Linux kernel and check Tux logo is displayed on the framebuffer. - """ - screendump_path = os.path.join(self.workdir, 'screendump.pbm') - - kernel_url = ('https://github.com/philmd/qemu-testing-blob/raw/' - 'a5966ca4b5/mips/malta/mips64el/' - 'vmlinux-4.7.0-rc1.I6400.gz') - kernel_hash = '096f50c377ec5072e6a366943324622c312045f6' - kernel_path_gz = self.fetch_asset(kernel_url, asset_hash=kernel_hash) - kernel_path = self.workdir + "vmlinux" - archive.gzip_uncompress(kernel_path_gz, kernel_path) - - tuxlogo_url = ('https://github.com/torvalds/linux/raw/v2.6.12/' - 'drivers/video/logo/logo_linux_vga16.ppm') - tuxlogo_hash = '3991c2ddbd1ddaecda7601f8aafbcf5b02dc86af' - tuxlogo_path = self.fetch_asset(tuxlogo_url, asset_hash=tuxlogo_hash) - - self.vm.set_console() - kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + - 'clocksource=GIC console=tty0 console=ttyS0') - self.vm.add_args('-kernel', kernel_path, - '-smp', '%u' % cpu_cores_count, - '-vga', 'std', - '-append', kernel_command_line) - self.vm.launch() - framebuffer_ready = 'Console: switching to colour frame buffer device' - wait_for_console_pattern(self, framebuffer_ready, - failure_message='Kernel panic - not syncing') - self.vm.cmd('human-monitor-command', command_line='stop') - self.vm.cmd('human-monitor-command', - command_line='screendump %s' % screendump_path) - logger = logging.getLogger('framebuffer') - - match_threshold = 0.95 - screendump_bgr = cv2.imread(screendump_path, cv2.IMREAD_COLOR) - tuxlogo_bgr = cv2.imread(tuxlogo_path, cv2.IMREAD_COLOR) - result = cv2.matchTemplate(screendump_bgr, tuxlogo_bgr, - cv2.TM_CCOEFF_NORMED) - loc = np.where(result >= match_threshold) - tuxlogo_count = 0 - h, w = tuxlogo_bgr.shape[:2] - debug_png = os.getenv('AVOCADO_CV2_SCREENDUMP_PNG_PATH') - for tuxlogo_count, pt in enumerate(zip(*loc[::-1]), start=1): - logger.debug('found Tux at position (x, y) = %s', pt) - cv2.rectangle(screendump_bgr, pt, - (pt[0] + w, pt[1] + h), (0, 0, 255), 2) - if debug_png: - cv2.imwrite(debug_png, screendump_bgr) - self.assertGreaterEqual(tuxlogo_count, cpu_cores_count) - - def test_mips_malta_i6400_framebuffer_logo_1core(self): - """ - :avocado: tags=arch:mips64el - :avocado: tags=machine:malta - :avocado: tags=cpu:I6400 - """ - self.do_test_i6400_framebuffer_logo(1) - - @skipUnless(os.getenv('QEMU_TEST_FLAKY_TESTS'), 'Test is unstable on GitLab') - def test_mips_malta_i6400_framebuffer_logo_7cores(self): - """ - :avocado: tags=arch:mips64el - :avocado: tags=machine:malta - :avocado: tags=cpu:I6400 - :avocado: tags=mips:smp - :avocado: tags=flaky - """ - self.do_test_i6400_framebuffer_logo(7) - - @skipUnless(os.getenv('QEMU_TEST_FLAKY_TESTS'), 'Test is unstable on GitLab') - def test_mips_malta_i6400_framebuffer_logo_8cores(self): - """ - :avocado: tags=arch:mips64el - :avocado: tags=machine:malta - :avocado: tags=cpu:I6400 - :avocado: tags=mips:smp - :avocado: tags=flaky - """ - self.do_test_i6400_framebuffer_logo(8) - -class MaltaMachine(QemuSystemTest): - - def do_test_yamon(self): - rom_url = ('https://s3-eu-west-1.amazonaws.com/' - 'downloads-mips/mips-downloads/' - 'YAMON/yamon-bin-02.22.zip') - rom_hash = '8da7ecddbc5312704b8b324341ee238189bde480' - zip_path = self.fetch_asset(rom_url, asset_hash=rom_hash) - - archive.extract(zip_path, self.workdir) - yamon_path = os.path.join(self.workdir, 'yamon-02.22.bin') - - self.vm.set_console() - self.vm.add_args('-bios', yamon_path) - self.vm.launch() - - prompt = 'YAMON>' - pattern = 'YAMON ROM Monitor' - interrupt_interactive_console_until_pattern(self, pattern, prompt) - wait_for_console_pattern(self, prompt) - self.vm.shutdown() - - def test_mipsel_malta_yamon(self): - """ - :avocado: tags=arch:mipsel - :avocado: tags=machine:malta - :avocado: tags=endian:little - """ - self.do_test_yamon() - - def test_mips64el_malta_yamon(self): - """ - :avocado: tags=arch:mips64el - :avocado: tags=machine:malta - :avocado: tags=endian:little - """ - self.do_test_yamon() diff --git a/tests/avocado/migration.py b/tests/avocado/migration.py deleted file mode 100644 index be6234b..0000000 --- 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/multiprocess.py b/tests/avocado/multiprocess.py deleted file mode 100644 index ee7490a..0000000 --- a/tests/avocado/multiprocess.py +++ /dev/null @@ -1,102 +0,0 @@ -# Test for multiprocess qemu -# -# 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 socket - -from avocado_qemu import QemuSystemTest -from avocado_qemu import wait_for_console_pattern -from avocado_qemu import exec_command -from avocado_qemu import exec_command_and_wait_for_pattern - -class Multiprocess(QemuSystemTest): - """ - :avocado: tags=multiprocess - """ - KERNEL_COMMON_COMMAND_LINE = 'printk.time=0 ' - - def do_test(self, kernel_url, kernel_hash, initrd_url, initrd_hash, - kernel_command_line, machine_type): - """Main test method""" - self.require_accelerator('kvm') - self.require_multiprocess() - - # Create socketpair to connect proxy and remote processes - proxy_sock, remote_sock = socket.socketpair(socket.AF_UNIX, - socket.SOCK_STREAM) - os.set_inheritable(proxy_sock.fileno(), True) - os.set_inheritable(remote_sock.fileno(), True) - - kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) - initrd_path = self.fetch_asset(initrd_url, asset_hash=initrd_hash) - - # Create remote process - remote_vm = self.get_vm() - remote_vm.add_args('-machine', 'x-remote') - remote_vm.add_args('-nodefaults') - remote_vm.add_args('-device', 'lsi53c895a,id=lsi1') - remote_vm.add_args('-object', 'x-remote-object,id=robj1,' - 'devid=lsi1,fd='+str(remote_sock.fileno())) - remote_vm.launch() - - # Create proxy process - self.vm.set_console() - self.vm.add_args('-machine', machine_type) - self.vm.add_args('-accel', 'kvm') - self.vm.add_args('-cpu', 'host') - self.vm.add_args('-object', - 'memory-backend-memfd,id=sysmem-file,size=2G') - self.vm.add_args('--numa', 'node,memdev=sysmem-file') - self.vm.add_args('-m', '2048') - self.vm.add_args('-kernel', kernel_path, - '-initrd', initrd_path, - '-append', kernel_command_line) - self.vm.add_args('-device', - 'x-pci-proxy-dev,' - 'id=lsi1,fd='+str(proxy_sock.fileno())) - self.vm.launch() - wait_for_console_pattern(self, 'as init process', - 'Kernel panic - not syncing') - exec_command(self, 'mount -t sysfs sysfs /sys') - exec_command_and_wait_for_pattern(self, - 'cat /sys/bus/pci/devices/*/uevent', - 'PCI_ID=1000:0012') - - def test_multiprocess_x86_64(self): - """ - :avocado: tags=arch:x86_64 - """ - kernel_url = ('https://archives.fedoraproject.org/pub/archive/fedora' - '/linux/releases/31/Everything/x86_64/os/images' - '/pxeboot/vmlinuz') - kernel_hash = '5b6f6876e1b5bda314f93893271da0d5777b1f3c' - initrd_url = ('https://archives.fedoraproject.org/pub/archive/fedora' - '/linux/releases/31/Everything/x86_64/os/images' - '/pxeboot/initrd.img') - initrd_hash = 'dd0340a1b39bd28f88532babd4581c67649ec5b1' - kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + - 'console=ttyS0 rdinit=/bin/bash') - machine_type = 'pc' - self.do_test(kernel_url, kernel_hash, initrd_url, initrd_hash, - kernel_command_line, machine_type) - - def test_multiprocess_aarch64(self): - """ - :avocado: tags=arch:aarch64 - """ - kernel_url = ('https://archives.fedoraproject.org/pub/archive/fedora' - '/linux/releases/31/Everything/aarch64/os/images' - '/pxeboot/vmlinuz') - kernel_hash = '3505f2751e2833c681de78cee8dda1e49cabd2e8' - initrd_url = ('https://archives.fedoraproject.org/pub/archive/fedora' - '/linux/releases/31/Everything/aarch64/os/images' - '/pxeboot/initrd.img') - initrd_hash = '519a1962daf17d67fc3a9c89d45affcb399607db' - kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + - 'rdinit=/bin/bash console=ttyAMA0') - machine_type = 'virt,gic-version=3' - self.do_test(kernel_url, kernel_hash, initrd_url, initrd_hash, - kernel_command_line, machine_type) diff --git a/tests/avocado/replay_kernel.py b/tests/avocado/replay_kernel.py deleted file mode 100644 index e22c200..0000000 --- a/tests/avocado/replay_kernel.py +++ /dev/null @@ -1,577 +0,0 @@ -# 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 lzma -import shutil -import logging -import time -import subprocess - -from avocado import skip -from avocado import skipUnless -from avocado import skipUnless -from avocado_qemu import wait_for_console_pattern -from avocado.utils import archive -from avocado.utils import process -from boot_linux_console 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 - KERNEL_COMMON_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)) - -class ReplayKernelNormal(ReplayKernelBase): - - def test_i386_pc(self): - """ - :avocado: tags=arch:i386 - :avocado: tags=machine:pc - """ - kernel_url = ('https://storage.tuxboot.com/20230331/i386/bzImage') - kernel_hash = 'a3e5b32a354729e65910f5a1ffcda7c14a6c12a55e8213fb86e277f1b76ed956' - kernel_path = self.fetch_asset(kernel_url, - asset_hash=kernel_hash, - algorithm = "sha256") - - kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'console=ttyS0' - console_pattern = 'VFS: Cannot open root device' - - self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=5) - - # See https://gitlab.com/qemu-project/qemu/-/issues/2094 - @skipUnless(os.getenv('QEMU_TEST_FLAKY_TESTS'), 'pc machine is unstable with replay') - def test_x86_64_pc(self): - """ - :avocado: tags=arch:x86_64 - :avocado: tags=machine:pc - :avocado: tags=flaky - """ - kernel_url = ('https://archives.fedoraproject.org/pub/archive/fedora' - '/linux/releases/29/Everything/x86_64/os/images/pxeboot' - '/vmlinuz') - kernel_hash = '23bebd2680757891cf7adedb033532163a792495' - kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) - - kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'console=ttyS0' - console_pattern = 'VFS: Cannot open root device' - - self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=5) - - def test_x86_64_q35(self): - """ - :avocado: tags=arch:x86_64 - :avocado: tags=machine:q35 - """ - kernel_url = ('https://archives.fedoraproject.org/pub/archive/fedora' - '/linux/releases/29/Everything/x86_64/os/images/pxeboot' - '/vmlinuz') - kernel_hash = '23bebd2680757891cf7adedb033532163a792495' - kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) - - kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'console=ttyS0' - console_pattern = 'VFS: Cannot open root device' - - 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 - :avocado: tags=machine:virt - :avocado: tags=cpu:cortex-a53 - """ - kernel_url = ('https://archives.fedoraproject.org/pub/archive/fedora' - '/linux/releases/29/Everything/aarch64/os/images/pxeboot' - '/vmlinuz') - kernel_hash = '8c73e469fc6ea06a58dc83a628fc695b693b8493' - kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) - - kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + - 'console=ttyAMA0') - console_pattern = 'VFS: Cannot open root device' - - self.run_rr(kernel_path, kernel_command_line, console_pattern) - - def test_arm_virt(self): - """ - :avocado: tags=arch:arm - :avocado: tags=machine:virt - """ - kernel_url = ('https://archives.fedoraproject.org/pub/archive/fedora' - '/linux/releases/29/Everything/armhfp/os/images/pxeboot' - '/vmlinuz') - kernel_hash = 'e9826d741b4fb04cadba8d4824d1ed3b7fb8b4d4' - kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) - - kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + - 'console=ttyAMA0') - console_pattern = 'VFS: Cannot open root device' - - self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=1) - - def test_arm_cubieboard_initrd(self): - """ - :avocado: tags=arch:arm - :avocado: tags=machine:cubieboard - """ - deb_url = ('https://apt.armbian.com/pool/main/l/' - 'linux-6.6.16/linux-image-current-sunxi_24.2.1_armhf__6.6.16-Seb3e-D6b4a-P2359-Ce96bHfe66-HK01ba-V014b-B067e-R448a.deb') - deb_hash = 'f7c3c8c5432f765445dc6e7eab02f3bbe668256b' - deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash) - kernel_path = self.extract_from_deb(deb_path, - '/boot/vmlinuz-6.6.16-current-sunxi') - dtb_path = '/usr/lib/linux-image-6.6.16-current-sunxi/sun4i-a10-cubieboard.dtb' - dtb_path = self.extract_from_deb(deb_path, dtb_path) - initrd_url = ('https://github.com/groeck/linux-build-test/raw/' - '2eb0a73b5d5a28df3170c546ddaaa9757e1e0848/rootfs/' - 'arm/rootfs-armv5.cpio.gz') - initrd_hash = '2b50f1873e113523967806f4da2afe385462ff9b' - initrd_path_gz = self.fetch_asset(initrd_url, asset_hash=initrd_hash) - initrd_path = os.path.join(self.workdir, 'rootfs.cpio') - archive.gzip_uncompress(initrd_path_gz, initrd_path) - - kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + - 'console=ttyS0,115200 ' - 'usbcore.nousb ' - 'panic=-1 noreboot') - console_pattern = 'Boot successful.' - self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=1, - args=('-dtb', dtb_path, - '-initrd', initrd_path, - '-no-reboot')) - - def test_s390x_s390_ccw_virtio(self): - """ - :avocado: tags=arch:s390x - :avocado: tags=machine:s390-ccw-virtio - """ - kernel_url = ('https://archives.fedoraproject.org/pub/archive' - '/fedora-secondary/releases/29/Everything/s390x/os/images' - '/kernel.img') - kernel_hash = 'e8e8439103ef8053418ef062644ffd46a7919313' - kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) - - kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'console=sclp0' - console_pattern = 'Kernel command line: %s' % kernel_command_line - self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=9) - - def test_alpha_clipper(self): - """ - :avocado: tags=arch:alpha - :avocado: tags=machine:clipper - """ - kernel_url = ('http://archive.debian.org/debian/dists/lenny/main/' - 'installer-alpha/20090123lenny10/images/cdrom/vmlinuz') - kernel_hash = '3a943149335529e2ed3e74d0d787b85fb5671ba3' - kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) - - uncompressed_kernel = archive.uncompress(kernel_path, self.workdir) - - kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'console=ttyS0' - console_pattern = 'Kernel command line: %s' % kernel_command_line - self.run_rr(uncompressed_kernel, kernel_command_line, console_pattern, shift=9, - args=('-nodefaults', )) - - def test_ppc64_pseries(self): - """ - :avocado: tags=arch:ppc64 - :avocado: tags=machine:pseries - :avocado: tags=accel:tcg - """ - kernel_url = ('https://archives.fedoraproject.org/pub/archive' - '/fedora-secondary/releases/29/Everything/ppc64le/os' - '/ppc/ppc64/vmlinuz') - kernel_hash = '3fe04abfc852b66653b8c3c897a59a689270bc77' - kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) - - kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'console=hvc0' - console_pattern = 'VFS: Cannot open root device' - self.run_rr(kernel_path, kernel_command_line, console_pattern) - - def test_ppc64_powernv(self): - """ - :avocado: tags=arch:ppc64 - :avocado: tags=machine:powernv - :avocado: tags=accel:tcg - """ - kernel_url = ('https://archives.fedoraproject.org/pub/archive' - '/fedora-secondary/releases/29/Everything/ppc64le/os' - '/ppc/ppc64/vmlinuz') - kernel_hash = '3fe04abfc852b66653b8c3c897a59a689270bc77' - kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) - - kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + \ - 'console=tty0 console=hvc0' - console_pattern = 'VFS: Cannot open root device' - self.run_rr(kernel_path, kernel_command_line, console_pattern) - - def test_m68k_q800(self): - """ - :avocado: tags=arch:m68k - :avocado: tags=machine:q800 - """ - deb_url = ('https://snapshot.debian.org/archive/debian-ports' - '/20191021T083923Z/pool-m68k/main' - '/l/linux/kernel-image-5.3.0-1-m68k-di_5.3.7-1_m68k.udeb') - deb_hash = '044954bb9be4160a3ce81f8bc1b5e856b75cccd1' - deb_path = self.fetch_asset(deb_url, asset_hash=deb_hash) - kernel_path = self.extract_from_deb(deb_path, - '/boot/vmlinux-5.3.0-1-m68k') - - kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + - 'console=ttyS0 vga=off') - console_pattern = 'No filesystem could mount root' - self.run_rr(kernel_path, kernel_command_line, console_pattern) - - def do_test_advcal_2018(self, file_path, kernel_name, args=None): - archive.extract(file_path, self.workdir) - - for entry in os.scandir(self.workdir): - if entry.name.startswith('day') and entry.is_dir(): - kernel_path = os.path.join(entry.path, kernel_name) - break - - kernel_command_line = '' - console_pattern = 'QEMU advent calendar' - self.run_rr(kernel_path, kernel_command_line, console_pattern, - args=args) - - def test_arm_vexpressa9(self): - """ - :avocado: tags=arch:arm - :avocado: tags=machine:vexpress-a9 - """ - tar_hash = '32b7677ce8b6f1471fb0059865f451169934245b' - tar_url = ('https://qemu-advcal.gitlab.io' - '/qac-best-of-multiarch/download/day16.tar.xz') - file_path = self.fetch_asset(tar_url, asset_hash=tar_hash) - dtb_path = self.workdir + '/day16/vexpress-v2p-ca9.dtb' - self.do_test_advcal_2018(file_path, 'winter.zImage', - args=('-dtb', dtb_path)) - - def test_m68k_mcf5208evb(self): - """ - :avocado: tags=arch:m68k - :avocado: tags=machine:mcf5208evb - """ - tar_hash = 'ac688fd00561a2b6ce1359f9ff6aa2b98c9a570c' - tar_url = ('https://qemu-advcal.gitlab.io' - '/qac-best-of-multiarch/download/day07.tar.xz') - file_path = self.fetch_asset(tar_url, asset_hash=tar_hash) - self.do_test_advcal_2018(file_path, 'sanity-clause.elf') - - def test_microblaze_s3adsp1800(self): - """ - :avocado: tags=arch:microblaze - :avocado: tags=machine:petalogix-s3adsp1800 - """ - tar_hash = '08bf3e3bfb6b6c7ce1e54ab65d54e189f2caf13f' - tar_url = ('https://qemu-advcal.gitlab.io' - '/qac-best-of-multiarch/download/day17.tar.xz') - file_path = self.fetch_asset(tar_url, asset_hash=tar_hash) - self.do_test_advcal_2018(file_path, 'ballerina.bin') - - def test_ppc64_e500(self): - """ - :avocado: tags=arch:ppc64 - :avocado: tags=machine:ppce500 - :avocado: tags=cpu:e5500 - """ - tar_hash = '6951d86d644b302898da2fd701739c9406527fe1' - tar_url = ('https://qemu-advcal.gitlab.io' - '/qac-best-of-multiarch/download/day19.tar.xz') - file_path = self.fetch_asset(tar_url, asset_hash=tar_hash) - self.do_test_advcal_2018(file_path, 'uImage') - - def test_or1k_sim(self): - """ - :avocado: tags=arch:or1k - :avocado: tags=machine:or1k-sim - """ - tar_hash = '20334cdaf386108c530ff0badaecc955693027dd' - tar_url = ('https://qemu-advcal.gitlab.io' - '/qac-best-of-multiarch/download/day20.tar.xz') - file_path = self.fetch_asset(tar_url, asset_hash=tar_hash) - self.do_test_advcal_2018(file_path, 'vmlinux') - - def test_ppc_g3beige(self): - """ - :avocado: tags=arch:ppc - :avocado: tags=machine:g3beige - """ - tar_hash = 'e0b872a5eb8fdc5bed19bd43ffe863900ebcedfc' - tar_url = ('https://qemu-advcal.gitlab.io' - '/qac-best-of-multiarch/download/day15.tar.xz') - file_path = self.fetch_asset(tar_url, asset_hash=tar_hash) - self.do_test_advcal_2018(file_path, 'invaders.elf', - args=('-M', 'graphics=off')) - - def test_ppc_mac99(self): - """ - :avocado: tags=arch:ppc - :avocado: tags=machine:mac99 - """ - tar_hash = 'e0b872a5eb8fdc5bed19bd43ffe863900ebcedfc' - tar_url = ('https://qemu-advcal.gitlab.io' - '/qac-best-of-multiarch/download/day15.tar.xz') - file_path = self.fetch_asset(tar_url, asset_hash=tar_hash) - self.do_test_advcal_2018(file_path, 'invaders.elf', - args=('-M', 'graphics=off')) - - def test_sparc_ss20(self): - """ - :avocado: tags=arch:sparc - :avocado: tags=machine:SS-20 - """ - tar_hash = 'b18550d5d61c7615d989a06edace051017726a9f' - tar_url = ('https://qemu-advcal.gitlab.io' - '/qac-best-of-multiarch/download/day11.tar.xz') - file_path = self.fetch_asset(tar_url, asset_hash=tar_hash) - self.do_test_advcal_2018(file_path, 'zImage.elf') - - def test_xtensa_lx60(self): - """ - :avocado: tags=arch:xtensa - :avocado: tags=machine:lx60 - :avocado: tags=cpu:dc233c - """ - tar_hash = '49e88d9933742f0164b60839886c9739cb7a0d34' - tar_url = ('https://qemu-advcal.gitlab.io' - '/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/avocado/replay_linux.py b/tests/avocado/replay_linux.py deleted file mode 100644 index 5916922..0000000 --- a/tests/avocado/replay_linux.py +++ /dev/null @@ -1,206 +0,0 @@ -# Record/replay test that boots a complete Linux system via a cloud image -# -# 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 - -from avocado import skipUnless -from avocado_qemu import BUILD_DIR -from avocado.utils import cloudinit -from avocado.utils import network -from avocado.utils import vmimage -from avocado.utils import datadrainer -from avocado.utils.path import find_command -from avocado_qemu.linuxtest import LinuxTest - -class ReplayLinux(LinuxTest): - """ - Boots a Linux system, checking for a successful initialization - """ - - timeout = 1800 - chksum = None - hdd = 'ide-hd' - cd = 'ide-cd' - bus = 'ide' - - def setUp(self): - # LinuxTest does many replay-incompatible things, but includes - # useful methods. Do not setup LinuxTest here and just - # call some functions. - super(LinuxTest, self).setUp() - self._set_distro() - self.boot_path = self.download_boot() - self.phone_server = cloudinit.PhoneHomeServer(('0.0.0.0', 0), - self.name) - ssh_pubkey, self.ssh_key = self.set_up_existing_ssh_keys() - self.cloudinit_path = self.prepare_cloudinit(ssh_pubkey) - - def vm_add_disk(self, vm, path, id, device): - bus_string = '' - if self.bus: - bus_string = ',bus=%s.%d' % (self.bus, id,) - vm.add_args('-drive', 'file=%s,snapshot=on,id=disk%s,if=none' % (path, id)) - vm.add_args('-drive', - 'driver=blkreplay,id=disk%s-rr,if=none,image=disk%s' % (id, id)) - vm.add_args('-device', - '%s,drive=disk%s-rr%s' % (device, id, bus_string)) - - def vm_add_cdrom(self, vm, path, id, device): - vm.add_args('-drive', 'file=%s,id=disk%s,if=none,media=cdrom' % (path, id)) - - def launch_and_wait(self, record, args, shift): - self.require_netdev('user') - vm = self.get_vm() - vm.add_args('-smp', '1') - vm.add_args('-m', '1024') - vm.add_args('-netdev', 'user,id=vnet,hostfwd=:127.0.0.1:0-:22', - '-device', 'virtio-net,netdev=vnet') - vm.add_args('-object', 'filter-replay,id=replay,netdev=vnet') - if args: - vm.add_args(*args) - self.vm_add_disk(vm, self.boot_path, 0, self.hdd) - self.vm_add_cdrom(vm, self.cloudinit_path, 1, self.cd) - logger = logging.getLogger('replay') - if record: - logger.info('recording the execution...') - mode = 'record' - else: - logger.info('replaying the execution...') - mode = 'replay' - replay_path = os.path.join(self.workdir, 'replay.bin') - vm.add_args('-icount', 'shift=%s,rr=%s,rrfile=%s' % - (shift, mode, replay_path)) - - start_time = time.time() - - vm.set_console() - vm.launch() - console_drainer = datadrainer.LineLogger(vm.console_socket.fileno(), - logger=self.log.getChild('console'), - stop_check=(lambda : not vm.is_running())) - console_drainer.start() - if record: - while not self.phone_server.instance_phoned_back: - self.phone_server.handle_request() - 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.event_wait('SHUTDOWN', self.timeout) - vm.wait() - logger.info('successfully finished the replay') - elapsed = time.time() - start_time - logger.info('elapsed time %.2f sec' % elapsed) - return elapsed - - def run_rr(self, args=None, shift=7): - t1 = self.launch_and_wait(True, args, shift) - t2 = self.launch_and_wait(False, args, shift) - logger = logging.getLogger('replay') - logger.info('replay overhead {:.2%}'.format(t2 / t1 - 1)) - - 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') - -@skipUnless(os.getenv('AVOCADO_TIMEOUT_EXPECTED'), 'Test might timeout') -class ReplayLinuxX8664(ReplayLinux): - """ - :avocado: tags=arch:x86_64 - :avocado: tags=accel:tcg - """ - - chksum = 'e3c1b309d9203604922d6e255c2c5d098a309c2d46215d8fc026954f3c5c27a0' - - def test_pc_i440fx(self): - """ - :avocado: tags=machine:pc - """ - self.run_rr(shift=1) - - def test_pc_q35(self): - """ - :avocado: tags=machine:q35 - """ - self.run_rr(shift=3) - -@skipUnless(os.getenv('AVOCADO_TIMEOUT_EXPECTED'), 'Test might timeout') -class ReplayLinuxX8664Virtio(ReplayLinux): - """ - :avocado: tags=arch:x86_64 - :avocado: tags=virtio - :avocado: tags=accel:tcg - """ - - hdd = 'virtio-blk-pci' - cd = 'virtio-blk-pci' - bus = None - - chksum = 'e3c1b309d9203604922d6e255c2c5d098a309c2d46215d8fc026954f3c5c27a0' - - def test_pc_i440fx(self): - """ - :avocado: tags=machine:pc - """ - self.run_rr(shift=1) - - def test_pc_q35(self): - """ - :avocado: tags=machine:q35 - """ - self.run_rr(shift=3) - -@skipUnless(os.getenv('AVOCADO_TIMEOUT_EXPECTED'), 'Test might timeout') -class ReplayLinuxAarch64(ReplayLinux): - """ - :avocado: tags=accel:tcg - :avocado: tags=arch:aarch64 - :avocado: tags=machine:virt - :avocado: tags=cpu:max - """ - - chksum = '1e18d9c0cf734940c4b5d5ec592facaed2af0ad0329383d5639c997fdf16fe49' - - hdd = 'virtio-blk-device' - cd = 'virtio-blk-device' - bus = None - - def get_common_args(self): - return ('-bios', - os.path.join(BUILD_DIR, 'pc-bios', 'edk2-aarch64-code.fd'), - "-cpu", "max,lpa2=off", - '-device', 'virtio-rng-pci,rng=rng0', - '-object', 'rng-builtin,id=rng0') - - def test_virt_gicv2(self): - """ - :avocado: tags=machine:gic-version=2 - """ - - self.run_rr(shift=3, - args=(*self.get_common_args(), - "-machine", "virt,gic-version=2")) - - def test_virt_gicv3(self): - """ - :avocado: tags=machine:gic-version=3 - """ - - self.run_rr(shift=3, - args=(*self.get_common_args(), - "-machine", "virt,gic-version=3")) diff --git a/tests/avocado/riscv_opensbi.py b/tests/avocado/riscv_opensbi.py deleted file mode 100644 index bfff9cc..0000000 --- a/tests/avocado/riscv_opensbi.py +++ /dev/null @@ -1,63 +0,0 @@ -# OpenSBI boot test for RISC-V machines -# -# Copyright (c) 2022, Ventana Micro -# -# 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 import QemuSystemTest -from avocado_qemu import wait_for_console_pattern - -class RiscvOpenSBI(QemuSystemTest): - """ - :avocado: tags=accel:tcg - """ - timeout = 5 - - def boot_opensbi(self): - self.vm.set_console() - self.vm.launch() - wait_for_console_pattern(self, 'Platform Name') - wait_for_console_pattern(self, 'Boot HART MEDELEG') - - def test_riscv32_spike(self): - """ - :avocado: tags=arch:riscv32 - :avocado: tags=machine:spike - """ - self.boot_opensbi() - - def test_riscv64_spike(self): - """ - :avocado: tags=arch:riscv64 - :avocado: tags=machine:spike - """ - self.boot_opensbi() - - def test_riscv32_sifive_u(self): - """ - :avocado: tags=arch:riscv32 - :avocado: tags=machine:sifive_u - """ - self.boot_opensbi() - - def test_riscv64_sifive_u(self): - """ - :avocado: tags=arch:riscv64 - :avocado: tags=machine:sifive_u - """ - self.boot_opensbi() - - def test_riscv32_virt(self): - """ - :avocado: tags=arch:riscv32 - :avocado: tags=machine:virt - """ - self.boot_opensbi() - - def test_riscv64_virt(self): - """ - :avocado: tags=arch:riscv64 - :avocado: tags=machine:virt - """ - self.boot_opensbi() diff --git a/tests/avocado/smmu.py b/tests/avocado/smmu.py deleted file mode 100644 index 83fd79e..0000000 --- a/tests/avocado/smmu.py +++ /dev/null @@ -1,139 +0,0 @@ -# SMMUv3 Functional tests -# -# Copyright (c) 2021 Red Hat, Inc. -# -# Author: -# Eric Auger <eric.auger@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 os - -from avocado import skipUnless -from avocado_qemu import BUILD_DIR -from avocado_qemu.linuxtest import LinuxTest - -@skipUnless(os.getenv('QEMU_TEST_FLAKY_TESTS'), 'Test is unstable on GitLab') -class SMMU(LinuxTest): - """ - :avocado: tags=accel:kvm - :avocado: tags=cpu:host - :avocado: tags=arch:aarch64 - :avocado: tags=machine:virt - :avocado: tags=distro:fedora - :avocado: tags=smmu - :avocado: tags=flaky - """ - - IOMMU_ADDON = ',iommu_platform=on,disable-modern=off,disable-legacy=on' - kernel_path = None - initrd_path = None - kernel_params = None - - def set_up_boot(self): - path = self.download_boot() - self.vm.add_args('-device', 'virtio-blk-pci,bus=pcie.0,' + - 'drive=drv0,id=virtio-disk0,bootindex=1,' - 'werror=stop,rerror=stop' + self.IOMMU_ADDON) - self.vm.add_args('-drive', - 'file=%s,if=none,cache=writethrough,id=drv0' % path) - - def setUp(self): - super(SMMU, self).setUp(None, 'virtio-net-pci' + self.IOMMU_ADDON) - - def common_vm_setup(self, custom_kernel=False): - self.require_accelerator("kvm") - self.vm.add_args("-accel", "kvm") - self.vm.add_args("-cpu", "host") - self.vm.add_args("-machine", "iommu=smmuv3") - self.vm.add_args("-d", "guest_errors") - self.vm.add_args('-bios', os.path.join(BUILD_DIR, 'pc-bios', - 'edk2-aarch64-code.fd')) - self.vm.add_args('-device', 'virtio-rng-pci,rng=rng0') - self.vm.add_args('-object', - 'rng-random,id=rng0,filename=/dev/urandom') - - if custom_kernel is False: - return - - kernel_url = self.distro.pxeboot_url + 'vmlinuz' - initrd_url = self.distro.pxeboot_url + 'initrd.img' - self.kernel_path = self.fetch_asset(kernel_url) - self.initrd_path = self.fetch_asset(initrd_url) - - def run_and_check(self): - if self.kernel_path: - self.vm.add_args('-kernel', self.kernel_path, - '-append', self.kernel_params, - '-initrd', self.initrd_path) - self.launch_and_wait() - self.ssh_command('cat /proc/cmdline') - self.ssh_command('dnf -y install numactl-devel') - - - # 5.3 kernel without RIL # - - def test_smmu_noril(self): - """ - :avocado: tags=smmu_noril - :avocado: tags=smmu_noril_tests - :avocado: tags=distro_version:31 - """ - self.common_vm_setup() - self.run_and_check() - - def test_smmu_noril_passthrough(self): - """ - :avocado: tags=smmu_noril_passthrough - :avocado: tags=smmu_noril_tests - :avocado: tags=distro_version:31 - """ - self.common_vm_setup(True) - self.kernel_params = (self.distro.default_kernel_params + - ' iommu.passthrough=on') - self.run_and_check() - - def test_smmu_noril_nostrict(self): - """ - :avocado: tags=smmu_noril_nostrict - :avocado: tags=smmu_noril_tests - :avocado: tags=distro_version:31 - """ - self.common_vm_setup(True) - self.kernel_params = (self.distro.default_kernel_params + - ' iommu.strict=0') - self.run_and_check() - - # 5.8 kernel featuring range invalidation - # >= v5.7 kernel - - def test_smmu_ril(self): - """ - :avocado: tags=smmu_ril - :avocado: tags=smmu_ril_tests - :avocado: tags=distro_version:33 - """ - self.common_vm_setup() - self.run_and_check() - - def test_smmu_ril_passthrough(self): - """ - :avocado: tags=smmu_ril_passthrough - :avocado: tags=smmu_ril_tests - :avocado: tags=distro_version:33 - """ - self.common_vm_setup(True) - self.kernel_params = (self.distro.default_kernel_params + - ' iommu.passthrough=on') - self.run_and_check() - - def test_smmu_ril_nostrict(self): - """ - :avocado: tags=smmu_ril_nostrict - :avocado: tags=smmu_ril_tests - :avocado: tags=distro_version:33 - """ - self.common_vm_setup(True) - self.kernel_params = (self.distro.default_kernel_params + - ' iommu.strict=0') - self.run_and_check() diff --git a/tests/avocado/tuxrun_baselines.py b/tests/avocado/tuxrun_baselines.py deleted file mode 100644 index 736e4aa..0000000 --- a/tests/avocado/tuxrun_baselines.py +++ /dev/null @@ -1,620 +0,0 @@ -# Functional test that boots known good tuxboot images the same way -# that tuxrun (www.tuxrun.org) does. This tool is used by things like -# the LKFT project to run regression tests on kernels. -# -# Copyright (c) 2023 Linaro Ltd. -# -# Author: -# Alex Bennée <alex.bennee@linaro.org> -# -# SPDX-License-Identifier: GPL-2.0-or-later - -import os -import time -import tempfile - -from avocado import skip, skipUnless -from avocado_qemu import QemuSystemTest -from avocado_qemu import exec_command, exec_command_and_wait_for_pattern -from avocado_qemu import wait_for_console_pattern -from avocado.utils import process -from avocado.utils.path import find_command - -class TuxRunBaselineTest(QemuSystemTest): - """ - :avocado: tags=accel:tcg - """ - - KERNEL_COMMON_COMMAND_LINE = 'printk.time=0' - # Tests are ~10-40s, allow for --debug/--enable-gcov overhead - timeout = 100 - - def get_tag(self, tagname, default=None): - """ - Get the metadata tag or return the default. - """ - utag = self._get_unique_tag_val(tagname) - print(f"{tagname}/{default} -> {utag}") - if utag: - return utag - - return default - - def setUp(self): - super().setUp() - - # We need zstd for all the tuxrun tests - # See https://github.com/avocado-framework/avocado/issues/5609 - zstd = find_command('zstd', False) - if zstd is False: - self.cancel('Could not find "zstd", which is required to ' - 'decompress rootfs') - self.zstd = zstd - - # Process the TuxRun specific tags, most machines work with - # reasonable defaults but we sometimes need to tweak the - # config. To avoid open coding everything we store all these - # details in the metadata for each test. - - # The tuxboot tag matches the root directory - self.tuxboot = self.get_tag('tuxboot') - - # Most Linux's use ttyS0 for their serial port - self.console = self.get_tag('console', "ttyS0") - - # Does the machine shutdown QEMU nicely on "halt" - self.shutdown = self.get_tag('shutdown') - - # The name of the kernel Image file - self.image = self.get_tag('image', "Image") - - self.root = self.get_tag('root', "vda") - - # Occasionally we need extra devices to hook things up - self.extradev = self.get_tag('extradev') - - self.qemu_img = super().get_qemu_img() - - def wait_for_console_pattern(self, success_message, vm=None): - wait_for_console_pattern(self, success_message, - failure_message='Kernel panic - not syncing', - vm=vm) - - def fetch_tuxrun_assets(self, csums=None, dt=None): - """ - Fetch the TuxBoot assets. They are stored in a standard way so we - use the per-test tags to fetch details. - """ - base_url = f"https://storage.tuxboot.com/20230331/{self.tuxboot}/" - - # empty hash if we weren't passed one - csums = {} if csums is None else csums - ksum = csums.get(self.image, None) - isum = csums.get("rootfs.ext4.zst", None) - - kernel_image = self.fetch_asset(base_url + self.image, - asset_hash = ksum, - algorithm = "sha256") - disk_image_zst = self.fetch_asset(base_url + "rootfs.ext4.zst", - asset_hash = isum, - algorithm = "sha256") - - cmd = f"{self.zstd} -d {disk_image_zst} -o {self.workdir}/rootfs.ext4" - process.run(cmd) - - if dt: - dsum = csums.get(dt, None) - dtb = self.fetch_asset(base_url + dt, - asset_hash = dsum, - algorithm = "sha256") - else: - dtb = None - - return (kernel_image, self.workdir + "/rootfs.ext4", dtb) - - def prepare_run(self, kernel, disk, drive, dtb=None, console_index=0): - """ - Setup to run and add the common parameters to the system - """ - self.vm.set_console(console_index=console_index) - - # all block devices are raw ext4's - blockdev = "driver=raw,file.driver=file," \ - + f"file.filename={disk},node-name=hd0" - - kcmd_line = self.KERNEL_COMMON_COMMAND_LINE - kcmd_line += f" root=/dev/{self.root}" - kcmd_line += f" console={self.console}" - - self.vm.add_args('-kernel', kernel, - '-append', kcmd_line, - '-blockdev', blockdev) - - # Sometimes we need extra devices attached - if self.extradev: - self.vm.add_args('-device', self.extradev) - - self.vm.add_args('-device', - f"{drive},drive=hd0") - - # Some machines need an explicit DTB - if dtb: - self.vm.add_args('-dtb', dtb) - - def run_tuxtest_tests(self, haltmsg): - """ - Wait for the system to boot up, wait for the login prompt and - then do a few things on the console. Trigger a shutdown and - wait to exit cleanly. - """ - self.wait_for_console_pattern("Welcome to TuxTest") - time.sleep(0.2) - exec_command(self, 'root') - time.sleep(0.2) - exec_command(self, 'cat /proc/interrupts') - time.sleep(0.1) - exec_command(self, 'cat /proc/self/maps') - time.sleep(0.1) - exec_command(self, 'uname -a') - time.sleep(0.1) - exec_command_and_wait_for_pattern(self, 'halt', haltmsg) - - # Wait for VM to shut down gracefully if it can - if self.shutdown == "nowait": - self.vm.shutdown() - else: - self.vm.wait() - - def common_tuxrun(self, - csums=None, - dt=None, - drive="virtio-blk-device", - haltmsg="reboot: System halted", - console_index=0): - """ - Common path for LKFT tests. Unless we need to do something - special with the command line we can process most things using - the tag metadata. - """ - (kernel, disk, dtb) = self.fetch_tuxrun_assets(csums, dt) - - self.prepare_run(kernel, disk, drive, dtb, console_index) - self.vm.launch() - self.run_tuxtest_tests(haltmsg) - - def ppc64_common_tuxrun(self, sums, prefix): - # add device args to command line. - self.require_netdev('user') - self.vm.add_args('-netdev', 'user,id=vnet,hostfwd=:127.0.0.1:0-:22', - '-device', 'virtio-net,netdev=vnet') - self.vm.add_args('-netdev', '{"type":"user","id":"hostnet0"}', - '-device', '{"driver":"virtio-net-pci","netdev":' - '"hostnet0","id":"net0","mac":"52:54:00:4c:e3:86",' - '"bus":"pci.0","addr":"0x9"}') - self.vm.add_args('-device', '{"driver":"qemu-xhci","p2":15,"p3":15,' - '"id":"usb","bus":"pci.0","addr":"0x2"}') - self.vm.add_args('-device', '{"driver":"virtio-scsi-pci","id":"scsi0"' - ',"bus":"pci.0","addr":"0x3"}') - self.vm.add_args('-device', '{"driver":"virtio-serial-pci","id":' - '"virtio-serial0","bus":"pci.0","addr":"0x4"}') - self.vm.add_args('-device', '{"driver":"scsi-cd","bus":"scsi0.0"' - ',"channel":0,"scsi-id":0,"lun":0,"device_id":' - '"drive-scsi0-0-0-0","id":"scsi0-0-0-0"}') - self.vm.add_args('-device', '{"driver":"virtio-balloon-pci",' - '"id":"balloon0","bus":"pci.0","addr":"0x6"}') - self.vm.add_args('-audiodev', '{"id":"audio1","driver":"none"}') - self.vm.add_args('-device', '{"driver":"usb-tablet","id":"input0"' - ',"bus":"usb.0","port":"1"}') - self.vm.add_args('-device', '{"driver":"usb-kbd","id":"input1"' - ',"bus":"usb.0","port":"2"}') - self.vm.add_args('-device', '{"driver":"VGA","id":"video0",' - '"vgamem_mb":16,"bus":"pci.0","addr":"0x7"}') - self.vm.add_args('-object', '{"qom-type":"rng-random","id":"objrng0"' - ',"filename":"/dev/urandom"}', - '-device', '{"driver":"virtio-rng-pci","rng":"objrng0"' - ',"id":"rng0","bus":"pci.0","addr":"0x8"}') - self.vm.add_args('-object', '{"qom-type":"cryptodev-backend-builtin",' - '"id":"objcrypto0","queues":1}', - '-device', '{"driver":"virtio-crypto-pci",' - '"cryptodev":"objcrypto0","id":"crypto0","bus"' - ':"pci.0","addr":"0xa"}') - self.vm.add_args('-device', '{"driver":"spapr-pci-host-bridge"' - ',"index":1,"id":"pci.1"}') - self.vm.add_args('-device', '{"driver":"spapr-vscsi","id":"scsi1"' - ',"reg":12288}') - self.vm.add_args('-m', '2G,slots=32,maxmem=4G', - '-object', 'memory-backend-ram,id=ram1,size=1G', - '-device', 'pc-dimm,id=dimm1,memdev=ram1') - - # Create a temporary qcow2 and launch the test-case - with tempfile.NamedTemporaryFile(prefix=prefix, - suffix='.qcow2') as qcow2: - process.run(self.qemu_img + ' create -f qcow2 ' + - qcow2.name + ' 1G') - - self.vm.add_args('-drive', 'file=' + qcow2.name + - ',format=qcow2,if=none,id=' - 'drive-virtio-disk1', - '-device', 'virtio-blk-pci,bus=pci.0,' - 'addr=0xb,drive=drive-virtio-disk1,id=virtio-disk1' - ',bootindex=2') - self.common_tuxrun(csums=sums, drive="scsi-hd") - - # - # The tests themselves. The configuration is derived from how - # tuxrun invokes qemu (with minor tweaks like using -blockdev - # consistently). The tuxrun equivalent is something like: - # - # tuxrun --device qemu-{ARCH} \ - # --kernel https://storage.tuxboot.com/{TUXBOOT}/{IMAGE} - # - - def test_arm64(self): - """ - :avocado: tags=arch:aarch64 - :avocado: tags=cpu:cortex-a57 - :avocado: tags=machine:virt - :avocado: tags=tuxboot:arm64 - :avocado: tags=console:ttyAMA0 - :avocado: tags=shutdown:nowait - """ - sums = {"Image" : - "ce95a7101a5fecebe0fe630deee6bd97b32ba41bc8754090e9ad8961ea8674c7", - "rootfs.ext4.zst" : - "bbd5ed4b9c7d3f4ca19ba71a323a843c6b585e880115df3b7765769dbd9dd061"} - self.common_tuxrun(csums=sums) - - def test_arm64be(self): - """ - :avocado: tags=arch:aarch64 - :avocado: tags=cpu:cortex-a57 - :avocado: tags=endian:big - :avocado: tags=machine:virt - :avocado: tags=tuxboot:arm64be - :avocado: tags=console:ttyAMA0 - :avocado: tags=shutdown:nowait - """ - sums = { "Image" : - "e0df4425eb2cd9ea9a283e808037f805641c65d8fcecc8f6407d8f4f339561b4", - "rootfs.ext4.zst" : - "e6ffd8813c8a335bc15728f2835f90539c84be7f8f5f691a8b01451b47fb4bd7"} - self.common_tuxrun(csums=sums) - - def test_armv5(self): - """ - :avocado: tags=arch:arm - :avocado: tags=cpu:arm926 - :avocado: tags=machine:versatilepb - :avocado: tags=tuxboot:armv5 - :avocado: tags=image:zImage - :avocado: tags=console:ttyAMA0 - :avocado: tags=shutdown:nowait - """ - sums = { "rootfs.ext4.zst" : - "17177afa74e7294da0642861f08c88ca3c836764299a54bf6d1ce276cb9712a5", - "versatile-pb.dtb" : - "0bc0c0b0858cefd3c32b385c0d66d97142ded29472a496f4f490e42fc7615b25", - "zImage" : - "c95af2f27647c12265d75e9df44c22ff5228c59855f54aaa70f41ec2842e3a4d" } - - self.common_tuxrun(csums=sums, - drive="virtio-blk-pci", - dt="versatile-pb.dtb") - - def test_armv7(self): - """ - :avocado: tags=arch:arm - :avocado: tags=cpu:cortex-a15 - :avocado: tags=machine:virt - :avocado: tags=tuxboot:armv7 - :avocado: tags=image:zImage - :avocado: tags=console:ttyAMA0 - :avocado: tags=shutdown:nowait - """ - sums = { "rootfs.ext4.zst" : - "ab1fbbeaddda1ffdd45c9405a28cd5370c20f23a7cbc809cc90dc9f243a8eb5a", - "zImage" : - "4c7a22e9f15875bec06bd2a29d822496571eb297d4f22694099ffcdb19077572" } - - self.common_tuxrun(csums=sums) - - def test_armv7be(self): - """ - :avocado: tags=arch:arm - :avocado: tags=cpu:cortex-a15 - :avocado: tags=endian:big - :avocado: tags=machine:virt - :avocado: tags=tuxboot:armv7be - :avocado: tags=image:zImage - :avocado: tags=console:ttyAMA0 - :avocado: tags=shutdown:nowait - """ - sums = {"rootfs.ext4.zst" : - "42ed46dd2d59986206c5b1f6cf35eab58fe3fd20c96b41aaa16b32f3f90a9835", - "zImage" : - "7facc62082b57af12015b08f7fdbaf2f123ba07a478367853ae12b219afc9f2f" } - - self.common_tuxrun(csums=sums) - - def test_i386(self): - """ - :avocado: tags=arch:i386 - :avocado: tags=cpu:coreduo - :avocado: tags=machine:q35 - :avocado: tags=tuxboot:i386 - :avocado: tags=image:bzImage - :avocado: tags=shutdown:nowait - """ - sums = {"bzImage" : - "a3e5b32a354729e65910f5a1ffcda7c14a6c12a55e8213fb86e277f1b76ed956", - "rootfs.ext4.zst" : - "f15e66b2bf673a210ec2a4b2e744a80530b36289e04f5388aab812b97f69754a" } - - self.common_tuxrun(csums=sums, drive="virtio-blk-pci") - - def test_mips32(self): - """ - :avocado: tags=arch:mips - :avocado: tags=machine:malta - :avocado: tags=cpu:mips32r6-generic - :avocado: tags=endian:big - :avocado: tags=tuxboot:mips32 - :avocado: tags=image:vmlinux - :avocado: tags=root:sda - :avocado: tags=shutdown:nowait - """ - sums = { "rootfs.ext4.zst" : - "fc3da0b4c2f38d74c6d705123bb0f633c76ed953128f9d0859378c328a6d11a0", - "vmlinux" : - "bfd2172f8b17fb32970ca0c8c58f59c5a4ca38aa5855d920be3a69b5d16e52f0" } - - self.common_tuxrun(csums=sums, drive="driver=ide-hd,bus=ide.0,unit=0") - - def test_mips32el(self): - """ - :avocado: tags=arch:mipsel - :avocado: tags=machine:malta - :avocado: tags=cpu:mips32r6-generic - :avocado: tags=tuxboot:mips32el - :avocado: tags=image:vmlinux - :avocado: tags=root:sda - :avocado: tags=shutdown:nowait - """ - sums = { "rootfs.ext4.zst" : - "e799768e289fd69209c21f4dacffa11baea7543d5db101e8ce27e3bc2c41d90e", - "vmlinux" : - "8573867c68a8443db8de6d08bb33fb291c189ca2ca671471d3973a3e712096a3" } - - self.common_tuxrun(csums=sums, drive="driver=ide-hd,bus=ide.0,unit=0") - - def test_mips64(self): - """ - :avocado: tags=arch:mips64 - :avocado: tags=machine:malta - :avocado: tags=tuxboot:mips64 - :avocado: tags=endian:big - :avocado: tags=image:vmlinux - :avocado: tags=root:sda - :avocado: tags=shutdown:nowait - """ - sums = { "rootfs.ext4.zst" : - "69d91eeb04df3d8d172922c6993bb37d4deeb6496def75d8580f6f9de3e431da", - "vmlinux" : - "09010e51e4b8bcbbd2494786ffb48eca78f228e96e5c5438344b0eac4029dc61" } - - self.common_tuxrun(csums=sums, drive="driver=ide-hd,bus=ide.0,unit=0") - - def test_mips64el(self): - """ - :avocado: tags=arch:mips64el - :avocado: tags=machine:malta - :avocado: tags=tuxboot:mips64el - :avocado: tags=image:vmlinux - :avocado: tags=root:sda - :avocado: tags=shutdown:nowait - """ - sums = { "rootfs.ext4.zst" : - "fba585368f5915b1498ed081863474b2d7ec4e97cdd46d21bdcb2f9698f83de4", - "vmlinux" : - "d4e08965e2155c4cccce7c5f34d18fe34c636cda2f2c9844387d614950155266" } - - self.common_tuxrun(csums=sums, drive="driver=ide-hd,bus=ide.0,unit=0") - - def test_ppc32(self): - """ - :avocado: tags=arch:ppc - :avocado: tags=machine:ppce500 - :avocado: tags=cpu:e500mc - :avocado: tags=tuxboot:ppc32 - :avocado: tags=image:uImage - :avocado: tags=shutdown:nowait - """ - sums = { "rootfs.ext4.zst" : - "8885b9d999cc24d679542a02e9b6aaf48f718f2050ece6b8347074b6ee41dd09", - "uImage" : - "1a68f74b860fda022fb12e03c5efece8c2b8b590d96cca37a8481a3ae0b3f81f" } - - self.common_tuxrun(csums=sums, drive="virtio-blk-pci") - - def test_ppc64(self): - """ - :avocado: tags=arch:ppc64 - :avocado: tags=machine:pseries - :avocado: tags=cpu:POWER10 - :avocado: tags=endian:big - :avocado: tags=console:hvc0 - :avocado: tags=tuxboot:ppc64 - :avocado: tags=image:vmlinux - :avocado: tags=extradev:driver=spapr-vscsi - :avocado: tags=root:sda - """ - sums = { "rootfs.ext4.zst" : - "1d953e81a4379e537fc8e41e05a0a59d9b453eef97aa03d47866c6c45b00bdff", - "vmlinux" : - "f22a9b9e924174a4c199f4c7e5d91a2339fcfe51c6eafd0907dc3e09b64ab728" } - self.ppc64_common_tuxrun(sums, prefix='tuxrun_ppc64_') - - def test_ppc64le(self): - """ - :avocado: tags=arch:ppc64 - :avocado: tags=machine:pseries - :avocado: tags=cpu:POWER10 - :avocado: tags=console:hvc0 - :avocado: tags=tuxboot:ppc64le - :avocado: tags=image:vmlinux - :avocado: tags=extradev:driver=spapr-vscsi - :avocado: tags=root:sda - """ - sums = { "rootfs.ext4.zst" : - "b442678c93fb8abe1f7d3bfa20556488de6b475c22c8fed363f42cf81a0a3906", - "vmlinux" : - "979eb61b445a010fb13e2b927126991f8ceef9c590fa2be0996c00e293e80cf2" } - self.ppc64_common_tuxrun(sums, prefix='tuxrun_ppc64le_') - - def test_riscv32(self): - """ - :avocado: tags=arch:riscv32 - :avocado: tags=machine:virt - :avocado: tags=tuxboot:riscv32 - """ - sums = { "Image" : - "89599407d7334de629a40e7ad6503c73670359eb5f5ae9d686353a3d6deccbd5", - "fw_jump.elf" : - "f2ef28a0b77826f79d085d3e4aa686f1159b315eff9099a37046b18936676985", - "rootfs.ext4.zst" : - "7168d296d0283238ea73cd5a775b3dd608e55e04c7b92b76ecce31bb13108cba" } - - self.common_tuxrun(csums=sums) - - def test_riscv64(self): - """ - :avocado: tags=arch:riscv64 - :avocado: tags=machine:virt - :avocado: tags=tuxboot:riscv64 - """ - sums = { "Image" : - "cd634badc65e52fb63465ec99e309c0de0369f0841b7d9486f9729e119bac25e", - "fw_jump.elf" : - "6e3373abcab4305fe151b564a4c71110d833c21f2c0a1753b7935459e36aedcf", - "rootfs.ext4.zst" : - "b18e3a3bdf27be03da0b285e84cb71bf09eca071c3a087b42884b6982ed679eb" } - - self.common_tuxrun(csums=sums) - - def test_riscv32_maxcpu(self): - """ - :avocado: tags=arch:riscv32 - :avocado: tags=machine:virt - :avocado: tags=cpu:max - :avocado: tags=tuxboot:riscv32 - """ - sums = { "Image" : - "89599407d7334de629a40e7ad6503c73670359eb5f5ae9d686353a3d6deccbd5", - "fw_jump.elf" : - "f2ef28a0b77826f79d085d3e4aa686f1159b315eff9099a37046b18936676985", - "rootfs.ext4.zst" : - "7168d296d0283238ea73cd5a775b3dd608e55e04c7b92b76ecce31bb13108cba" } - - self.common_tuxrun(csums=sums) - - def test_riscv64_maxcpu(self): - """ - :avocado: tags=arch:riscv64 - :avocado: tags=machine:virt - :avocado: tags=cpu:max - :avocado: tags=tuxboot:riscv64 - """ - sums = { "Image" : - "cd634badc65e52fb63465ec99e309c0de0369f0841b7d9486f9729e119bac25e", - "fw_jump.elf" : - "6e3373abcab4305fe151b564a4c71110d833c21f2c0a1753b7935459e36aedcf", - "rootfs.ext4.zst" : - "b18e3a3bdf27be03da0b285e84cb71bf09eca071c3a087b42884b6982ed679eb" } - - self.common_tuxrun(csums=sums) - - def test_s390(self): - """ - :avocado: tags=arch:s390x - :avocado: tags=endian:big - :avocado: tags=tuxboot:s390 - :avocado: tags=image:bzImage - :avocado: tags=shutdown:nowait - """ - sums = { "bzImage" : - "0414e98dd1c3dafff8496c9cd9c28a5f8d04553bb5ba37e906a812b48d442ef0", - "rootfs.ext4.zst" : - "88c37c32276677f873a25ab9ec6247895b8e3e6f8259134de2a616080b8ab3fc" } - - self.common_tuxrun(csums=sums, - drive="virtio-blk-ccw", - haltmsg="Requesting system halt") - - # Note: some segfaults caused by unaligned userspace access - @skipUnless(os.getenv('QEMU_TEST_FLAKY_TESTS'), 'Test is unstable on GitLab') - def test_sh4(self): - """ - :avocado: tags=arch:sh4 - :avocado: tags=machine:r2d - :avocado: tags=cpu:sh7785 - :avocado: tags=tuxboot:sh4 - :avocado: tags=image:zImage - :avocado: tags=root:sda - :avocado: tags=console:ttySC1 - :avocado: tags=flaky - """ - sums = { "rootfs.ext4.zst" : - "3592a7a3d5a641e8b9821449e77bc43c9904a56c30d45da0694349cfd86743fd", - "zImage" : - "29d9b2aba604a0f53a5dc3b5d0f2b8e35d497de1129f8ee5139eb6fdf0db692f" } - - # The test is currently too unstable to do much in userspace - # so we skip common_tuxrun and do a minimal boot and shutdown. - (kernel, disk, dtb) = self.fetch_tuxrun_assets(csums=sums) - - # the console comes on the second serial port - self.prepare_run(kernel, disk, - "driver=ide-hd,bus=ide.0,unit=0", - console_index=1) - self.vm.launch() - - self.wait_for_console_pattern("Welcome to TuxTest") - time.sleep(0.1) - exec_command(self, 'root') - time.sleep(0.1) - exec_command_and_wait_for_pattern(self, 'halt', - "reboot: System halted") - - def test_sparc64(self): - """ - :avocado: tags=arch:sparc64 - :avocado: tags=tuxboot:sparc64 - :avocado: tags=image:vmlinux - :avocado: tags=root:sda - :avocado: tags=shutdown:nowait - """ - - sums = { "rootfs.ext4.zst" : - "ad2f1dc436ab51583543d25d2c210cab478645d47078d30d129a66ab0e281d76", - "vmlinux" : - "e34313e4325ff21deaa3d38a502aa09a373ef62b9bd4d7f8f29388b688225c55" } - - self.common_tuxrun(csums=sums, drive="driver=ide-hd,bus=ide.0,unit=0") - - def test_x86_64(self): - """ - :avocado: tags=arch:x86_64 - :avocado: tags=machine:q35 - :avocado: tags=cpu:Nehalem - :avocado: tags=tuxboot:x86_64 - :avocado: tags=image:bzImage - :avocado: tags=root:sda - :avocado: tags=shutdown:nowait - """ - sums = { "bzImage" : - "2bc7480a669ee9b6b82500a236aba0c54233debe98cb968268fa230f52f03461", - "rootfs.ext4.zst" : - "b72ac729769b8f51c6dffb221113c9a063c774dbe1d66af30eb593c4e9999b4b" } - - self.common_tuxrun(csums=sums, - drive="driver=ide-hd,bus=ide.0,unit=0") diff --git a/tests/bench/benchmark-crypto-akcipher.c b/tests/bench/benchmark-crypto-akcipher.c index 5e68cb0..0a6e5db 100644 --- a/tests/bench/benchmark-crypto-akcipher.c +++ b/tests/bench/benchmark-crypto-akcipher.c @@ -16,19 +16,19 @@ #include "crypto/akcipher.h" #include "standard-headers/linux/virtio_crypto.h" -#include "test_akcipher_keys.inc" +#include "test_akcipher_keys.c.inc" static QCryptoAkCipher *create_rsa_akcipher(const uint8_t *priv_key, size_t keylen, - QCryptoRSAPaddingAlgorithm padding, - QCryptoHashAlgorithm hash) + QCryptoRSAPaddingAlgo padding, + QCryptoHashAlgo hash) { QCryptoAkCipherOptions opt; - opt.alg = QCRYPTO_AKCIPHER_ALG_RSA; + opt.alg = QCRYPTO_AK_CIPHER_ALGO_RSA; opt.u.rsa.padding_alg = padding; opt.u.rsa.hash_alg = hash; - return qcrypto_akcipher_new(&opt, QCRYPTO_AKCIPHER_KEY_TYPE_PRIVATE, + return qcrypto_akcipher_new(&opt, QCRYPTO_AK_CIPHER_KEY_TYPE_PRIVATE, priv_key, keylen, &error_abort); } @@ -39,8 +39,8 @@ static void test_rsa_speed(const uint8_t *priv_key, size_t keylen, #define SHA1_DGST_LEN 20 #define SIGN_TIMES 10000 #define VERIFY_TIMES 100000 -#define PADDING QCRYPTO_RSA_PADDING_ALG_PKCS1 -#define HASH QCRYPTO_HASH_ALG_SHA1 +#define PADDING QCRYPTO_RSA_PADDING_ALGO_PKCS1 +#define HASH QCRYPTO_HASH_ALGO_SHA1 g_autoptr(QCryptoAkCipher) rsa = create_rsa_akcipher(priv_key, keylen, PADDING, HASH); @@ -53,8 +53,8 @@ static void test_rsa_speed(const uint8_t *priv_key, size_t keylen, signature = g_new0(uint8_t, key_size / BYTE); g_test_message("benchmark rsa%zu (%s-%s) sign...", key_size, - QCryptoRSAPaddingAlgorithm_str(PADDING), - QCryptoHashAlgorithm_str(HASH)); + QCryptoRSAPaddingAlgo_str(PADDING), + QCryptoHashAlgo_str(HASH)); g_test_timer_start(); for (count = 0; count < SIGN_TIMES; ++count) { g_assert(qcrypto_akcipher_sign(rsa, dgst, SHA1_DGST_LEN, @@ -64,14 +64,14 @@ static void test_rsa_speed(const uint8_t *priv_key, size_t keylen, g_test_timer_elapsed(); g_test_message("rsa%zu (%s-%s) sign %zu times in %.2f seconds," " %.2f times/sec ", - key_size, QCryptoRSAPaddingAlgorithm_str(PADDING), - QCryptoHashAlgorithm_str(HASH), + key_size, QCryptoRSAPaddingAlgo_str(PADDING), + QCryptoHashAlgo_str(HASH), count, g_test_timer_last(), (double)count / g_test_timer_last()); g_test_message("benchmark rsa%zu (%s-%s) verification...", key_size, - QCryptoRSAPaddingAlgorithm_str(PADDING), - QCryptoHashAlgorithm_str(HASH)); + QCryptoRSAPaddingAlgo_str(PADDING), + QCryptoHashAlgo_str(HASH)); g_test_timer_start(); for (count = 0; count < VERIFY_TIMES; ++count) { g_assert(qcrypto_akcipher_verify(rsa, signature, key_size / BYTE, @@ -81,8 +81,8 @@ static void test_rsa_speed(const uint8_t *priv_key, size_t keylen, g_test_timer_elapsed(); g_test_message("rsa%zu (%s-%s) verify %zu times in %.2f seconds," " %.2f times/sec ", - key_size, QCryptoRSAPaddingAlgorithm_str(PADDING), - QCryptoHashAlgorithm_str(HASH), + key_size, QCryptoRSAPaddingAlgo_str(PADDING), + QCryptoHashAlgo_str(HASH), count, g_test_timer_last(), (double)count / g_test_timer_last()); } diff --git a/tests/bench/benchmark-crypto-cipher.c b/tests/bench/benchmark-crypto-cipher.c index c04f0a0..889a29b 100644 --- a/tests/bench/benchmark-crypto-cipher.c +++ b/tests/bench/benchmark-crypto-cipher.c @@ -17,7 +17,7 @@ static void test_cipher_speed(size_t chunk_size, QCryptoCipherMode mode, - QCryptoCipherAlgorithm alg) + QCryptoCipherAlgo alg) { QCryptoCipher *cipher; Error *err = NULL; @@ -71,7 +71,7 @@ static void test_cipher_speed(size_t chunk_size, g_test_timer_elapsed(); g_test_message("enc(%s-%s) chunk %zu bytes %.2f MB/sec ", - QCryptoCipherAlgorithm_str(alg), + QCryptoCipherAlgo_str(alg), QCryptoCipherMode_str(mode), chunk_size, (double)total / MiB / g_test_timer_last()); @@ -88,7 +88,7 @@ static void test_cipher_speed(size_t chunk_size, g_test_timer_elapsed(); g_test_message("dec(%s-%s) chunk %zu bytes %.2f MB/sec ", - QCryptoCipherAlgorithm_str(alg), + QCryptoCipherAlgo_str(alg), QCryptoCipherMode_str(mode), chunk_size, (double)total / MiB / g_test_timer_last()); @@ -105,7 +105,7 @@ static void test_cipher_speed_ecb_aes_128(const void *opaque) size_t chunk_size = (size_t)opaque; test_cipher_speed(chunk_size, QCRYPTO_CIPHER_MODE_ECB, - QCRYPTO_CIPHER_ALG_AES_128); + QCRYPTO_CIPHER_ALGO_AES_128); } static void test_cipher_speed_ecb_aes_256(const void *opaque) @@ -113,7 +113,7 @@ static void test_cipher_speed_ecb_aes_256(const void *opaque) size_t chunk_size = (size_t)opaque; test_cipher_speed(chunk_size, QCRYPTO_CIPHER_MODE_ECB, - QCRYPTO_CIPHER_ALG_AES_256); + QCRYPTO_CIPHER_ALGO_AES_256); } static void test_cipher_speed_cbc_aes_128(const void *opaque) @@ -121,7 +121,7 @@ static void test_cipher_speed_cbc_aes_128(const void *opaque) size_t chunk_size = (size_t)opaque; test_cipher_speed(chunk_size, QCRYPTO_CIPHER_MODE_CBC, - QCRYPTO_CIPHER_ALG_AES_128); + QCRYPTO_CIPHER_ALGO_AES_128); } static void test_cipher_speed_cbc_aes_256(const void *opaque) @@ -129,7 +129,7 @@ static void test_cipher_speed_cbc_aes_256(const void *opaque) size_t chunk_size = (size_t)opaque; test_cipher_speed(chunk_size, QCRYPTO_CIPHER_MODE_CBC, - QCRYPTO_CIPHER_ALG_AES_256); + QCRYPTO_CIPHER_ALGO_AES_256); } static void test_cipher_speed_ctr_aes_128(const void *opaque) @@ -137,7 +137,7 @@ static void test_cipher_speed_ctr_aes_128(const void *opaque) size_t chunk_size = (size_t)opaque; test_cipher_speed(chunk_size, QCRYPTO_CIPHER_MODE_CTR, - QCRYPTO_CIPHER_ALG_AES_128); + QCRYPTO_CIPHER_ALGO_AES_128); } static void test_cipher_speed_ctr_aes_256(const void *opaque) @@ -145,7 +145,7 @@ static void test_cipher_speed_ctr_aes_256(const void *opaque) size_t chunk_size = (size_t)opaque; test_cipher_speed(chunk_size, QCRYPTO_CIPHER_MODE_CTR, - QCRYPTO_CIPHER_ALG_AES_256); + QCRYPTO_CIPHER_ALGO_AES_256); } static void test_cipher_speed_xts_aes_128(const void *opaque) @@ -153,7 +153,7 @@ static void test_cipher_speed_xts_aes_128(const void *opaque) size_t chunk_size = (size_t)opaque; test_cipher_speed(chunk_size, QCRYPTO_CIPHER_MODE_XTS, - QCRYPTO_CIPHER_ALG_AES_128); + QCRYPTO_CIPHER_ALGO_AES_128); } static void test_cipher_speed_xts_aes_256(const void *opaque) @@ -161,7 +161,7 @@ static void test_cipher_speed_xts_aes_256(const void *opaque) size_t chunk_size = (size_t)opaque; test_cipher_speed(chunk_size, QCRYPTO_CIPHER_MODE_XTS, - QCRYPTO_CIPHER_ALG_AES_256); + QCRYPTO_CIPHER_ALGO_AES_256); } diff --git a/tests/bench/benchmark-crypto-hash.c b/tests/bench/benchmark-crypto-hash.c index 927b00b..252098a 100644 --- a/tests/bench/benchmark-crypto-hash.c +++ b/tests/bench/benchmark-crypto-hash.c @@ -17,7 +17,7 @@ typedef struct QCryptoHashOpts { size_t chunk_size; - QCryptoHashAlgorithm alg; + QCryptoHashAlgo alg; } QCryptoHashOpts; static void test_hash_speed(const void *opaque) @@ -49,7 +49,7 @@ static void test_hash_speed(const void *opaque) g_test_timer_elapsed(); g_test_message("hash(%s): chunk %zu bytes %.2f MB/sec", - QCryptoHashAlgorithm_str(opts->alg), + QCryptoHashAlgo_str(opts->alg), opts->chunk_size, total / g_test_timer_last()); g_free(out); @@ -65,14 +65,14 @@ int main(int argc, char **argv) #define TEST_ONE(a, c) \ QCryptoHashOpts opts ## a ## c = { \ - .alg = QCRYPTO_HASH_ALG_ ## a, .chunk_size = c, \ + .alg = QCRYPTO_HASH_ALGO_ ## a, .chunk_size = c, \ }; \ memset(name, 0 , sizeof(name)); \ snprintf(name, sizeof(name), \ "/crypto/benchmark/hash/%s/bufsize-%d", \ - QCryptoHashAlgorithm_str(QCRYPTO_HASH_ALG_ ## a), \ + QCryptoHashAlgo_str(QCRYPTO_HASH_ALGO_ ## a), \ c); \ - if (qcrypto_hash_supports(QCRYPTO_HASH_ALG_ ## a)) \ + if (qcrypto_hash_supports(QCRYPTO_HASH_ALGO_ ## a)) \ g_test_add_data_func(name, \ &opts ## a ## c, \ test_hash_speed); diff --git a/tests/bench/benchmark-crypto-hmac.c b/tests/bench/benchmark-crypto-hmac.c index 5cca636..d51de98 100644 --- a/tests/bench/benchmark-crypto-hmac.c +++ b/tests/bench/benchmark-crypto-hmac.c @@ -28,7 +28,7 @@ static void test_hmac_speed(const void *opaque) Error *err = NULL; int ret; - if (!qcrypto_hmac_supports(QCRYPTO_HASH_ALG_SHA256)) { + if (!qcrypto_hmac_supports(QCRYPTO_HASH_ALGO_SHA256)) { return; } @@ -40,7 +40,7 @@ static void test_hmac_speed(const void *opaque) g_test_timer_start(); do { - hmac = qcrypto_hmac_new(QCRYPTO_HASH_ALG_SHA256, + hmac = qcrypto_hmac_new(QCRYPTO_HASH_ALGO_SHA256, (const uint8_t *)KEY, strlen(KEY), &err); g_assert(err == NULL); g_assert(hmac != NULL); @@ -56,7 +56,7 @@ static void test_hmac_speed(const void *opaque) total /= MiB; g_test_message("hmac(%s): chunk %zu bytes %.2f MB/sec", - QCryptoHashAlgorithm_str(QCRYPTO_HASH_ALG_SHA256), + QCryptoHashAlgo_str(QCRYPTO_HASH_ALGO_SHA256), chunk_size, total / g_test_timer_last()); g_free(out); diff --git a/tests/bench/test_akcipher_keys.inc b/tests/bench/test_akcipher_keys.c.inc index df3eccb..df3eccb 100644 --- a/tests/bench/test_akcipher_keys.inc +++ b/tests/bench/test_akcipher_keys.c.inc diff --git a/tests/data/acpi/aarch64/virt/APIC.its_off b/tests/data/acpi/aarch64/virt/APIC.its_off Binary files differnew file mode 100644 index 0000000..6130cb7 --- /dev/null +++ b/tests/data/acpi/aarch64/virt/APIC.its_off diff --git a/tests/data/acpi/aarch64/virt/IORT.its_off b/tests/data/acpi/aarch64/virt/IORT.its_off Binary files differnew file mode 100644 index 0000000..c10da4e --- /dev/null +++ b/tests/data/acpi/aarch64/virt/IORT.its_off diff --git a/tests/data/acpi/aarch64/virt/SSDT.memhp b/tests/data/acpi/aarch64/virt/SSDT.memhp Binary files differindex fb3dcde..1deb1d2 100644 --- a/tests/data/acpi/aarch64/virt/SSDT.memhp +++ b/tests/data/acpi/aarch64/virt/SSDT.memhp diff --git a/tests/data/acpi/disassemle-aml.sh b/tests/data/acpi/disassemle-aml.sh index 253b762..89561d2 100755 --- a/tests/data/acpi/disassemle-aml.sh +++ b/tests/data/acpi/disassemle-aml.sh @@ -14,7 +14,7 @@ while getopts "o:" arg; do esac done -for machine in tests/data/acpi/* +for machine in tests/data/acpi/*/* do if [[ ! -d "$machine" ]]; then diff --git a/tests/data/acpi/riscv64/virt/RHCT b/tests/data/acpi/riscv64/virt/RHCT Binary files differindex 4f23173..13c8025 100644 --- a/tests/data/acpi/riscv64/virt/RHCT +++ b/tests/data/acpi/riscv64/virt/RHCT diff --git a/tests/data/acpi/riscv64/virt/SPCR b/tests/data/acpi/riscv64/virt/SPCR Binary files differindex 4da9daf..09617f8 100644 --- a/tests/data/acpi/riscv64/virt/SPCR +++ b/tests/data/acpi/riscv64/virt/SPCR diff --git a/tests/data/acpi/riscv64/virt/SRAT.numamem b/tests/data/acpi/riscv64/virt/SRAT.numamem Binary files differnew file mode 100644 index 0000000..2b64673 --- /dev/null +++ b/tests/data/acpi/riscv64/virt/SRAT.numamem diff --git a/tests/data/acpi/x86/pc/DSDT b/tests/data/acpi/x86/pc/DSDT Binary files differindex c93ad6b..4beb519 100644 --- a/tests/data/acpi/x86/pc/DSDT +++ b/tests/data/acpi/x86/pc/DSDT diff --git a/tests/data/acpi/x86/pc/DSDT.acpierst b/tests/data/acpi/x86/pc/DSDT.acpierst Binary files differindex f643fa2..abda686 100644 --- a/tests/data/acpi/x86/pc/DSDT.acpierst +++ b/tests/data/acpi/x86/pc/DSDT.acpierst diff --git a/tests/data/acpi/x86/pc/DSDT.acpihmat b/tests/data/acpi/x86/pc/DSDT.acpihmat Binary files differindex 9d3695f..d081db2 100644 --- a/tests/data/acpi/x86/pc/DSDT.acpihmat +++ b/tests/data/acpi/x86/pc/DSDT.acpihmat diff --git a/tests/data/acpi/x86/pc/DSDT.bridge b/tests/data/acpi/x86/pc/DSDT.bridge Binary files differindex 840b45f..e16897d 100644 --- a/tests/data/acpi/x86/pc/DSDT.bridge +++ b/tests/data/acpi/x86/pc/DSDT.bridge diff --git a/tests/data/acpi/x86/pc/DSDT.cphp b/tests/data/acpi/x86/pc/DSDT.cphp Binary files differindex dbc0141..e95711c 100644 --- a/tests/data/acpi/x86/pc/DSDT.cphp +++ b/tests/data/acpi/x86/pc/DSDT.cphp diff --git a/tests/data/acpi/x86/pc/DSDT.dimmpxm b/tests/data/acpi/x86/pc/DSDT.dimmpxm Binary files differindex 1294f65..90ba66b 100644 --- a/tests/data/acpi/x86/pc/DSDT.dimmpxm +++ b/tests/data/acpi/x86/pc/DSDT.dimmpxm diff --git a/tests/data/acpi/x86/pc/DSDT.hpbridge b/tests/data/acpi/x86/pc/DSDT.hpbridge Binary files differindex 8012b5e..0eafe5f 100644 --- a/tests/data/acpi/x86/pc/DSDT.hpbridge +++ b/tests/data/acpi/x86/pc/DSDT.hpbridge diff --git a/tests/data/acpi/x86/pc/DSDT.hpbrroot b/tests/data/acpi/x86/pc/DSDT.hpbrroot Binary files differindex 4fa0c6f..077a4cc 100644 --- a/tests/data/acpi/x86/pc/DSDT.hpbrroot +++ b/tests/data/acpi/x86/pc/DSDT.hpbrroot diff --git a/tests/data/acpi/x86/pc/DSDT.ipmikcs b/tests/data/acpi/x86/pc/DSDT.ipmikcs Binary files differindex 0a891ba..8d465f0 100644 --- a/tests/data/acpi/x86/pc/DSDT.ipmikcs +++ b/tests/data/acpi/x86/pc/DSDT.ipmikcs diff --git a/tests/data/acpi/x86/pc/DSDT.memhp b/tests/data/acpi/x86/pc/DSDT.memhp Binary files differindex 9b442a6..e3b4975 100644 --- a/tests/data/acpi/x86/pc/DSDT.memhp +++ b/tests/data/acpi/x86/pc/DSDT.memhp diff --git a/tests/data/acpi/x86/pc/DSDT.nohpet b/tests/data/acpi/x86/pc/DSDT.nohpet Binary files differindex 1754c68..9e772c1 100644 --- a/tests/data/acpi/x86/pc/DSDT.nohpet +++ b/tests/data/acpi/x86/pc/DSDT.nohpet diff --git a/tests/data/acpi/x86/pc/DSDT.numamem b/tests/data/acpi/x86/pc/DSDT.numamem Binary files differindex 9fc731d..9bfbfc2 100644 --- a/tests/data/acpi/x86/pc/DSDT.numamem +++ b/tests/data/acpi/x86/pc/DSDT.numamem diff --git a/tests/data/acpi/x86/pc/DSDT.roothp b/tests/data/acpi/x86/pc/DSDT.roothp Binary files differindex e654c83..efbee6d 100644 --- a/tests/data/acpi/x86/pc/DSDT.roothp +++ b/tests/data/acpi/x86/pc/DSDT.roothp diff --git a/tests/data/acpi/x86/q35/APIC.acpihmat-generic-x b/tests/data/acpi/x86/q35/APIC.acpihmat-generic-x Binary files differnew file mode 100644 index 0000000..317ddb3 --- /dev/null +++ b/tests/data/acpi/x86/q35/APIC.acpihmat-generic-x diff --git a/tests/data/acpi/x86/q35/CEDT.acpihmat-generic-x b/tests/data/acpi/x86/q35/CEDT.acpihmat-generic-x Binary files differnew file mode 100644 index 0000000..31c9011 --- /dev/null +++ b/tests/data/acpi/x86/q35/CEDT.acpihmat-generic-x diff --git a/tests/data/acpi/x86/q35/DMAR.dmar b/tests/data/acpi/x86/q35/DMAR.dmar Binary files differindex 0dca6e6..0c05976 100644 --- a/tests/data/acpi/x86/q35/DMAR.dmar +++ b/tests/data/acpi/x86/q35/DMAR.dmar diff --git a/tests/data/acpi/x86/q35/DSDT b/tests/data/acpi/x86/q35/DSDT Binary files differindex fb89ae0..e5e8d1e 100644 --- a/tests/data/acpi/x86/q35/DSDT +++ b/tests/data/acpi/x86/q35/DSDT diff --git a/tests/data/acpi/x86/q35/DSDT.acpierst b/tests/data/acpi/x86/q35/DSDT.acpierst Binary files differindex 46fd254..072a3fe 100644 --- a/tests/data/acpi/x86/q35/DSDT.acpierst +++ b/tests/data/acpi/x86/q35/DSDT.acpierst diff --git a/tests/data/acpi/x86/q35/DSDT.acpihmat b/tests/data/acpi/x86/q35/DSDT.acpihmat Binary files differindex 61c5bd5..2a4f2fc 100644 --- a/tests/data/acpi/x86/q35/DSDT.acpihmat +++ b/tests/data/acpi/x86/q35/DSDT.acpihmat diff --git a/tests/data/acpi/x86/q35/DSDT.acpihmat-generic-x b/tests/data/acpi/x86/q35/DSDT.acpihmat-generic-x Binary files differnew file mode 100644 index 0000000..7911c05 --- /dev/null +++ b/tests/data/acpi/x86/q35/DSDT.acpihmat-generic-x diff --git a/tests/data/acpi/x86/q35/DSDT.acpihmat-noinitiator b/tests/data/acpi/x86/q35/DSDT.acpihmat-noinitiator Binary files differindex 3aaa2bb..580b4a4 100644 --- a/tests/data/acpi/x86/q35/DSDT.acpihmat-noinitiator +++ b/tests/data/acpi/x86/q35/DSDT.acpihmat-noinitiator diff --git a/tests/data/acpi/x86/q35/DSDT.applesmc b/tests/data/acpi/x86/q35/DSDT.applesmc Binary files differindex 944209a..5e8220e 100644 --- a/tests/data/acpi/x86/q35/DSDT.applesmc +++ b/tests/data/acpi/x86/q35/DSDT.applesmc diff --git a/tests/data/acpi/x86/q35/DSDT.bridge b/tests/data/acpi/x86/q35/DSDT.bridge Binary files differindex d9938db..ee03945 100644 --- a/tests/data/acpi/x86/q35/DSDT.bridge +++ b/tests/data/acpi/x86/q35/DSDT.bridge diff --git a/tests/data/acpi/x86/q35/DSDT.core-count b/tests/data/acpi/x86/q35/DSDT.core-count Binary files differindex a24b04c..7ebfcee 100644 --- a/tests/data/acpi/x86/q35/DSDT.core-count +++ b/tests/data/acpi/x86/q35/DSDT.core-count diff --git a/tests/data/acpi/x86/q35/DSDT.core-count2 b/tests/data/acpi/x86/q35/DSDT.core-count2 Binary files differindex 3a0cb8c..d039455 100644 --- a/tests/data/acpi/x86/q35/DSDT.core-count2 +++ b/tests/data/acpi/x86/q35/DSDT.core-count2 diff --git a/tests/data/acpi/x86/q35/DSDT.cphp b/tests/data/acpi/x86/q35/DSDT.cphp Binary files differindex 20955d0..a055c2e 100644 --- a/tests/data/acpi/x86/q35/DSDT.cphp +++ b/tests/data/acpi/x86/q35/DSDT.cphp diff --git a/tests/data/acpi/x86/q35/DSDT.cxl b/tests/data/acpi/x86/q35/DSDT.cxl Binary files differindex afcdc0d..2084354 100644 --- a/tests/data/acpi/x86/q35/DSDT.cxl +++ b/tests/data/acpi/x86/q35/DSDT.cxl diff --git a/tests/data/acpi/x86/q35/DSDT.dimmpxm b/tests/data/acpi/x86/q35/DSDT.dimmpxm Binary files differindex 228374b..664e926 100644 --- a/tests/data/acpi/x86/q35/DSDT.dimmpxm +++ b/tests/data/acpi/x86/q35/DSDT.dimmpxm diff --git a/tests/data/acpi/x86/q35/DSDT.ipmibt b/tests/data/acpi/x86/q35/DSDT.ipmibt Binary files differindex 45f911a..4066a76 100644 --- a/tests/data/acpi/x86/q35/DSDT.ipmibt +++ b/tests/data/acpi/x86/q35/DSDT.ipmibt diff --git a/tests/data/acpi/x86/q35/DSDT.ipmismbus b/tests/data/acpi/x86/q35/DSDT.ipmismbus Binary files differindex e5d6811..6d0b6b9 100644 --- a/tests/data/acpi/x86/q35/DSDT.ipmismbus +++ b/tests/data/acpi/x86/q35/DSDT.ipmismbus diff --git a/tests/data/acpi/x86/q35/DSDT.ivrs b/tests/data/acpi/x86/q35/DSDT.ivrs Binary files differindex 46fd254..072a3fe 100644 --- a/tests/data/acpi/x86/q35/DSDT.ivrs +++ b/tests/data/acpi/x86/q35/DSDT.ivrs diff --git a/tests/data/acpi/x86/q35/DSDT.memhp b/tests/data/acpi/x86/q35/DSDT.memhp Binary files differindex 5ce0811..4f2f9bc 100644 --- a/tests/data/acpi/x86/q35/DSDT.memhp +++ b/tests/data/acpi/x86/q35/DSDT.memhp diff --git a/tests/data/acpi/x86/q35/DSDT.mmio64 b/tests/data/acpi/x86/q35/DSDT.mmio64 Binary files differindex bdf36c4..0fb6aab 100644 --- a/tests/data/acpi/x86/q35/DSDT.mmio64 +++ b/tests/data/acpi/x86/q35/DSDT.mmio64 diff --git a/tests/data/acpi/x86/q35/DSDT.multi-bridge b/tests/data/acpi/x86/q35/DSDT.multi-bridge Binary files differindex 1db43a6..f6afa6d 100644 --- a/tests/data/acpi/x86/q35/DSDT.multi-bridge +++ b/tests/data/acpi/x86/q35/DSDT.multi-bridge diff --git a/tests/data/acpi/x86/q35/DSDT.noacpihp b/tests/data/acpi/x86/q35/DSDT.noacpihp Binary files differindex 8bc1688..9f7261d 100644 --- a/tests/data/acpi/x86/q35/DSDT.noacpihp +++ b/tests/data/acpi/x86/q35/DSDT.noacpihp diff --git a/tests/data/acpi/x86/q35/DSDT.nohpet b/tests/data/acpi/x86/q35/DSDT.nohpet Binary files differindex c13e45e..99ad629 100644 --- a/tests/data/acpi/x86/q35/DSDT.nohpet +++ b/tests/data/acpi/x86/q35/DSDT.nohpet diff --git a/tests/data/acpi/x86/q35/DSDT.numamem b/tests/data/acpi/x86/q35/DSDT.numamem Binary files differindex ba66694..fd1d8a7 100644 --- a/tests/data/acpi/x86/q35/DSDT.numamem +++ b/tests/data/acpi/x86/q35/DSDT.numamem diff --git a/tests/data/acpi/x86/q35/DSDT.pvpanic-isa b/tests/data/acpi/x86/q35/DSDT.pvpanic-isa Binary files differindex 6ad4287..89032fa 100644 --- a/tests/data/acpi/x86/q35/DSDT.pvpanic-isa +++ b/tests/data/acpi/x86/q35/DSDT.pvpanic-isa diff --git a/tests/data/acpi/x86/q35/DSDT.thread-count b/tests/data/acpi/x86/q35/DSDT.thread-count Binary files differindex a24b04c..7ebfcee 100644 --- a/tests/data/acpi/x86/q35/DSDT.thread-count +++ b/tests/data/acpi/x86/q35/DSDT.thread-count diff --git a/tests/data/acpi/x86/q35/DSDT.thread-count2 b/tests/data/acpi/x86/q35/DSDT.thread-count2 Binary files differindex 3a0cb8c..d039455 100644 --- a/tests/data/acpi/x86/q35/DSDT.thread-count2 +++ b/tests/data/acpi/x86/q35/DSDT.thread-count2 diff --git a/tests/data/acpi/x86/q35/DSDT.tis.tpm12 b/tests/data/acpi/x86/q35/DSDT.tis.tpm12 Binary files differindex e381ce4..f2ed40c 100644 --- a/tests/data/acpi/x86/q35/DSDT.tis.tpm12 +++ b/tests/data/acpi/x86/q35/DSDT.tis.tpm12 diff --git a/tests/data/acpi/x86/q35/DSDT.tis.tpm2 b/tests/data/acpi/x86/q35/DSDT.tis.tpm2 Binary files differindex a092530..5c975d2 100644 --- a/tests/data/acpi/x86/q35/DSDT.tis.tpm2 +++ b/tests/data/acpi/x86/q35/DSDT.tis.tpm2 diff --git a/tests/data/acpi/x86/q35/DSDT.type4-count b/tests/data/acpi/x86/q35/DSDT.type4-count Binary files differindex edc2319..3194a82 100644 --- a/tests/data/acpi/x86/q35/DSDT.type4-count +++ b/tests/data/acpi/x86/q35/DSDT.type4-count diff --git a/tests/data/acpi/x86/q35/DSDT.viot b/tests/data/acpi/x86/q35/DSDT.viot Binary files differindex 64e81f5..129d43e 100644 --- a/tests/data/acpi/x86/q35/DSDT.viot +++ b/tests/data/acpi/x86/q35/DSDT.viot diff --git a/tests/data/acpi/x86/q35/DSDT.xapic b/tests/data/acpi/x86/q35/DSDT.xapic Binary files differindex d4acd85..b37ab59 100644 --- a/tests/data/acpi/x86/q35/DSDT.xapic +++ b/tests/data/acpi/x86/q35/DSDT.xapic diff --git a/tests/data/acpi/x86/q35/HMAT.acpihmat-generic-x b/tests/data/acpi/x86/q35/HMAT.acpihmat-generic-x Binary files differnew file mode 100644 index 0000000..0e5765f --- /dev/null +++ b/tests/data/acpi/x86/q35/HMAT.acpihmat-generic-x diff --git a/tests/data/acpi/x86/q35/SRAT.acpihmat-generic-x b/tests/data/acpi/x86/q35/SRAT.acpihmat-generic-x Binary files differnew file mode 100644 index 0000000..b45838a --- /dev/null +++ b/tests/data/acpi/x86/q35/SRAT.acpihmat-generic-x diff --git a/tests/data/qobject/qdict.txt b/tests/data/qobject/qdict.txt index e2edc88..888f343 100644 --- a/tests/data/qobject/qdict.txt +++ b/tests/data/qobject/qdict.txt @@ -3487,12 +3487,6 @@ cred-internals.h: 559 CREDITS: 603 crime.c: 2833 crime.h: 5271 -cris: 4096 -cris_defs_asm.h: 3805 -crisksyms.c: 472 -cris_supp_reg.h: 198 -crisv10.c: 129158 -crisv10.h: 4289 crm_regs.h: 1700 cr_pll.c: 4842 crt0_ram.S: 2152 diff --git a/tests/data/uefi-boot-images/bios-tables-test.loongarch64.iso.qcow2 b/tests/data/uefi-boot-images/bios-tables-test.loongarch64.iso.qcow2 Binary files differnew file mode 100644 index 0000000..18daee0 --- /dev/null +++ b/tests/data/uefi-boot-images/bios-tables-test.loongarch64.iso.qcow2 diff --git a/tests/docker/Makefile.include b/tests/docker/Makefile.include index 708e3a7..3959d8a 100644 --- a/tests/docker/Makefile.include +++ b/tests/docker/Makefile.include @@ -92,10 +92,10 @@ endif docker-image-alpine: NOUSER=1 debian-toolchain-run = \ - $(if $(NOCACHE), \ + $(if $(NOCACHE)$(NOFETCH), \ $(call quiet-command, \ $(DOCKER_SCRIPT) build -t qemu/$1 -f $< \ - $(if $V,,--quiet) --no-cache \ + $(if $V,,--quiet) $(if $(NOCACHE),--no-cache) \ --registry $(DOCKER_REGISTRY) --extra-files \ $(DOCKER_FILES_DIR)/$1.d/build-toolchain.sh, \ "BUILD", $1), \ @@ -117,7 +117,6 @@ docker-image-debian-microblaze-cross: $(DOCKER_FILES_DIR)/debian-toolchain.docke # These images may be good enough for building tests but not for test builds DOCKER_PARTIAL_IMAGES += debian-microblaze-cross DOCKER_PARTIAL_IMAGES += debian-xtensa-cross -DOCKER_PARTIAL_IMAGES += fedora-cris-cross # images that are only used to build other images DOCKER_VIRTUAL_IMAGES := debian-bootstrap debian-toolchain @@ -178,6 +177,7 @@ docker: @echo ' NETWORK=$$BACKEND Enable virtual network interface with $$BACKEND.' @echo ' NOUSER=1 Define to disable adding current user to containers passwd.' @echo ' NOCACHE=1 Ignore cache when build images.' + @echo ' NOFETCH=1 Do not fetch from the registry.' @echo ' EXECUTABLE=<path> Include executable in image.' @echo ' EXTRA_FILES="<path> [... <path>]"' @echo ' Include extra files in image.' @@ -185,8 +185,10 @@ docker: docker-help: docker +# Where QEMU caches build artefacts +DOCKER_QEMU_CACHE_DIR := $$HOME/.cache/qemu # Use a global constant ccache directory to speed up repetitive builds -DOCKER_CCACHE_DIR := $$HOME/.cache/qemu-docker-ccache +DOCKER_QEMU_CCACHE_DIR := DOCKER_QEMU_CACHE_DIR/docker-ccache # This rule if for directly running against an arbitrary docker target. # It is called by the expanded docker targets (e.g. make @@ -195,7 +197,7 @@ DOCKER_CCACHE_DIR := $$HOME/.cache/qemu-docker-ccache # For example: make docker-run TEST="test-quick" IMAGE="debian:arm64" EXECUTABLE=./aarch64-linux-user/qemu-aarch64 # docker-run: docker-qemu-src - @mkdir -p "$(DOCKER_CCACHE_DIR)" + @mkdir -p "$(DOCKER_QEMU_CCACHE_DIR)" @if test -z "$(IMAGE)" || test -z "$(TEST)"; \ then echo "Invalid target $(IMAGE)/$(TEST)"; exit 1; \ fi @@ -222,8 +224,8 @@ docker-run: docker-qemu-src -e V=$V -e J=$J -e DEBUG=$(DEBUG) \ -e SHOW_ENV=$(SHOW_ENV) \ $(if $(NOUSER),, \ - -e CCACHE_DIR=/var/tmp/ccache \ - -v $(DOCKER_CCACHE_DIR):/var/tmp/ccache:z \ + -v $(DOCKER_QEMU_CACHE_DIR):$(DOCKER_QEMU_CACHE_DIR) \ + -e CCACHE_DIR=$(DOCKER_QEMU_CCACHE_DIR) \ ) \ -v $$(readlink -e $(DOCKER_SRC_COPY)):/var/tmp/qemu:z$(COMMA)ro \ $(IMAGE) \ @@ -236,3 +238,6 @@ docker-image: ${DOCKER_IMAGES:%=docker-image-%} docker-clean: $(call quiet-command, $(DOCKER_SCRIPT) clean) + +# Overrides +docker-test-rust%: NETWORK=1 diff --git a/tests/docker/dockerfiles/alpine.docker b/tests/docker/dockerfiles/alpine.docker index 54b9721..bf3bd5a 100644 --- a/tests/docker/dockerfiles/alpine.docker +++ b/tests/docker/dockerfiles/alpine.docker @@ -1,10 +1,10 @@ # THIS FILE WAS AUTO-GENERATED # -# $ lcitool dockerfile --layers all alpine-319 qemu +# $ lcitool dockerfile --layers all alpine-321 qemu # # https://gitlab.com/libvirt/libvirt-ci -FROM docker.io/library/alpine:3.19 +FROM docker.io/library/alpine:3.21 RUN apk update && \ apk upgrade && \ @@ -45,6 +45,7 @@ RUN apk update && \ libaio-dev \ libbpf-dev \ libcap-ng-dev \ + libcbor-dev \ libdrm-dev \ libepoxy-dev \ libffi-dev \ @@ -90,6 +91,8 @@ RUN apk update && \ py3-yaml \ python3 \ rpm2cpio \ + rust \ + rust-bindgen \ samurai \ sdl2-dev \ sdl2_image-dev \ @@ -108,6 +111,7 @@ RUN apk update && \ vde2-dev \ virglrenderer-dev \ vte3-dev \ + vulkan-tools \ which \ xen-dev \ xorriso \ diff --git a/tests/docker/dockerfiles/centos9.docker b/tests/docker/dockerfiles/centos9.docker index 0256865..a942835 100644 --- a/tests/docker/dockerfiles/centos9.docker +++ b/tests/docker/dockerfiles/centos9.docker @@ -16,6 +16,7 @@ RUN dnf distro-sync -y && \ alsa-lib-devel \ bash \ bc \ + bindgen-cli \ bison \ brlapi-devel \ bzip2 \ @@ -102,6 +103,7 @@ RUN dnf distro-sync -y && \ python3-sphinx_rtd_theme \ python3-tomli \ rdma-core-devel \ + rust \ sed \ snappy-devel \ socat \ @@ -113,6 +115,7 @@ RUN dnf distro-sync -y && \ usbredir-devel \ util-linux \ vte291-devel \ + vulkan-tools \ which \ xorriso \ zlib-devel \ diff --git a/tests/docker/dockerfiles/debian-amd64-cross.docker b/tests/docker/dockerfiles/debian-amd64-cross.docker index 136c3a7..081f3e0 100644 --- a/tests/docker/dockerfiles/debian-amd64-cross.docker +++ b/tests/docker/dockerfiles/debian-amd64-cross.docker @@ -13,6 +13,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ eatmydata apt-get install --no-install-recommends -y \ bash \ bc \ + bindgen \ bison \ bsdextrautils \ bzip2 \ @@ -30,10 +31,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ git \ hostname \ libglib2.0-dev \ - libgtk-vnc-2.0-dev \ - libpcre2-dev \ - libsndio-dev \ - libspice-protocol-dev \ llvm \ locales \ make \ @@ -53,6 +50,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ python3-venv \ python3-yaml \ rpm2cpio \ + rustc-web \ sed \ socat \ sparse \ @@ -60,6 +58,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ tar \ tesseract-ocr \ tesseract-ocr-eng \ + vulkan-tools \ xorriso \ zstd && \ eatmydata apt-get autoremove -y && \ @@ -92,6 +91,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libcacard-dev:amd64 \ libcap-ng-dev:amd64 \ libcapstone-dev:amd64 \ + libcbor-dev:amd64 \ libcmocka-dev:amd64 \ libcurl4-gnutls-dev:amd64 \ libdaxctl-dev:amd64 \ @@ -106,6 +106,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libglusterfs-dev:amd64 \ libgnutls28-dev:amd64 \ libgtk-3-dev:amd64 \ + libgtk-vnc-2.0-dev:amd64 \ libibverbs-dev:amd64 \ libiscsi-dev:amd64 \ libjemalloc-dev:amd64 \ @@ -117,6 +118,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libnfs-dev:amd64 \ libnuma-dev:amd64 \ libpam0g-dev:amd64 \ + libpcre2-dev:amd64 \ libpipewire-0.3-dev:amd64 \ libpixman-1-dev:amd64 \ libpmem-dev:amd64 \ @@ -131,8 +133,10 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libselinux1-dev:amd64 \ libslirp-dev:amd64 \ libsnappy-dev:amd64 \ + libsndio-dev:amd64 \ + libspice-protocol-dev:amd64 \ libspice-server-dev:amd64 \ - libssh-gcrypt-dev:amd64 \ + libssh-dev:amd64 \ libsystemd-dev:amd64 \ libtasn1-6-dev:amd64 \ libubsan1:amd64 \ @@ -170,6 +174,7 @@ endian = 'little'\n" > /usr/local/share/meson/cross/x86_64-linux-gnu && \ ENV ABI "x86_64-linux-gnu" ENV MESON_OPTS "--cross-file=x86_64-linux-gnu" +ENV RUST_TARGET "x86_64-unknown-linux-gnu" ENV QEMU_CONFIGURE_OPTS --cross-prefix=x86_64-linux-gnu- ENV DEF_TARGET_LIST x86_64-softmmu,x86_64-linux-user,i386-softmmu,i386-linux-user # As a final step configure the user (if env is defined) diff --git a/tests/docker/dockerfiles/debian-arm64-cross.docker b/tests/docker/dockerfiles/debian-arm64-cross.docker index 233f6ee..91c555a 100644 --- a/tests/docker/dockerfiles/debian-arm64-cross.docker +++ b/tests/docker/dockerfiles/debian-arm64-cross.docker @@ -13,6 +13,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ eatmydata apt-get install --no-install-recommends -y \ bash \ bc \ + bindgen \ bison \ bsdextrautils \ bzip2 \ @@ -30,10 +31,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ git \ hostname \ libglib2.0-dev \ - libgtk-vnc-2.0-dev \ - libpcre2-dev \ - libsndio-dev \ - libspice-protocol-dev \ llvm \ locales \ make \ @@ -53,6 +50,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ python3-venv \ python3-yaml \ rpm2cpio \ + rustc-web \ sed \ socat \ sparse \ @@ -60,6 +58,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ tar \ tesseract-ocr \ tesseract-ocr-eng \ + vulkan-tools \ xorriso \ zstd && \ eatmydata apt-get autoremove -y && \ @@ -92,6 +91,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libcacard-dev:arm64 \ libcap-ng-dev:arm64 \ libcapstone-dev:arm64 \ + libcbor-dev:arm64 \ libcmocka-dev:arm64 \ libcurl4-gnutls-dev:arm64 \ libdaxctl-dev:arm64 \ @@ -106,6 +106,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libglusterfs-dev:arm64 \ libgnutls28-dev:arm64 \ libgtk-3-dev:arm64 \ + libgtk-vnc-2.0-dev:arm64 \ libibverbs-dev:arm64 \ libiscsi-dev:arm64 \ libjemalloc-dev:arm64 \ @@ -117,6 +118,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libnfs-dev:arm64 \ libnuma-dev:arm64 \ libpam0g-dev:arm64 \ + libpcre2-dev:arm64 \ libpipewire-0.3-dev:arm64 \ libpixman-1-dev:arm64 \ libpng-dev:arm64 \ @@ -130,8 +132,10 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libselinux1-dev:arm64 \ libslirp-dev:arm64 \ libsnappy-dev:arm64 \ + libsndio-dev:arm64 \ + libspice-protocol-dev:arm64 \ libspice-server-dev:arm64 \ - libssh-gcrypt-dev:arm64 \ + libssh-dev:arm64 \ libsystemd-dev:arm64 \ libtasn1-6-dev:arm64 \ libubsan1:arm64 \ @@ -169,6 +173,7 @@ endian = 'little'\n" > /usr/local/share/meson/cross/aarch64-linux-gnu && \ ENV ABI "aarch64-linux-gnu" ENV MESON_OPTS "--cross-file=aarch64-linux-gnu" +ENV RUST_TARGET "aarch64-unknown-linux-gnu" ENV QEMU_CONFIGURE_OPTS --cross-prefix=aarch64-linux-gnu- ENV DEF_TARGET_LIST aarch64-softmmu,aarch64-linux-user # As a final step configure the user (if env is defined) diff --git a/tests/docker/dockerfiles/debian-armel-cross.docker b/tests/docker/dockerfiles/debian-armel-cross.docker deleted file mode 100644 index 8476fc8..0000000 --- a/tests/docker/dockerfiles/debian-armel-cross.docker +++ /dev/null @@ -1,179 +0,0 @@ -# THIS FILE WAS AUTO-GENERATED -# -# $ lcitool dockerfile --layers all --cross-arch armv6l debian-11 qemu -# -# https://gitlab.com/libvirt/libvirt-ci - -FROM docker.io/library/debian:11-slim - -RUN export DEBIAN_FRONTEND=noninteractive && \ - apt-get update && \ - apt-get install -y eatmydata && \ - eatmydata apt-get dist-upgrade -y && \ - eatmydata apt-get install --no-install-recommends -y \ - bash \ - bc \ - bison \ - bsdextrautils \ - bzip2 \ - ca-certificates \ - ccache \ - dbus \ - debianutils \ - diffutils \ - exuberant-ctags \ - findutils \ - flex \ - gcc \ - gcovr \ - gettext \ - git \ - hostname \ - libglib2.0-dev \ - libgtk-vnc-2.0-dev \ - libpcre2-dev \ - libsndio-dev \ - libspice-protocol-dev \ - llvm \ - locales \ - make \ - meson \ - mtools \ - ncat \ - ninja-build \ - openssh-client \ - pkgconf \ - python3 \ - python3-numpy \ - python3-opencv \ - python3-pillow \ - python3-pip \ - python3-setuptools \ - python3-sphinx \ - python3-sphinx-rtd-theme \ - python3-venv \ - python3-wheel \ - python3-yaml \ - rpm2cpio \ - sed \ - socat \ - sparse \ - tar \ - tesseract-ocr \ - tesseract-ocr-eng \ - xorriso \ - zstd && \ - eatmydata apt-get autoremove -y && \ - eatmydata apt-get autoclean -y && \ - sed -Ei 's,^# (en_US\.UTF-8 .*)$,\1,' /etc/locale.gen && \ - dpkg-reconfigure locales && \ - rm -f /usr/lib*/python3*/EXTERNALLY-MANAGED - -RUN /usr/bin/pip3 install tomli - -ENV CCACHE_WRAPPERSDIR "/usr/libexec/ccache-wrappers" -ENV LANG "en_US.UTF-8" -ENV MAKE "/usr/bin/make" -ENV NINJA "/usr/bin/ninja" -ENV PYTHON "/usr/bin/python3" - -RUN export DEBIAN_FRONTEND=noninteractive && \ - dpkg --add-architecture armel && \ - eatmydata apt-get update && \ - eatmydata apt-get dist-upgrade -y && \ - eatmydata apt-get install --no-install-recommends -y dpkg-dev && \ - eatmydata apt-get install --no-install-recommends -y \ - gcc-arm-linux-gnueabi \ - libaio-dev:armel \ - libasan6:armel \ - libasound2-dev:armel \ - libattr1-dev:armel \ - libbpf-dev:armel \ - libbrlapi-dev:armel \ - libbz2-dev:armel \ - libc6-dev:armel \ - libcacard-dev:armel \ - libcap-ng-dev:armel \ - libcapstone-dev:armel \ - libcmocka-dev:armel \ - libcurl4-gnutls-dev:armel \ - libdaxctl-dev:armel \ - libdrm-dev:armel \ - libepoxy-dev:armel \ - libfdt-dev:armel \ - libffi-dev:armel \ - libfuse3-dev:armel \ - libgbm-dev:armel \ - libgcrypt20-dev:armel \ - libglib2.0-dev:armel \ - libglusterfs-dev:armel \ - libgnutls28-dev:armel \ - libgtk-3-dev:armel \ - libibverbs-dev:armel \ - libiscsi-dev:armel \ - libjemalloc-dev:armel \ - libjpeg62-turbo-dev:armel \ - libjson-c-dev:armel \ - liblttng-ust-dev:armel \ - liblzo2-dev:armel \ - libncursesw5-dev:armel \ - libnfs-dev:armel \ - libnuma-dev:armel \ - libpam0g-dev:armel \ - libpipewire-0.3-dev:armel \ - libpixman-1-dev:armel \ - libpng-dev:armel \ - libpulse-dev:armel \ - librbd-dev:armel \ - librdmacm-dev:armel \ - libsasl2-dev:armel \ - libsdl2-dev:armel \ - libsdl2-image-dev:armel \ - libseccomp-dev:armel \ - libselinux1-dev:armel \ - libslirp-dev:armel \ - libsnappy-dev:armel \ - libspice-server-dev:armel \ - libssh-gcrypt-dev:armel \ - libsystemd-dev:armel \ - libtasn1-6-dev:armel \ - libubsan1:armel \ - libudev-dev:armel \ - liburing-dev:armel \ - libusb-1.0-0-dev:armel \ - libusbredirhost-dev:armel \ - libvdeplug-dev:armel \ - libvirglrenderer-dev:armel \ - libvte-2.91-dev:armel \ - libzstd-dev:armel \ - nettle-dev:armel \ - systemtap-sdt-dev:armel \ - zlib1g-dev:armel && \ - eatmydata apt-get autoremove -y && \ - eatmydata apt-get autoclean -y && \ - mkdir -p /usr/local/share/meson/cross && \ - printf "[binaries]\n\ -c = '/usr/bin/arm-linux-gnueabi-gcc'\n\ -ar = '/usr/bin/arm-linux-gnueabi-gcc-ar'\n\ -strip = '/usr/bin/arm-linux-gnueabi-strip'\n\ -pkgconfig = '/usr/bin/arm-linux-gnueabi-pkg-config'\n\ -\n\ -[host_machine]\n\ -system = 'linux'\n\ -cpu_family = 'arm'\n\ -cpu = 'arm'\n\ -endian = 'little'\n" > /usr/local/share/meson/cross/arm-linux-gnueabi && \ - dpkg-query --showformat '${Package}_${Version}_${Architecture}\n' --show > /packages.txt && \ - mkdir -p /usr/libexec/ccache-wrappers && \ - ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/arm-linux-gnueabi-cc && \ - ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/arm-linux-gnueabi-gcc - -ENV ABI "arm-linux-gnueabi" -ENV MESON_OPTS "--cross-file=arm-linux-gnueabi" -ENV QEMU_CONFIGURE_OPTS --cross-prefix=arm-linux-gnueabi- -ENV DEF_TARGET_LIST arm-softmmu,arm-linux-user,armeb-linux-user -# As a final step configure the user (if env is defined) -ARG USER -ARG UID -RUN if [ "${USER}" ]; then \ - id ${USER} 2>/dev/null || useradd -u ${UID} -U ${USER}; fi diff --git a/tests/docker/dockerfiles/debian-armhf-cross.docker b/tests/docker/dockerfiles/debian-armhf-cross.docker index f26385e..f0e2efc 100644 --- a/tests/docker/dockerfiles/debian-armhf-cross.docker +++ b/tests/docker/dockerfiles/debian-armhf-cross.docker @@ -13,6 +13,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ eatmydata apt-get install --no-install-recommends -y \ bash \ bc \ + bindgen \ bison \ bsdextrautils \ bzip2 \ @@ -30,10 +31,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ git \ hostname \ libglib2.0-dev \ - libgtk-vnc-2.0-dev \ - libpcre2-dev \ - libsndio-dev \ - libspice-protocol-dev \ llvm \ locales \ make \ @@ -53,6 +50,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ python3-venv \ python3-yaml \ rpm2cpio \ + rustc-web \ sed \ socat \ sparse \ @@ -60,6 +58,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ tar \ tesseract-ocr \ tesseract-ocr-eng \ + vulkan-tools \ xorriso \ zstd && \ eatmydata apt-get autoremove -y && \ @@ -92,6 +91,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libcacard-dev:armhf \ libcap-ng-dev:armhf \ libcapstone-dev:armhf \ + libcbor-dev:armhf \ libcmocka-dev:armhf \ libcurl4-gnutls-dev:armhf \ libdaxctl-dev:armhf \ @@ -106,6 +106,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libglusterfs-dev:armhf \ libgnutls28-dev:armhf \ libgtk-3-dev:armhf \ + libgtk-vnc-2.0-dev:armhf \ libibverbs-dev:armhf \ libiscsi-dev:armhf \ libjemalloc-dev:armhf \ @@ -117,6 +118,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libnfs-dev:armhf \ libnuma-dev:armhf \ libpam0g-dev:armhf \ + libpcre2-dev:armhf \ libpipewire-0.3-dev:armhf \ libpixman-1-dev:armhf \ libpng-dev:armhf \ @@ -130,8 +132,10 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libselinux1-dev:armhf \ libslirp-dev:armhf \ libsnappy-dev:armhf \ + libsndio-dev:armhf \ + libspice-protocol-dev:armhf \ libspice-server-dev:armhf \ - libssh-gcrypt-dev:armhf \ + libssh-dev:armhf \ libsystemd-dev:armhf \ libtasn1-6-dev:armhf \ libubsan1:armhf \ @@ -169,6 +173,7 @@ endian = 'little'\n" > /usr/local/share/meson/cross/arm-linux-gnueabihf && \ ENV ABI "arm-linux-gnueabihf" ENV MESON_OPTS "--cross-file=arm-linux-gnueabihf" +ENV RUST_TARGET "armv7-unknown-linux-gnueabihf" ENV QEMU_CONFIGURE_OPTS --cross-prefix=arm-linux-gnueabihf- ENV DEF_TARGET_LIST arm-softmmu,arm-linux-user # As a final step configure the user (if env is defined) diff --git a/tests/docker/dockerfiles/debian-i686-cross.docker b/tests/docker/dockerfiles/debian-i686-cross.docker index 3fe8ee6..025beb1 100644 --- a/tests/docker/dockerfiles/debian-i686-cross.docker +++ b/tests/docker/dockerfiles/debian-i686-cross.docker @@ -1,10 +1,10 @@ # THIS FILE WAS AUTO-GENERATED # -# $ lcitool dockerfile --layers all --cross-arch i686 debian-11 qemu +# $ lcitool dockerfile --layers all --cross-arch i686 debian-12 qemu # # https://gitlab.com/libvirt/libvirt-ci -FROM docker.io/library/debian:11-slim +FROM docker.io/library/debian:12-slim RUN export DEBIAN_FRONTEND=noninteractive && \ apt-get update && \ @@ -13,6 +13,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ eatmydata apt-get install --no-install-recommends -y \ bash \ bc \ + bindgen \ bison \ bsdextrautils \ bzip2 \ @@ -30,10 +31,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ git \ hostname \ libglib2.0-dev \ - libgtk-vnc-2.0-dev \ - libpcre2-dev \ - libsndio-dev \ - libspice-protocol-dev \ llvm \ locales \ make \ @@ -48,19 +45,20 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ python3-opencv \ python3-pillow \ python3-pip \ - python3-setuptools \ python3-sphinx \ python3-sphinx-rtd-theme \ python3-venv \ - python3-wheel \ python3-yaml \ rpm2cpio \ + rustc-web \ sed \ socat \ sparse \ + swtpm \ tar \ tesseract-ocr \ tesseract-ocr-eng \ + vulkan-tools \ xorriso \ zstd && \ eatmydata apt-get autoremove -y && \ @@ -69,8 +67,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ dpkg-reconfigure locales && \ rm -f /usr/lib*/python3*/EXTERNALLY-MANAGED -RUN /usr/bin/pip3 install tomli - ENV CCACHE_WRAPPERSDIR "/usr/libexec/ccache-wrappers" ENV LANG "en_US.UTF-8" ENV MAKE "/usr/bin/make" @@ -95,6 +91,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libcacard-dev:i386 \ libcap-ng-dev:i386 \ libcapstone-dev:i386 \ + libcbor-dev:i386 \ libcmocka-dev:i386 \ libcurl4-gnutls-dev:i386 \ libdaxctl-dev:i386 \ @@ -109,6 +106,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libglusterfs-dev:i386 \ libgnutls28-dev:i386 \ libgtk-3-dev:i386 \ + libgtk-vnc-2.0-dev:i386 \ libibverbs-dev:i386 \ libiscsi-dev:i386 \ libjemalloc-dev:i386 \ @@ -120,6 +118,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libnfs-dev:i386 \ libnuma-dev:i386 \ libpam0g-dev:i386 \ + libpcre2-dev:i386 \ libpipewire-0.3-dev:i386 \ libpixman-1-dev:i386 \ libpng-dev:i386 \ @@ -133,8 +132,10 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libselinux1-dev:i386 \ libslirp-dev:i386 \ libsnappy-dev:i386 \ + libsndio-dev:i386 \ + libspice-protocol-dev:i386 \ libspice-server-dev:i386 \ - libssh-gcrypt-dev:i386 \ + libssh-dev:i386 \ libsystemd-dev:i386 \ libtasn1-6-dev:i386 \ libubsan1:i386 \ @@ -145,6 +146,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libvdeplug-dev:i386 \ libvirglrenderer-dev:i386 \ libvte-2.91-dev:i386 \ + libxdp-dev:i386 \ libzstd-dev:i386 \ nettle-dev:i386 \ systemtap-sdt-dev:i386 \ @@ -170,6 +172,7 @@ endian = 'little'\n" > /usr/local/share/meson/cross/i686-linux-gnu && \ ENV ABI "i686-linux-gnu" ENV MESON_OPTS "--cross-file=i686-linux-gnu" +ENV RUST_TARGET "i686-unknown-linux-gnu" ENV QEMU_CONFIGURE_OPTS --cross-prefix=i686-linux-gnu- ENV DEF_TARGET_LIST x86_64-softmmu,x86_64-linux-user,i386-softmmu,i386-linux-user # As a final step configure the user (if env is defined) diff --git a/tests/docker/dockerfiles/debian-loongarch-cross.docker b/tests/docker/dockerfiles/debian-loongarch-cross.docker index 79eab56..538ab53 100644 --- a/tests/docker/dockerfiles/debian-loongarch-cross.docker +++ b/tests/docker/dockerfiles/debian-loongarch-cross.docker @@ -43,8 +43,8 @@ RUN curl -#SL https://github.com/loongson/build-tools/releases/download/2023.08. ENV PATH $PATH:/opt/cross-tools/bin ENV LD_LIBRARY_PATH /opt/cross-tools/lib:/opt/cross-tools/loongarch64-unknown-linux-gnu/lib:$LD_LIBRARY_PATH -ENV QEMU_CONFIGURE_OPTS --disable-system --disable-docs --disable-tools -ENV DEF_TARGET_LIST loongarch64-linux-user,loongarch-softmmu +ENV QEMU_CONFIGURE_OPTS --disable-docs --disable-tools +ENV DEF_TARGET_LIST loongarch64-linux-user,loongarch64-softmmu ENV MAKE /usr/bin/make # As a final step configure the user (if env is defined) diff --git a/tests/docker/dockerfiles/debian-microblaze-cross.d/build-toolchain.sh b/tests/docker/dockerfiles/debian-microblaze-cross.d/build-toolchain.sh index 23ec0aa..c5cd0aa 100755 --- a/tests/docker/dockerfiles/debian-microblaze-cross.d/build-toolchain.sh +++ b/tests/docker/dockerfiles/debian-microblaze-cross.d/build-toolchain.sh @@ -10,6 +10,8 @@ TOOLCHAIN_INSTALL=/usr/local TOOLCHAIN_BIN=${TOOLCHAIN_INSTALL}/bin CROSS_SYSROOT=${TOOLCHAIN_INSTALL}/$TARGET/sys-root +GCC_PATCH0_URL=https://raw.githubusercontent.com/Xilinx/meta-xilinx/refs/tags/xlnx-rel-v2024.1/meta-microblaze/recipes-devtools/gcc/gcc-12/0009-Patch-microblaze-Fix-atomic-boolean-return-value.patch + export PATH=${TOOLCHAIN_BIN}:$PATH # @@ -31,6 +33,12 @@ mv gcc-11.2.0 src-gcc mv musl-1.2.2 src-musl mv linux-5.10.70 src-linux +# +# Patch gcc +# + +wget -O - ${GCC_PATCH0_URL} | patch -d src-gcc -p1 + mkdir -p bld-hdr bld-binu bld-gcc bld-musl mkdir -p ${CROSS_SYSROOT}/usr/include diff --git a/tests/docker/dockerfiles/debian-mips64el-cross.docker b/tests/docker/dockerfiles/debian-mips64el-cross.docker index 2862785..4a941dd 100644 --- a/tests/docker/dockerfiles/debian-mips64el-cross.docker +++ b/tests/docker/dockerfiles/debian-mips64el-cross.docker @@ -1,10 +1,10 @@ # THIS FILE WAS AUTO-GENERATED # -# $ lcitool dockerfile --layers all --cross-arch mips64el debian-11 qemu +# $ lcitool dockerfile --layers all --cross-arch mips64el debian-12 qemu # # https://gitlab.com/libvirt/libvirt-ci -FROM docker.io/library/debian:11-slim +FROM docker.io/library/debian:12-slim RUN export DEBIAN_FRONTEND=noninteractive && \ apt-get update && \ @@ -13,6 +13,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ eatmydata apt-get install --no-install-recommends -y \ bash \ bc \ + bindgen \ bison \ bsdextrautils \ bzip2 \ @@ -30,10 +31,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ git \ hostname \ libglib2.0-dev \ - libgtk-vnc-2.0-dev \ - libpcre2-dev \ - libsndio-dev \ - libspice-protocol-dev \ llvm \ locales \ make \ @@ -48,19 +45,20 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ python3-opencv \ python3-pillow \ python3-pip \ - python3-setuptools \ python3-sphinx \ python3-sphinx-rtd-theme \ python3-venv \ - python3-wheel \ python3-yaml \ rpm2cpio \ + rustc-web \ sed \ socat \ sparse \ + swtpm \ tar \ tesseract-ocr \ tesseract-ocr-eng \ + vulkan-tools \ xorriso \ zstd && \ eatmydata apt-get autoremove -y && \ @@ -69,8 +67,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ dpkg-reconfigure locales && \ rm -f /usr/lib*/python3*/EXTERNALLY-MANAGED -RUN /usr/bin/pip3 install tomli - ENV CCACHE_WRAPPERSDIR "/usr/libexec/ccache-wrappers" ENV LANG "en_US.UTF-8" ENV MAKE "/usr/bin/make" @@ -94,6 +90,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libcacard-dev:mips64el \ libcap-ng-dev:mips64el \ libcapstone-dev:mips64el \ + libcbor-dev:mips64el \ libcmocka-dev:mips64el \ libcurl4-gnutls-dev:mips64el \ libdaxctl-dev:mips64el \ @@ -108,6 +105,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libglusterfs-dev:mips64el \ libgnutls28-dev:mips64el \ libgtk-3-dev:mips64el \ + libgtk-vnc-2.0-dev:mips64el \ libibverbs-dev:mips64el \ libiscsi-dev:mips64el \ libjemalloc-dev:mips64el \ @@ -119,6 +117,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libnfs-dev:mips64el \ libnuma-dev:mips64el \ libpam0g-dev:mips64el \ + libpcre2-dev:mips64el \ libpipewire-0.3-dev:mips64el \ libpixman-1-dev:mips64el \ libpng-dev:mips64el \ @@ -132,8 +131,10 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libselinux1-dev:mips64el \ libslirp-dev:mips64el \ libsnappy-dev:mips64el \ + libsndio-dev:mips64el \ + libspice-protocol-dev:mips64el \ libspice-server-dev:mips64el \ - libssh-gcrypt-dev:mips64el \ + libssh-dev:mips64el \ libsystemd-dev:mips64el \ libtasn1-6-dev:mips64el \ libudev-dev:mips64el \ @@ -143,6 +144,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libvdeplug-dev:mips64el \ libvirglrenderer-dev:mips64el \ libvte-2.91-dev:mips64el \ + libxdp-dev:mips64el \ libzstd-dev:mips64el \ nettle-dev:mips64el \ systemtap-sdt-dev:mips64el \ @@ -168,6 +170,7 @@ endian = 'little'\n" > /usr/local/share/meson/cross/mips64el-linux-gnuabi64 && \ ENV ABI "mips64el-linux-gnuabi64" ENV MESON_OPTS "--cross-file=mips64el-linux-gnuabi64" +ENV RUST_TARGET "mips64el-unknown-linux-gnuabi64" ENV QEMU_CONFIGURE_OPTS --cross-prefix=mips64el-linux-gnuabi64- ENV DEF_TARGET_LIST mips64el-softmmu,mips64el-linux-user # As a final step configure the user (if env is defined) diff --git a/tests/docker/dockerfiles/debian-mipsel-cross.docker b/tests/docker/dockerfiles/debian-mipsel-cross.docker index 0d559ae..4d3e5d7 100644 --- a/tests/docker/dockerfiles/debian-mipsel-cross.docker +++ b/tests/docker/dockerfiles/debian-mipsel-cross.docker @@ -1,10 +1,10 @@ # THIS FILE WAS AUTO-GENERATED # -# $ lcitool dockerfile --layers all --cross-arch mipsel debian-11 qemu +# $ lcitool dockerfile --layers all --cross-arch mipsel debian-12 qemu # # https://gitlab.com/libvirt/libvirt-ci -FROM docker.io/library/debian:11-slim +FROM docker.io/library/debian:12-slim RUN export DEBIAN_FRONTEND=noninteractive && \ apt-get update && \ @@ -13,6 +13,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ eatmydata apt-get install --no-install-recommends -y \ bash \ bc \ + bindgen \ bison \ bsdextrautils \ bzip2 \ @@ -30,10 +31,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ git \ hostname \ libglib2.0-dev \ - libgtk-vnc-2.0-dev \ - libpcre2-dev \ - libsndio-dev \ - libspice-protocol-dev \ llvm \ locales \ make \ @@ -48,19 +45,20 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ python3-opencv \ python3-pillow \ python3-pip \ - python3-setuptools \ python3-sphinx \ python3-sphinx-rtd-theme \ python3-venv \ - python3-wheel \ python3-yaml \ rpm2cpio \ + rustc-web \ sed \ socat \ sparse \ + swtpm \ tar \ tesseract-ocr \ tesseract-ocr-eng \ + vulkan-tools \ xorriso \ zstd && \ eatmydata apt-get autoremove -y && \ @@ -69,8 +67,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ dpkg-reconfigure locales && \ rm -f /usr/lib*/python3*/EXTERNALLY-MANAGED -RUN /usr/bin/pip3 install tomli - ENV CCACHE_WRAPPERSDIR "/usr/libexec/ccache-wrappers" ENV LANG "en_US.UTF-8" ENV MAKE "/usr/bin/make" @@ -94,6 +90,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libcacard-dev:mipsel \ libcap-ng-dev:mipsel \ libcapstone-dev:mipsel \ + libcbor-dev:mipsel \ libcmocka-dev:mipsel \ libcurl4-gnutls-dev:mipsel \ libdaxctl-dev:mipsel \ @@ -108,6 +105,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libglusterfs-dev:mipsel \ libgnutls28-dev:mipsel \ libgtk-3-dev:mipsel \ + libgtk-vnc-2.0-dev:mipsel \ libibverbs-dev:mipsel \ libiscsi-dev:mipsel \ libjemalloc-dev:mipsel \ @@ -119,6 +117,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libnfs-dev:mipsel \ libnuma-dev:mipsel \ libpam0g-dev:mipsel \ + libpcre2-dev:mipsel \ libpipewire-0.3-dev:mipsel \ libpixman-1-dev:mipsel \ libpng-dev:mipsel \ @@ -132,8 +131,10 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libselinux1-dev:mipsel \ libslirp-dev:mipsel \ libsnappy-dev:mipsel \ + libsndio-dev:mipsel \ + libspice-protocol-dev:mipsel \ libspice-server-dev:mipsel \ - libssh-gcrypt-dev:mipsel \ + libssh-dev:mipsel \ libsystemd-dev:mipsel \ libtasn1-6-dev:mipsel \ libudev-dev:mipsel \ @@ -143,6 +144,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libvdeplug-dev:mipsel \ libvirglrenderer-dev:mipsel \ libvte-2.91-dev:mipsel \ + libxdp-dev:mipsel \ libzstd-dev:mipsel \ nettle-dev:mipsel \ systemtap-sdt-dev:mipsel \ @@ -168,6 +170,7 @@ endian = 'little'\n" > /usr/local/share/meson/cross/mipsel-linux-gnu && \ ENV ABI "mipsel-linux-gnu" ENV MESON_OPTS "--cross-file=mipsel-linux-gnu" +ENV RUST_TARGET "mipsel-unknown-linux-gnu" ENV QEMU_CONFIGURE_OPTS --cross-prefix=mipsel-linux-gnu- ENV DEF_TARGET_LIST mipsel-softmmu,mipsel-linux-user # As a final step configure the user (if env is defined) diff --git a/tests/docker/dockerfiles/debian-ppc64el-cross.docker b/tests/docker/dockerfiles/debian-ppc64el-cross.docker index 8c1dcec..22b4457 100644 --- a/tests/docker/dockerfiles/debian-ppc64el-cross.docker +++ b/tests/docker/dockerfiles/debian-ppc64el-cross.docker @@ -13,6 +13,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ eatmydata apt-get install --no-install-recommends -y \ bash \ bc \ + bindgen \ bison \ bsdextrautils \ bzip2 \ @@ -30,10 +31,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ git \ hostname \ libglib2.0-dev \ - libgtk-vnc-2.0-dev \ - libpcre2-dev \ - libsndio-dev \ - libspice-protocol-dev \ llvm \ locales \ make \ @@ -53,6 +50,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ python3-venv \ python3-yaml \ rpm2cpio \ + rustc-web \ sed \ socat \ sparse \ @@ -60,6 +58,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ tar \ tesseract-ocr \ tesseract-ocr-eng \ + vulkan-tools \ xorriso \ zstd && \ eatmydata apt-get autoremove -y && \ @@ -92,6 +91,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libcacard-dev:ppc64el \ libcap-ng-dev:ppc64el \ libcapstone-dev:ppc64el \ + libcbor-dev:ppc64el \ libcmocka-dev:ppc64el \ libcurl4-gnutls-dev:ppc64el \ libdaxctl-dev:ppc64el \ @@ -106,6 +106,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libglusterfs-dev:ppc64el \ libgnutls28-dev:ppc64el \ libgtk-3-dev:ppc64el \ + libgtk-vnc-2.0-dev:ppc64el \ libibverbs-dev:ppc64el \ libiscsi-dev:ppc64el \ libjemalloc-dev:ppc64el \ @@ -117,6 +118,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libnfs-dev:ppc64el \ libnuma-dev:ppc64el \ libpam0g-dev:ppc64el \ + libpcre2-dev:ppc64el \ libpipewire-0.3-dev:ppc64el \ libpixman-1-dev:ppc64el \ libpng-dev:ppc64el \ @@ -130,8 +132,10 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libselinux1-dev:ppc64el \ libslirp-dev:ppc64el \ libsnappy-dev:ppc64el \ + libsndio-dev:ppc64el \ + libspice-protocol-dev:ppc64el \ libspice-server-dev:ppc64el \ - libssh-gcrypt-dev:ppc64el \ + libssh-dev:ppc64el \ libsystemd-dev:ppc64el \ libtasn1-6-dev:ppc64el \ libubsan1:ppc64el \ @@ -168,6 +172,7 @@ endian = 'little'\n" > /usr/local/share/meson/cross/powerpc64le-linux-gnu && \ ENV ABI "powerpc64le-linux-gnu" ENV MESON_OPTS "--cross-file=powerpc64le-linux-gnu" +ENV RUST_TARGET "powerpc64le-unknown-linux-gnu" ENV QEMU_CONFIGURE_OPTS --cross-prefix=powerpc64le-linux-gnu- ENV DEF_TARGET_LIST ppc64-softmmu,ppc64-linux-user # As a final step configure the user (if env is defined) diff --git a/tests/docker/dockerfiles/debian-riscv64-cross.docker b/tests/docker/dockerfiles/debian-riscv64-cross.docker index 4d8ca83..b0386cd 100644 --- a/tests/docker/dockerfiles/debian-riscv64-cross.docker +++ b/tests/docker/dockerfiles/debian-riscv64-cross.docker @@ -1,10 +1,10 @@ # THIS FILE WAS AUTO-GENERATED # -# $ lcitool dockerfile --layers all --cross-arch riscv64 debian-sid qemu-minimal +# $ lcitool dockerfile --layers all --cross-arch riscv64 debian-13 qemu-minimal # # https://gitlab.com/libvirt/libvirt-ci -FROM docker.io/library/debian:sid-slim +FROM docker.io/library/debian:trixie-slim RUN export DEBIAN_FRONTEND=noninteractive && \ apt-get update && \ diff --git a/tests/docker/dockerfiles/debian-s390x-cross.docker b/tests/docker/dockerfiles/debian-s390x-cross.docker index 72668e0..13ec52c 100644 --- a/tests/docker/dockerfiles/debian-s390x-cross.docker +++ b/tests/docker/dockerfiles/debian-s390x-cross.docker @@ -13,6 +13,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ eatmydata apt-get install --no-install-recommends -y \ bash \ bc \ + bindgen \ bison \ bsdextrautils \ bzip2 \ @@ -30,10 +31,6 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ git \ hostname \ libglib2.0-dev \ - libgtk-vnc-2.0-dev \ - libpcre2-dev \ - libsndio-dev \ - libspice-protocol-dev \ llvm \ locales \ make \ @@ -53,6 +50,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ python3-venv \ python3-yaml \ rpm2cpio \ + rustc-web \ sed \ socat \ sparse \ @@ -60,6 +58,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ tar \ tesseract-ocr \ tesseract-ocr-eng \ + vulkan-tools \ xorriso \ zstd && \ eatmydata apt-get autoremove -y && \ @@ -92,6 +91,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libcacard-dev:s390x \ libcap-ng-dev:s390x \ libcapstone-dev:s390x \ + libcbor-dev:s390x \ libcmocka-dev:s390x \ libcurl4-gnutls-dev:s390x \ libdaxctl-dev:s390x \ @@ -106,6 +106,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libglusterfs-dev:s390x \ libgnutls28-dev:s390x \ libgtk-3-dev:s390x \ + libgtk-vnc-2.0-dev:s390x \ libibverbs-dev:s390x \ libiscsi-dev:s390x \ libjemalloc-dev:s390x \ @@ -117,6 +118,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libnfs-dev:s390x \ libnuma-dev:s390x \ libpam0g-dev:s390x \ + libpcre2-dev:s390x \ libpipewire-0.3-dev:s390x \ libpixman-1-dev:s390x \ libpng-dev:s390x \ @@ -130,7 +132,9 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libselinux1-dev:s390x \ libslirp-dev:s390x \ libsnappy-dev:s390x \ - libssh-gcrypt-dev:s390x \ + libsndio-dev:s390x \ + libspice-protocol-dev:s390x \ + libssh-dev:s390x \ libsystemd-dev:s390x \ libtasn1-6-dev:s390x \ libubsan1:s390x \ @@ -167,6 +171,7 @@ endian = 'big'\n" > /usr/local/share/meson/cross/s390x-linux-gnu && \ ENV ABI "s390x-linux-gnu" ENV MESON_OPTS "--cross-file=s390x-linux-gnu" +ENV RUST_TARGET "s390x-unknown-linux-gnu" ENV QEMU_CONFIGURE_OPTS --cross-prefix=s390x-linux-gnu- ENV DEF_TARGET_LIST s390x-softmmu,s390x-linux-user # As a final step configure the user (if env is defined) diff --git a/tests/docker/dockerfiles/debian-toolchain.docker b/tests/docker/dockerfiles/debian-toolchain.docker index 687a97f..ab4ce29 100644 --- a/tests/docker/dockerfiles/debian-toolchain.docker +++ b/tests/docker/dockerfiles/debian-toolchain.docker @@ -10,6 +10,8 @@ FROM docker.io/library/debian:11-slim # ??? The build-dep isn't working, missing a number of # minimal build dependiencies, e.g. libmpc. +RUN sed 's/^deb /deb-src /' </etc/apt/sources.list >/etc/apt/sources.list.d/deb-src.list + RUN apt update && \ DEBIAN_FRONTEND=noninteractive apt install -yy eatmydata && \ DEBIAN_FRONTEND=noninteractive eatmydata \ @@ -33,6 +35,11 @@ RUN cd /root && ./build-toolchain.sh # and the build trees by restoring the original image, # then copying the built toolchain from stage 0. FROM docker.io/library/debian:11-slim +RUN apt update && \ + DEBIAN_FRONTEND=noninteractive apt install -yy eatmydata && \ + DEBIAN_FRONTEND=noninteractive eatmydata \ + apt install -y --no-install-recommends \ + libmpc3 COPY --from=0 /usr/local /usr/local # As a final step configure the user (if env is defined) ARG USER diff --git a/tests/docker/dockerfiles/debian-tricore-cross.docker b/tests/docker/dockerfiles/debian-tricore-cross.docker index 479b4d6..7e00e87 100644 --- a/tests/docker/dockerfiles/debian-tricore-cross.docker +++ b/tests/docker/dockerfiles/debian-tricore-cross.docker @@ -11,8 +11,6 @@ # FROM docker.io/library/debian:11-slim -MAINTAINER Philippe Mathieu-Daudé <f4bug@amsat.org> - RUN apt update && \ DEBIAN_FRONTEND=noninteractive apt install -yy eatmydata && \ DEBIAN_FRONTEND=noninteractive eatmydata apt install -yy \ diff --git a/tests/docker/dockerfiles/debian.docker b/tests/docker/dockerfiles/debian.docker index 42bd006..0a57c1a 100644 --- a/tests/docker/dockerfiles/debian.docker +++ b/tests/docker/dockerfiles/debian.docker @@ -13,6 +13,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ eatmydata apt-get install --no-install-recommends -y \ bash \ bc \ + bindgen \ bison \ bsdextrautils \ bzip2 \ @@ -41,6 +42,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libcacard-dev \ libcap-ng-dev \ libcapstone-dev \ + libcbor-dev \ libcmocka-dev \ libcurl4-gnutls-dev \ libdaxctl-dev \ @@ -85,7 +87,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libsndio-dev \ libspice-protocol-dev \ libspice-server-dev \ - libssh-gcrypt-dev \ + libssh-dev \ libsystemd-dev \ libtasn1-6-dev \ libubsan1 \ @@ -120,6 +122,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ python3-venv \ python3-yaml \ rpm2cpio \ + rustc-web \ sed \ socat \ sparse \ @@ -128,6 +131,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ tar \ tesseract-ocr \ tesseract-ocr-eng \ + vulkan-tools \ xorriso \ zlib1g-dev \ zstd && \ diff --git a/tests/docker/dockerfiles/emsdk-wasm32-cross.docker b/tests/docker/dockerfiles/emsdk-wasm32-cross.docker new file mode 100644 index 0000000..60a7d02 --- /dev/null +++ b/tests/docker/dockerfiles/emsdk-wasm32-cross.docker @@ -0,0 +1,145 @@ +# syntax = docker/dockerfile:1.5 + +ARG EMSDK_VERSION_QEMU=3.1.50 +ARG ZLIB_VERSION=1.3.1 +ARG GLIB_MINOR_VERSION=2.84 +ARG GLIB_VERSION=${GLIB_MINOR_VERSION}.0 +ARG PIXMAN_VERSION=0.44.2 +ARG FFI_VERSION=v3.4.7 +ARG MESON_VERSION=1.5.0 + +FROM emscripten/emsdk:$EMSDK_VERSION_QEMU AS build-base +ARG MESON_VERSION +ENV TARGET=/builddeps/target +ENV CPATH="$TARGET/include" +ENV PKG_CONFIG_PATH="$TARGET/lib/pkgconfig" +ENV EM_PKG_CONFIG_PATH="$PKG_CONFIG_PATH" +ENV CFLAGS="-O3 -pthread -DWASM_BIGINT" +ENV CXXFLAGS="$CFLAGS" +ENV LDFLAGS="-sWASM_BIGINT -sASYNCIFY=1 -L$TARGET/lib" +RUN apt-get update && apt-get install -y \ + autoconf \ + build-essential \ + libglib2.0-dev \ + libtool \ + pkgconf \ + ninja-build \ + python3-pip +RUN pip3 install meson==${MESON_VERSION} tomli +RUN mkdir /build +WORKDIR /build +RUN mkdir -p $TARGET +RUN <<EOF +cat <<EOT > /cross.meson +[host_machine] +system = 'emscripten' +cpu_family = 'wasm32' +cpu = 'wasm32' +endian = 'little' + +[binaries] +c = 'emcc' +cpp = 'em++' +ar = 'emar' +ranlib = 'emranlib' +pkgconfig = ['pkg-config', '--static'] +EOT +EOF + +FROM build-base AS zlib-dev +ARG ZLIB_VERSION +RUN mkdir -p /zlib +RUN curl -Ls https://zlib.net/zlib-$ZLIB_VERSION.tar.xz | \ + tar xJC /zlib --strip-components=1 +WORKDIR /zlib +RUN emconfigure ./configure --prefix=$TARGET --static +RUN emmake make install -j$(nproc) + +FROM build-base AS libffi-dev +ARG FFI_VERSION +RUN mkdir -p /libffi +RUN git clone https://github.com/libffi/libffi /libffi +WORKDIR /libffi +RUN git checkout $FFI_VERSION +RUN autoreconf -fiv +RUN emconfigure ./configure --host=wasm32-unknown-linux \ + --prefix=$TARGET --enable-static \ + --disable-shared --disable-dependency-tracking \ + --disable-builddir --disable-multi-os-directory \ + --disable-raw-api --disable-docs +RUN emmake make install SUBDIRS='include' -j$(nproc) + +FROM build-base AS pixman-dev +ARG PIXMAN_VERSION +RUN mkdir /pixman/ +RUN git clone https://gitlab.freedesktop.org/pixman/pixman /pixman/ +WORKDIR /pixman +RUN git checkout pixman-$PIXMAN_VERSION +RUN <<EOF +cat <<EOT >> /cross.meson +[built-in options] +c_args = [$(printf "'%s', " $CFLAGS | sed 's/, $//')] +cpp_args = [$(printf "'%s', " $CFLAGS | sed 's/, $//')] +objc_args = [$(printf "'%s', " $CFLAGS | sed 's/, $//')] +c_link_args = [$(printf "'%s', " $LDFLAGS | sed 's/, $//')] +cpp_link_args = [$(printf "'%s', " $LDFLAGS | sed 's/, $//')] +EOT +EOF +RUN meson setup _build --prefix=$TARGET --cross-file=/cross.meson \ + --default-library=static \ + --buildtype=release -Dtests=disabled -Ddemos=disabled +RUN meson install -C _build + +FROM build-base AS glib-dev +ARG GLIB_VERSION +ARG GLIB_MINOR_VERSION +RUN mkdir -p /stub +WORKDIR /stub +RUN <<EOF +cat <<'EOT' > res_query.c +#include <netdb.h> +int res_query(const char *name, int class, + int type, unsigned char *dest, int len) +{ + h_errno = HOST_NOT_FOUND; + return -1; +} +EOT +EOF +RUN emcc ${CFLAGS} -c res_query.c -fPIC -o libresolv.o +RUN ar rcs libresolv.a libresolv.o +RUN mkdir -p $TARGET/lib/ +RUN cp libresolv.a $TARGET/lib/ + +RUN mkdir -p /glib +RUN curl -Lks https://download.gnome.org/sources/glib/${GLIB_MINOR_VERSION}/glib-$GLIB_VERSION.tar.xz | \ + tar xJC /glib --strip-components=1 + +COPY --link --from=zlib-dev /builddeps/ /builddeps/ +COPY --link --from=libffi-dev /builddeps/ /builddeps/ + +WORKDIR /glib +RUN <<EOF +CFLAGS="$CFLAGS -Wno-incompatible-function-pointer-types" ; +cat <<EOT >> /cross.meson +[built-in options] +c_args = [$(printf "'%s', " $CFLAGS | sed 's/, $//')] +cpp_args = [$(printf "'%s', " $CFLAGS | sed 's/, $//')] +objc_args = [$(printf "'%s', " $CFLAGS | sed 's/, $//')] +c_link_args = [$(printf "'%s', " $LDFLAGS | sed 's/, $//')] +cpp_link_args = [$(printf "'%s', " $LDFLAGS | sed 's/, $//')] +EOT +EOF +RUN meson setup _build --prefix=$TARGET --cross-file=/cross.meson \ + --default-library=static --buildtype=release --force-fallback-for=pcre2 \ + -Dselinux=disabled -Dxattr=false -Dlibmount=disabled -Dnls=disabled \ + -Dtests=false -Dglib_debug=disabled -Dglib_assert=false -Dglib_checks=false +# FIXME: emscripten doesn't provide some pthread functions in the final link, +# which isn't detected during meson setup. +RUN sed -i -E "/#define HAVE_POSIX_SPAWN 1/d" ./_build/config.h +RUN sed -i -E "/#define HAVE_PTHREAD_GETNAME_NP 1/d" ./_build/config.h +RUN meson install -C _build + +FROM build-base +COPY --link --from=glib-dev /builddeps/ /builddeps/ +COPY --link --from=pixman-dev /builddeps/ /builddeps/ diff --git a/tests/docker/dockerfiles/fedora-cris-cross.docker b/tests/docker/dockerfiles/fedora-cris-cross.docker deleted file mode 100644 index 97c9d37..0000000 --- a/tests/docker/dockerfiles/fedora-cris-cross.docker +++ /dev/null @@ -1,14 +0,0 @@ -# -# Cross compiler for cris system tests -# - -FROM registry.fedoraproject.org/fedora:33 -ENV PACKAGES gcc-cris-linux-gnu -ENV MAKE /usr/bin/make -RUN dnf install -y $PACKAGES -RUN rpm -q $PACKAGES | sort > /packages.txt -# As a final step configure the user (if env is defined) -ARG USER -ARG UID -RUN if [ "${USER}" ]; then \ - id ${USER} 2>/dev/null || useradd -u ${UID} -U ${USER}; fi diff --git a/tests/docker/dockerfiles/fedora-rust-nightly.docker b/tests/docker/dockerfiles/fedora-rust-nightly.docker new file mode 100644 index 0000000..4a03330 --- /dev/null +++ b/tests/docker/dockerfiles/fedora-rust-nightly.docker @@ -0,0 +1,183 @@ +# THIS FILE WAS AUTO-GENERATED +# +# $ lcitool dockerfile --layers all fedora-40 qemu +# +# https://gitlab.com/libvirt/libvirt-ci + +FROM registry.fedoraproject.org/fedora:40 + +RUN dnf install -y nosync && \ + printf '#!/bin/sh\n\ +if test -d /usr/lib64\n\ +then\n\ + export LD_PRELOAD=/usr/lib64/nosync/nosync.so\n\ +else\n\ + export LD_PRELOAD=/usr/lib/nosync/nosync.so\n\ +fi\n\ +exec "$@"\n' > /usr/bin/nosync && \ + chmod +x /usr/bin/nosync && \ + nosync dnf update -y && \ + nosync dnf install -y \ + SDL2-devel \ + SDL2_image-devel \ + alsa-lib-devel \ + bash \ + bc \ + bindgen-cli \ + bison \ + brlapi-devel \ + bzip2 \ + bzip2-devel \ + ca-certificates \ + capstone-devel \ + ccache \ + clang \ + ctags \ + cyrus-sasl-devel \ + daxctl-devel \ + dbus-daemon \ + device-mapper-multipath-devel \ + diffutils \ + findutils \ + flex \ + fuse3-devel \ + gcc \ + gcovr \ + gettext \ + git \ + glib2-devel \ + glib2-static \ + glibc-langpack-en \ + glibc-static \ + glusterfs-api-devel \ + gnutls-devel \ + gtk-vnc2-devel \ + gtk3-devel \ + hostname \ + jemalloc-devel \ + json-c-devel \ + libaio-devel \ + libasan \ + libattr-devel \ + libbpf-devel \ + libcacard-devel \ + libcap-ng-devel \ + libcbor-devel \ + libcmocka-devel \ + libcurl-devel \ + libdrm-devel \ + libepoxy-devel \ + libfdt-devel \ + libffi-devel \ + libgcrypt-devel \ + libiscsi-devel \ + libjpeg-devel \ + libnfs-devel \ + libpmem-devel \ + libpng-devel \ + librbd-devel \ + libseccomp-devel \ + libselinux-devel \ + libslirp-devel \ + libssh-devel \ + libtasn1-devel \ + libubsan \ + liburing-devel \ + libusbx-devel \ + libxdp-devel \ + libzstd-devel \ + llvm \ + lttng-ust-devel \ + lzo-devel \ + make \ + mesa-libgbm-devel \ + meson \ + mtools \ + ncurses-devel \ + nettle-devel \ + ninja-build \ + nmap-ncat \ + numactl-devel \ + openssh-clients \ + pam-devel \ + pcre-static \ + pipewire-devel \ + pixman-devel \ + pkgconfig \ + pulseaudio-libs-devel \ + python3 \ + python3-PyYAML \ + python3-numpy \ + python3-opencv \ + python3-pillow \ + python3-pip \ + python3-sphinx \ + python3-sphinx_rtd_theme \ + python3-zombie-imp \ + rdma-core-devel \ + rust \ + sed \ + snappy-devel \ + socat \ + sparse \ + spice-protocol \ + spice-server-devel \ + swtpm \ + systemd-devel \ + systemtap-sdt-devel \ + tar \ + tesseract \ + tesseract-langpack-eng \ + usbredir-devel \ + util-linux \ + virglrenderer-devel \ + vte291-devel \ + vulkan-tools \ + which \ + xen-devel \ + xorriso \ + zlib-devel \ + zlib-static \ + zstd && \ + nosync dnf autoremove -y && \ + nosync dnf clean all -y && \ + rm -f /usr/lib*/python3*/EXTERNALLY-MANAGED && \ + rpm -qa | sort > /packages.txt && \ + mkdir -p /usr/libexec/ccache-wrappers && \ + ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/cc && \ + ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/clang && \ + ln -s /usr/bin/ccache /usr/libexec/ccache-wrappers/gcc + +ENV CCACHE_WRAPPERSDIR "/usr/libexec/ccache-wrappers" +ENV LANG "en_US.UTF-8" +ENV MAKE "/usr/bin/make" +ENV NINJA "/usr/bin/ninja" +ENV PYTHON "/usr/bin/python3" +RUN dnf install -y wget +ENV RUSTUP_HOME=/usr/local/rustup CARGO_HOME=/usr/local/cargo +ENV RUSTC=/usr/local/rustup/toolchains/nightly-x86_64-unknown-linux-gnu/bin/rustc +ENV RUSTDOC=/usr/local/rustup/toolchains/nightly-x86_64-unknown-linux-gnu/bin/rustdoc +ENV CARGO=/usr/local/rustup/toolchains/nightly-x86_64-unknown-linux-gnu/bin/cargo +RUN set -eux && \ + rustArch='x86_64-unknown-linux-gnu' && \ + rustupSha256='6aeece6993e902708983b209d04c0d1dbb14ebb405ddb87def578d41f920f56d' && \ + url="https://static.rust-lang.org/rustup/archive/1.27.1/${rustArch}/rustup-init" && \ + wget "$url" && \ + echo "${rustupSha256} *rustup-init" | sha256sum -c - && \ + chmod +x rustup-init && \ + ./rustup-init -y --no-modify-path --profile default --default-toolchain nightly --default-host ${rustArch} && \ + chmod -R a+w $RUSTUP_HOME $CARGO_HOME && \ + /usr/local/cargo/bin/rustup --version && \ + /usr/local/cargo/bin/rustup run nightly cargo --version && \ + /usr/local/cargo/bin/rustup run nightly rustc --version && \ + test "$CARGO" = "$(/usr/local/cargo/bin/rustup +nightly which cargo)" && \ + test "$RUSTDOC" = "$(/usr/local/cargo/bin/rustup +nightly which rustdoc)" && \ + test "$RUSTC" = "$(/usr/local/cargo/bin/rustup +nightly which rustc)" +ENV PATH=$CARGO_HOME/bin:$PATH +RUN /usr/local/cargo/bin/rustup run nightly cargo install bindgen-cli +RUN $CARGO --list +# As a final step configure the user (if env is defined) +ARG USER +ARG UID +RUN if [ "${USER}" ]; then \ + id ${USER} 2>/dev/null || useradd -u ${UID} -U ${USER}; fi diff --git a/tests/docker/dockerfiles/fedora-win64-cross.docker b/tests/docker/dockerfiles/fedora-win64-cross.docker index 6b264d9..a950344 100644 --- a/tests/docker/dockerfiles/fedora-win64-cross.docker +++ b/tests/docker/dockerfiles/fedora-win64-cross.docker @@ -20,6 +20,7 @@ exec "$@"\n' > /usr/bin/nosync && \ nosync dnf install -y \ bash \ bc \ + bindgen-cli \ bison \ bzip2 \ ca-certificates \ @@ -34,7 +35,6 @@ exec "$@"\n' > /usr/bin/nosync && \ git \ glib2-devel \ glibc-langpack-en \ - gtk-vnc2-devel \ hostname \ llvm \ make \ @@ -43,7 +43,6 @@ exec "$@"\n' > /usr/bin/nosync && \ ninja-build \ nmap-ncat \ openssh-clients \ - pcre-static \ python3 \ python3-PyYAML \ python3-numpy \ @@ -53,15 +52,16 @@ exec "$@"\n' > /usr/bin/nosync && \ python3-sphinx \ python3-sphinx_rtd_theme \ python3-zombie-imp \ + rust \ sed \ socat \ sparse \ - spice-protocol \ swtpm \ tar \ tesseract \ tesseract-langpack-eng \ util-linux \ + vulkan-tools \ which \ xorriso \ zstd && \ @@ -87,6 +87,7 @@ RUN nosync dnf install -y \ mingw64-gettext \ mingw64-glib2 \ mingw64-gnutls \ + mingw64-gtk-vnc2 \ mingw64-gtk3 \ mingw64-libepoxy \ mingw64-libgcrypt \ diff --git a/tests/docker/dockerfiles/fedora.docker b/tests/docker/dockerfiles/fedora.docker index ecdefaf..014e3cc 100644 --- a/tests/docker/dockerfiles/fedora.docker +++ b/tests/docker/dockerfiles/fedora.docker @@ -23,6 +23,7 @@ exec "$@"\n' > /usr/bin/nosync && \ alsa-lib-devel \ bash \ bc \ + bindgen-cli \ bison \ brlapi-devel \ bzip2 \ @@ -61,6 +62,7 @@ exec "$@"\n' > /usr/bin/nosync && \ libbpf-devel \ libcacard-devel \ libcap-ng-devel \ + libcbor-devel \ libcmocka-devel \ libcurl-devel \ libdrm-devel \ @@ -113,6 +115,7 @@ exec "$@"\n' > /usr/bin/nosync && \ python3-sphinx_rtd_theme \ python3-zombie-imp \ rdma-core-devel \ + rust \ sed \ snappy-devel \ socat \ @@ -129,6 +132,7 @@ exec "$@"\n' > /usr/bin/nosync && \ util-linux \ virglrenderer-devel \ vte291-devel \ + vulkan-tools \ which \ xen-devel \ xorriso \ diff --git a/tests/docker/dockerfiles/opensuse-leap.docker b/tests/docker/dockerfiles/opensuse-leap.docker index 6614362..e90225d 100644 --- a/tests/docker/dockerfiles/opensuse-leap.docker +++ b/tests/docker/dockerfiles/opensuse-leap.docker @@ -4,9 +4,10 @@ # # https://gitlab.com/libvirt/libvirt-ci -FROM registry.opensuse.org/opensuse/leap:15.5 +FROM registry.opensuse.org/opensuse/leap:15.6 RUN zypper update -y && \ + zypper addrepo -fc https://download.opensuse.org/update/leap/15.6/backports/openSUSE:Backports:SLE-15-SP6:Update.repo && \ zypper install -y \ Mesa-devel \ alsa-lib-devel \ @@ -46,6 +47,7 @@ RUN zypper update -y && \ libbz2-devel \ libcacard-devel \ libcap-ng-devel \ + libcbor-devel \ libcmocka-devel \ libcurl-devel \ libdrm-devel \ @@ -95,6 +97,8 @@ RUN zypper update -y && \ python311-pip \ python311-setuptools \ rdma-core-devel \ + rust \ + rust-bindgen \ sed \ snappy-devel \ sndio-devel \ @@ -111,6 +115,7 @@ RUN zypper update -y && \ util-linux \ virglrenderer-devel \ vte-devel \ + vulkan-tools \ which \ xen-devel \ xorriso \ @@ -127,7 +132,7 @@ RUN zypper update -y && \ RUN /usr/bin/pip3.11 install \ PyYAML \ - meson==0.63.2 \ + meson==1.5.0 \ pillow \ sphinx \ sphinx-rtd-theme diff --git a/tests/docker/dockerfiles/python.docker b/tests/docker/dockerfiles/python.docker index 8f0af9e..59e70a0 100644 --- a/tests/docker/dockerfiles/python.docker +++ b/tests/docker/dockerfiles/python.docker @@ -15,7 +15,6 @@ ENV PACKAGES \ python3.11 \ python3.12 \ python3.13 \ - python3.8 \ python3.9 RUN dnf install -y $PACKAGES diff --git a/tests/docker/dockerfiles/ubuntu2204.docker b/tests/docker/dockerfiles/ubuntu2204.docker index 3a7de6a..28a6f93 100644 --- a/tests/docker/dockerfiles/ubuntu2204.docker +++ b/tests/docker/dockerfiles/ubuntu2204.docker @@ -41,6 +41,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libcacard-dev \ libcap-ng-dev \ libcapstone-dev \ + libcbor-dev \ libcmocka-dev \ libcurl4-gnutls-dev \ libdaxctl-dev \ @@ -120,6 +121,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ python3-venv \ python3-yaml \ rpm2cpio \ + rustc-1.77 \ sed \ socat \ sparse \ @@ -128,6 +130,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ tar \ tesseract-ocr \ tesseract-ocr-eng \ + vulkan-tools \ xorriso \ zlib1g-dev \ zstd && \ @@ -147,6 +150,13 @@ ENV LANG "en_US.UTF-8" ENV MAKE "/usr/bin/make" ENV NINJA "/usr/bin/ninja" ENV PYTHON "/usr/bin/python3" +ENV RUSTC=/usr/bin/rustc-1.77 +ENV RUSTDOC=/usr/bin/rustdoc-1.77 +ENV CARGO_HOME=/usr/local/cargo +ENV PATH=$CARGO_HOME/bin:$PATH +RUN DEBIAN_FRONTEND=noninteractive eatmydata \ + apt install -y --no-install-recommends cargo +RUN cargo install bindgen-cli # As a final step configure the user (if env is defined) ARG USER ARG UID diff --git a/tests/docker/test-debug b/tests/docker/test-debug index f52f163..678cecc 100755 --- a/tests/docker/test-debug +++ b/tests/docker/test-debug @@ -1,6 +1,6 @@ #!/bin/bash -e # -# Compile and check with clang & --enable-debug --enable-sanitizers. +# Compile and check with clang & debug & sanitizers # # Copyright (c) 2016-2018 Red Hat Inc. # @@ -19,7 +19,7 @@ requires_binary clang cd "$BUILD_DIR" OPTS="--cxx=clang++ --cc=clang --host-cc=clang" -OPTS="--enable-debug --enable-sanitizers $OPTS" +OPTS="--enable-debug --enable-asan --enable-ubsan $OPTS" export ASAN_OPTIONS=detect_leaks=0 build_qemu $OPTS diff --git a/tests/docker/test-rust b/tests/docker/test-rust new file mode 100755 index 0000000..e7e3e94 --- /dev/null +++ b/tests/docker/test-rust @@ -0,0 +1,21 @@ +#!/bin/bash -e +# +# Run the rust code checks (a.k.a. check-rust-tools-nightly) +# +# Copyright (c) 2025 Linaro Ltd +# +# Authors: +# Alex Bennée <alex.bennee@linaro.org> +# +# This work is licensed under the terms of the GNU GPL, version 2 +# or (at your option) any later version. See the COPYING file in +# the top-level directory. + +. common.rc + +cd "$BUILD_DIR" + +configure_qemu --disable-user --disable-docs --enable-rust +pyvenv/bin/meson devenv -w $QEMU_SRC/rust ${CARGO-cargo} fmt --check +make clippy +make rustdoc diff --git a/tests/fp/fp-bench.c b/tests/fp/fp-bench.c index 8ce0ca1..d90f542 100644 --- a/tests/fp/fp-bench.c +++ b/tests/fp/fp-bench.c @@ -488,6 +488,16 @@ static void run_bench(void) { bench_func_t f; + /* + * These implementation-defined choices for various things IEEE + * doesn't specify match those used by the Arm architecture. + */ + set_float_2nan_prop_rule(float_2nan_prop_s_ab, &soft_status); + set_float_3nan_prop_rule(float_3nan_prop_s_cab, &soft_status); + set_float_infzeronan_rule(float_infzeronan_dnan_if_qnan, &soft_status); + set_float_default_nan_pattern(0b01000000, &soft_status); + set_float_ftz_detection(float_ftz_before_rounding, &soft_status); + f = bench_funcs[operation][precision]; g_assert(f); f(); diff --git a/tests/fp/fp-test-log2.c b/tests/fp/fp-test-log2.c index 4eae93e..79f619c 100644 --- a/tests/fp/fp-test-log2.c +++ b/tests/fp/fp-test-log2.c @@ -70,6 +70,8 @@ int main(int ac, char **av) float_status qsf = {0}; int i; + set_float_2nan_prop_rule(float_2nan_prop_s_ab, &qsf); + set_float_default_nan_pattern(0b01000000, &qsf); set_float_rounding_mode(float_round_nearest_even, &qsf); test.d = 0.0; diff --git a/tests/fp/fp-test.c b/tests/fp/fp-test.c index 36b5712..c619e5d 100644 --- a/tests/fp/fp-test.c +++ b/tests/fp/fp-test.c @@ -935,6 +935,15 @@ void run_test(void) { unsigned int i; + /* + * These implementation-defined choices for various things IEEE + * doesn't specify match those used by the Arm architecture. + */ + set_float_2nan_prop_rule(float_2nan_prop_s_ab, &qsf); + set_float_3nan_prop_rule(float_3nan_prop_s_cab, &qsf); + set_float_default_nan_pattern(0b01000000, &qsf); + set_float_infzeronan_rule(float_infzeronan_dnan_if_qnan, &qsf); + genCases_setLevel(test_level); verCases_maxErrorCount = n_max_errors; diff --git a/tests/fp/meson.build b/tests/fp/meson.build index 114b4b4..9059a24 100644 --- a/tests/fp/meson.build +++ b/tests/fp/meson.build @@ -7,6 +7,16 @@ if host_os == 'windows' subdir_done() endif +# By default tests run with the usual 30s timeout; particularly +# slow tests can have that overridden here. The keys here are +# the testnames without their fp-test- prefix. +slow_fp_tests = { + 'rem': 60, + 'div': 60, + 'mul': 60, + 'mulAdd': 180, +} + sfcflags = [ # softfloat defines '-DSOFTFLOAT_ROUND_ODD', @@ -109,6 +119,7 @@ fptest_rounding_args = ['-r', 'all'] foreach k, v : softfloat_conv_tests test('fp-test-' + k, fptest, args: fptest_args + fptest_rounding_args + v.split(), + timeout: slow_fp_tests.get(k, 30), suite: ['softfloat', 'softfloat-conv']) endforeach @@ -116,6 +127,7 @@ foreach k, v : softfloat_tests test('fp-test-' + k, fptest, args: fptest_args + fptest_rounding_args + ['f16_' + k, 'f32_' + k, 'f64_' + k, 'f128_' + k, 'extF80_' + k], + timeout: slow_fp_tests.get(k, 30), suite: ['softfloat', 'softfloat-' + v]) endforeach @@ -124,7 +136,8 @@ test('fp-test-mulAdd', fptest, # no fptest_rounding_args args: fptest_args + ['f16_mulAdd', 'f32_mulAdd', 'f64_mulAdd', 'f128_mulAdd'], - suite: ['softfloat-slow', 'softfloat-ops-slow', 'slow'], timeout: 180) + timeout: slow_fp_tests.get('mulAdd', 30), + suite: ['softfloat-slow', 'softfloat-ops-slow', 'slow']) executable( 'fp-bench', @@ -140,4 +153,5 @@ fptestlog2 = executable( c_args: fpcflags, ) test('fp-test-log2', fptestlog2, + timeout: slow_fp_tests.get('log2', 30), suite: ['softfloat', 'softfloat-ops']) diff --git a/tests/functional/aspeed.py b/tests/functional/aspeed.py new file mode 100644 index 0000000..7a40d5d --- /dev/null +++ b/tests/functional/aspeed.py @@ -0,0 +1,58 @@ +# Test class to boot aspeed machines +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import exec_command_and_wait_for_pattern +from qemu_test import LinuxKernelTest + +class AspeedTest(LinuxKernelTest): + + def do_test_arm_aspeed_openbmc(self, machine, image, uboot='2019.04', + cpu_id='0x0', soc='AST2500 rev A1'): + hostname = machine.removesuffix('-bmc') + + self.set_machine(machine) + self.vm.set_console() + self.vm.add_args('-drive', f'file={image},if=mtd,format=raw', + '-snapshot') + self.vm.launch() + + self.wait_for_console_pattern(f'U-Boot {uboot}') + self.wait_for_console_pattern('## Loading kernel from FIT Image') + self.wait_for_console_pattern('Starting kernel ...') + self.wait_for_console_pattern(f'Booting Linux on physical CPU {cpu_id}') + self.wait_for_console_pattern(f'ASPEED {soc}') + self.wait_for_console_pattern('/init as init process') + self.wait_for_console_pattern(f'systemd[1]: Hostname set to <{hostname}>.') + + def do_test_arm_aspeed_buildroot_start(self, image, cpu_id, pattern='Aspeed EVB'): + self.require_netdev('user') + self.vm.set_console() + self.vm.add_args('-drive', 'file=' + image + ',if=mtd,format=raw,read-only=true', + '-net', 'nic', '-net', 'user') + self.vm.launch() + + self.wait_for_console_pattern('U-Boot 2019.04') + self.wait_for_console_pattern('## Loading kernel from FIT Image') + self.wait_for_console_pattern('Starting kernel ...') + self.wait_for_console_pattern('Booting Linux on physical CPU ' + cpu_id) + self.wait_for_console_pattern('lease of 10.0.2.15') + # the line before login: + self.wait_for_console_pattern(pattern) + exec_command_and_wait_for_pattern(self, 'root', 'Password:') + exec_command_and_wait_for_pattern(self, 'passw0rd', '#') + + def do_test_arm_aspeed_buildroot_poweroff(self): + exec_command_and_wait_for_pattern(self, 'poweroff', + 'System halted') + + def do_test_arm_aspeed_sdk_start(self, image): + self.require_netdev('user') + self.vm.set_console() + self.vm.add_args('-drive', 'file=' + image + ',if=mtd,format=raw', + '-net', 'nic', '-net', 'user', '-snapshot') + self.vm.launch() + + self.wait_for_console_pattern('U-Boot 2019.04') + self.wait_for_console_pattern('## Loading kernel from FIT Image') + self.wait_for_console_pattern('Starting kernel ...') diff --git a/tests/functional/meson.build b/tests/functional/meson.build index cda89c4..b542b3a 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -11,15 +11,58 @@ endif # Timeouts for individual tests that can be slow e.g. with debugging enabled test_timeouts = { - 'aarch64_sbsaref' : 600, + 'aarch64_aspeed_ast2700' : 600, + 'aarch64_aspeed_ast2700fc' : 600, + 'aarch64_device_passthrough' : 720, + 'aarch64_imx8mp_evk' : 240, + 'aarch64_raspi4' : 480, + 'aarch64_reverse_debug' : 180, + 'aarch64_rme_virt' : 1200, + 'aarch64_rme_sbsaref' : 1200, + 'aarch64_sbsaref_alpine' : 1200, + 'aarch64_sbsaref_freebsd' : 720, + 'aarch64_smmu' : 720, + 'aarch64_tuxrun' : 240, 'aarch64_virt' : 360, - 'acpi_bits' : 240, + 'aarch64_virt_gpu' : 480, + 'acpi_bits' : 420, + 'arm_aspeed_palmetto' : 120, + 'arm_aspeed_romulus' : 120, + 'arm_aspeed_witherspoon' : 120, + 'arm_aspeed_ast2500' : 720, + 'arm_aspeed_ast2600' : 1200, + 'arm_aspeed_bletchley' : 480, + 'arm_aspeed_rainier' : 480, + 'arm_bpim2u' : 500, + 'arm_collie' : 180, + 'arm_cubieboard' : 360, + 'arm_orangepi' : 540, + 'arm_quanta_gsj' : 240, + 'arm_raspi2' : 120, + 'arm_replay' : 240, + 'arm_tuxrun' : 240, + 'arm_sx1' : 360, + 'intel_iommu': 300, + 'mips_malta' : 480, + 'mipsel_malta' : 420, + 'mipsel_replay' : 480, + 'mips64_malta' : 240, + 'mips64el_malta' : 420, + 'mips64el_replay' : 180, 'netdev_ethtool' : 180, 'ppc_40p' : 240, 'ppc64_hv' : 1000, - 'ppc64_powernv' : 120, - 'ppc64_pseries' : 120, - 's390x_ccw_virtio' : 180, + 'ppc64_powernv' : 480, + 'ppc64_pseries' : 480, + 'ppc64_replay' : 210, + 'ppc64_tuxrun' : 420, + 'ppc64_mac99' : 120, + 'riscv64_tuxrun' : 120, + 's390x_ccw_virtio' : 420, + 'sh4_tuxrun' : 240, + 'virtio_balloon': 120, + 'x86_64_kvm_xen' : 180, + 'x86_64_replay' : 480, } tests_generic_system = [ @@ -34,14 +77,75 @@ tests_generic_linuxuser = [ tests_generic_bsduser = [ ] +tests_aarch64_system_quick = [ + 'migration', +] + tests_aarch64_system_thorough = [ + 'aarch64_aspeed_ast2700', + 'aarch64_aspeed_ast2700fc', + 'aarch64_device_passthrough', + 'aarch64_hotplug_pci', + 'aarch64_imx8mp_evk', + 'aarch64_raspi3', + 'aarch64_raspi4', + 'aarch64_replay', + 'aarch64_reverse_debug', + 'aarch64_rme_virt', + 'aarch64_rme_sbsaref', 'aarch64_sbsaref', + 'aarch64_sbsaref_alpine', + 'aarch64_sbsaref_freebsd', + 'aarch64_smmu', + 'aarch64_tcg_plugins', + 'aarch64_tuxrun', 'aarch64_virt', + 'aarch64_virt_gpu', + 'aarch64_xen', + 'aarch64_xlnx_versal', + 'multiprocess', +] + +tests_alpha_system_quick = [ + 'migration', +] + +tests_alpha_system_thorough = [ + 'alpha_clipper', + 'alpha_replay', +] + +tests_arm_system_quick = [ + 'migration', ] tests_arm_system_thorough = [ + 'arm_aspeed_ast1030', + 'arm_aspeed_palmetto', + 'arm_aspeed_romulus', + 'arm_aspeed_witherspoon', + 'arm_aspeed_ast2500', + 'arm_aspeed_ast2600', + 'arm_aspeed_bletchley', + 'arm_aspeed_rainier', + 'arm_bpim2u', 'arm_canona1100', + 'arm_collie', + 'arm_cubieboard', + 'arm_emcraft_sf2', 'arm_integratorcp', + 'arm_microbit', + 'arm_orangepi', + 'arm_quanta_gsj', + 'arm_raspi2', + 'arm_realview', + 'arm_replay', + 'arm_smdkc210', + 'arm_stellaris', + 'arm_sx1', + 'arm_vexpress', + 'arm_virt', + 'arm_tuxrun', ] tests_arm_linuxuser_thorough = [ @@ -50,6 +154,20 @@ tests_arm_linuxuser_thorough = [ tests_avr_system_thorough = [ 'avr_mega2560', + 'avr_uno', +] + +tests_hppa_system_quick = [ + 'hppa_seabios', +] + +tests_i386_system_quick = [ + 'migration', +] + +tests_i386_system_thorough = [ + 'i386_replay', + 'i386_tuxrun', ] tests_loongarch64_system_thorough = [ @@ -57,10 +175,15 @@ tests_loongarch64_system_thorough = [ ] tests_m68k_system_thorough = [ - 'm68k_nextcube' + 'm68k_mcf5208evb', + 'm68k_nextcube', + 'm68k_replay', + 'm68k_q800', + 'm68k_tuxrun', ] tests_microblaze_system_thorough = [ + 'microblaze_replay', 'microblaze_s3adsp1800' ] @@ -68,31 +191,84 @@ tests_microblazeel_system_thorough = [ 'microblazeel_s3adsp1800' ] -tests_mips64el_system_quick = [ - 'mips64el_fuloong2e', +tests_mips_system_thorough = [ + 'mips_malta', + 'mips_replay', + 'mips_tuxrun', +] + +tests_mipsel_system_thorough = [ + 'mipsel_malta', + 'mipsel_replay', + 'mipsel_tuxrun', +] + +tests_mips64_system_thorough = [ + 'mips64_malta', + 'mips64_tuxrun', ] tests_mips64el_system_thorough = [ + 'mips64el_fuloong2e', 'mips64el_loongson3v', + 'mips64el_malta', + 'mips64el_replay', + 'mips64el_tuxrun', +] + +tests_or1k_system_thorough = [ + 'or1k_replay', + 'or1k_sim', ] tests_ppc_system_quick = [ + 'migration', 'ppc_74xx', ] tests_ppc_system_thorough = [ - 'ppc_405', 'ppc_40p', 'ppc_amiga', 'ppc_bamboo', + 'ppc_mac', 'ppc_mpc8544ds', + 'ppc_replay', + 'ppc_sam460ex', + 'ppc_tuxrun', 'ppc_virtex_ml507', ] +tests_ppc64_system_quick = [ + 'migration', +] + tests_ppc64_system_thorough = [ + 'ppc64_e500', 'ppc64_hv', 'ppc64_powernv', 'ppc64_pseries', + 'ppc64_replay', + 'ppc64_reverse_debug', + 'ppc64_tuxrun', + 'ppc64_mac99', +] + +tests_riscv32_system_quick = [ + 'migration', + 'riscv_opensbi', +] + +tests_riscv32_system_thorough = [ + 'riscv32_tuxrun', +] + +tests_riscv64_system_quick = [ + 'migration', + 'riscv_opensbi', +] + +tests_riscv64_system_thorough = [ + 'riscv64_tuxrun', ] tests_rx_system_thorough = [ @@ -101,26 +277,68 @@ tests_rx_system_thorough = [ tests_s390x_system_thorough = [ 's390x_ccw_virtio', + 's390x_replay', 's390x_topology', + 's390x_tuxrun', +] + +tests_sh4_system_thorough = [ + 'sh4_r2d', + 'sh4_tuxrun', +] + +tests_sh4eb_system_thorough = [ + 'sh4eb_r2d', +] + +tests_sparc_system_quick = [ + 'migration', +] + +tests_sparc_system_thorough = [ + 'sparc_replay', + 'sparc_sun4m', +] + +tests_sparc64_system_quick = [ + 'migration', ] tests_sparc64_system_thorough = [ 'sparc64_sun4u', + 'sparc64_tuxrun', ] tests_x86_64_system_quick = [ 'cpu_queries', 'mem_addr_space', + 'migration', 'pc_cpu_hotplug_props', 'virtio_version', 'x86_cpu_model_versions', + 'vnc', + 'memlock', ] tests_x86_64_system_thorough = [ 'acpi_bits', + 'intel_iommu', 'linux_initrd', + 'multiprocess', 'netdev_ethtool', + 'virtio_balloon', 'virtio_gpu', + 'x86_64_hotplug_blk', + 'x86_64_hotplug_cpu', + 'x86_64_kvm_xen', + 'x86_64_replay', + 'x86_64_reverse_debug', + 'x86_64_tuxrun', +] + +tests_xtensa_system_thorough = [ + 'xtensa_lx60', + 'xtensa_replay', ] precache_all = [] @@ -189,12 +407,12 @@ foreach speed : ['quick', 'thorough'] # 'run_target' logic below & in Makefile.include test('func-' + testname, python, - depends: [test_deps, test_emulator, emulator_modules], + depends: [test_deps, test_emulator, emulator_modules, plugin_modules], env: test_env, args: [testpath], protocol: 'tap', - timeout: test_timeouts.get(test, 60), - priority: test_timeouts.get(test, 60), + timeout: test_timeouts.get(test, 90), + priority: test_timeouts.get(test, 90), suite: suites) endforeach endforeach @@ -202,4 +420,4 @@ endforeach run_target('precache-functional', depends: precache_all, - command: ['true']) + command: [python, '-c', '']) diff --git a/tests/functional/qemu_test/__init__.py b/tests/functional/qemu_test/__init__.py index f33282e..6e666a0 100644 --- a/tests/functional/qemu_test/__init__.py +++ b/tests/functional/qemu_test/__init__.py @@ -7,8 +7,14 @@ from .asset import Asset -from .config import BUILD_DIR -from .cmd import has_cmd, has_cmds, run_cmd, is_readable_executable_file, \ +from .config import BUILD_DIR, dso_suffix +from .cmd import is_readable_executable_file, \ interrupt_interactive_console_until_pattern, wait_for_console_pattern, \ - exec_command, exec_command_and_wait_for_pattern, get_qemu_img + exec_command, exec_command_and_wait_for_pattern, get_qemu_img, which from .testcase import QemuBaseTest, QemuUserTest, QemuSystemTest +from .linuxkernel import LinuxKernelTest +from .decorators import skipIfMissingCommands, skipIfNotMachine, \ + skipFlakyTest, skipUntrustedTest, skipBigDataTest, skipSlowTest, \ + skipIfMissingImports, skipIfOperatingSystem, skipLockedMemoryTest +from .archive import archive_extract +from .uncompress import uncompress diff --git a/tests/functional/qemu_test/archive.py b/tests/functional/qemu_test/archive.py new file mode 100644 index 0000000..c803fda --- /dev/null +++ b/tests/functional/qemu_test/archive.py @@ -0,0 +1,117 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Utilities for python-based QEMU tests +# +# Copyright 2024 Red Hat, Inc. +# +# Authors: +# Thomas Huth <thuth@redhat.com> + +import os +from subprocess import check_call, run, DEVNULL +import tarfile +from urllib.parse import urlparse +import zipfile + +from .asset import Asset + + +def tar_extract(archive, dest_dir, member=None): + with tarfile.open(archive) as tf: + if hasattr(tarfile, 'data_filter'): + tf.extraction_filter = getattr(tarfile, 'data_filter', + (lambda member, path: member)) + if member: + tf.extract(member=member, path=dest_dir) + else: + tf.extractall(path=dest_dir) + +def cpio_extract(archive, output_path): + cwd = os.getcwd() + os.chdir(output_path) + # Not passing 'check=True' as cpio exits with non-zero + # status if the archive contains any device nodes :-( + if type(archive) == str: + run(['cpio', '-i', '-F', archive], + stdout=DEVNULL, stderr=DEVNULL) + else: + run(['cpio', '-i'], + input=archive.read(), + stdout=DEVNULL, stderr=DEVNULL) + os.chdir(cwd) + +def zip_extract(archive, dest_dir, member=None): + with zipfile.ZipFile(archive, 'r') as zf: + if member: + zf.extract(member=member, path=dest_dir) + else: + zf.extractall(path=dest_dir) + +def deb_extract(archive, dest_dir, member=None): + cwd = os.getcwd() + os.chdir(dest_dir) + try: + proc = run(['ar', 't', archive], + check=True, capture_output=True, encoding='utf8') + file_path = proc.stdout.split()[2] + check_call(['ar', 'x', archive, file_path], + stdout=DEVNULL, stderr=DEVNULL) + tar_extract(file_path, dest_dir, member) + finally: + os.chdir(cwd) + +''' +@params archive: filename, Asset, or file-like object to extract +@params dest_dir: target directory to extract into +@params member: optional member file to limit extraction to + +Extracts @archive into @dest_dir. All files are extracted +unless @member specifies a limit. + +If @format is None, heuristics will be applied to guess the format +from the filename or Asset URL. @format must be non-None if @archive +is a file-like object. +''' +def archive_extract(archive, dest_dir, format=None, member=None): + if format is None: + format = guess_archive_format(archive) + if type(archive) == Asset: + archive = str(archive) + + if format == "tar": + tar_extract(archive, dest_dir, member) + elif format == "zip": + zip_extract(archive, dest_dir, member) + elif format == "cpio": + if member is not None: + raise Exception("Unable to filter cpio extraction") + cpio_extract(archive, dest_dir) + elif format == "deb": + if type(archive) != str: + raise Exception("Unable to use file-like object with deb archives") + deb_extract(archive, dest_dir, "./" + member) + else: + raise Exception(f"Unknown archive format {format}") + +''' +@params archive: filename, or Asset to guess + +Guess the format of @compressed, raising an exception if +no format can be determined +''' +def guess_archive_format(archive): + if type(archive) == Asset: + archive = urlparse(archive.url).path + elif type(archive) != str: + raise Exception(f"Unable to guess archive format for {archive}") + + if ".tar." in archive or archive.endswith("tgz"): + return "tar" + elif archive.endswith(".zip"): + return "zip" + elif archive.endswith(".cpio"): + return "cpio" + elif archive.endswith(".deb") or archive.endswith(".udeb"): + return "deb" + else: + raise Exception(f"Unknown archive format for {archive}") diff --git a/tests/functional/qemu_test/asset.py b/tests/functional/qemu_test/asset.py index d3be2af..704b84d 100644 --- a/tests/functional/qemu_test/asset.py +++ b/tests/functional/qemu_test/asset.py @@ -8,14 +8,23 @@ import hashlib import logging import os -import subprocess +import stat import sys import unittest import urllib.request from time import sleep from pathlib import Path from shutil import copyfileobj +from urllib.error import HTTPError +class AssetError(Exception): + def __init__(self, asset, msg, transient=False): + self.url = asset.url + self.msg = msg + self.transient = transient + + def __str__(self): + return "%s: %s" % (self.url, self.msg) # Instances of this class must be declared as class level variables # starting with a name "ASSET_". This enables the pre-caching logic @@ -39,23 +48,38 @@ class Asset: return "Asset: url=%s hash=%s cache=%s" % ( self.url, self.hash, self.cache_file) + def __str__(self): + return str(self.cache_file) + def _check(self, cache_file): if self.hash is None: return True if len(self.hash) == 64: - sum_prog = 'sha256sum' + hl = hashlib.sha256() elif len(self.hash) == 128: - sum_prog = 'sha512sum' + hl = hashlib.sha512() else: - raise Exception("unknown hash type") + raise AssetError(self, "unknown hash type") - checksum = subprocess.check_output( - [sum_prog, str(cache_file)]).split()[0] - return self.hash == checksum.decode("utf-8") + # Calculate the hash of the file: + with open(cache_file, 'rb') as file: + while True: + chunk = file.read(1 << 20) + if not chunk: + break + hl.update(chunk) + + return self.hash == hl.hexdigest() def valid(self): return self.cache_file.exists() and self._check(self.cache_file) + def fetchable(self): + return not os.environ.get("QEMU_TEST_NO_DOWNLOAD", False) + + def available(self): + return self.valid() or self.fetchable() + def _wait_for_other_download(self, tmp_cache_file): # Another thread already seems to download the asset, so wait until # it is done, while also checking the size to see whether it is stuck @@ -94,8 +118,9 @@ class Asset: self.cache_file, self.url) return str(self.cache_file) - if os.environ.get("QEMU_TEST_NO_DOWNLOAD", False): - raise Exception("Asset cache is invalid and downloads disabled") + if not self.fetchable(): + raise AssetError(self, + "Asset cache is invalid and downloads disabled") self.log.info("Downloading %s to %s...", self.url, self.cache_file) tmp_cache_file = self.cache_file.with_suffix(".download") @@ -105,6 +130,20 @@ class Asset: with tmp_cache_file.open("xb") as dst: with urllib.request.urlopen(self.url) as resp: copyfileobj(resp, dst) + length_hdr = resp.getheader("Content-Length") + + # Verify downloaded file size against length metadata, if + # available. + if length_hdr is not None: + length = int(length_hdr) + fsize = tmp_cache_file.stat().st_size + if fsize != length: + self.log.error("Unable to download %s: " + "connection closed before " + "transfer complete (%d/%d)", + self.url, fsize, length) + tmp_cache_file.unlink() + continue break except FileExistsError: self.log.debug("%s already exists, " @@ -117,10 +156,23 @@ class Asset: tmp_cache_file) tmp_cache_file.unlink() continue + except HTTPError as e: + tmp_cache_file.unlink() + self.log.error("Unable to download %s: HTTP error %d", + self.url, e.code) + # Treat 404 as fatal, since it is highly likely to + # indicate a broken test rather than a transient + # server or networking problem + if e.code == 404: + raise AssetError(self, "Unable to download: " + "HTTP error %d" % e.code) + continue except Exception as e: - self.log.error("Unable to download %s: %s", self.url, e) tmp_cache_file.unlink() - raise + raise AssetError(self, "Unable to download: " % e) + + if not os.path.exists(tmp_cache_file): + raise AssetError(self, "Download retries exceeded", transient=True) try: # Set these just for informational purposes @@ -134,9 +186,10 @@ class Asset: if not self._check(tmp_cache_file): tmp_cache_file.unlink() - raise Exception("Hash of %s does not match %s" % - (self.url, self.hash)) + raise AssetError(self, "Hash does not match %s" % self.hash) tmp_cache_file.replace(self.cache_file) + # Remove write perms to stop tests accidentally modifying them + os.chmod(self.cache_file, stat.S_IRUSR | stat.S_IRGRP) self.log.info("Cached %s at %s" % (self.url, self.cache_file)) return str(self.cache_file) @@ -153,7 +206,13 @@ class Asset: for name, asset in vars(test.__class__).items(): if name.startswith("ASSET_") and type(asset) == Asset: log.info("Attempting to cache '%s'" % asset) - asset.fetch() + try: + asset.fetch() + except AssetError as e: + if not e.transient: + raise + log.error("%s: skipping asset precache" % e) + log.removeHandler(handler) def precache_suite(suite): diff --git a/tests/functional/qemu_test/cmd.py b/tests/functional/qemu_test/cmd.py index 3acd617..dc5f422 100644 --- a/tests/functional/qemu_test/cmd.py +++ b/tests/functional/qemu_test/cmd.py @@ -14,77 +14,93 @@ import logging import os import os.path -import subprocess -from .config import BUILD_DIR - -def has_cmd(name, args=None): - """ - This function is for use in a @skipUnless decorator, e.g.: - - @skipUnless(*has_cmd('sudo -n', ('sudo', '-n', 'true'))) - def test_something_that_needs_sudo(self): - ... - """ - - if args is None: - args = ('which', name) - - try: - _, stderr, exitcode = run_cmd(args) - except Exception as e: - exitcode = -1 - stderr = str(e) - - if exitcode != 0: - cmd_line = ' '.join(args) - err = f'{name} required, but "{cmd_line}" failed: {stderr.strip()}' - return (False, err) - else: - return (True, '') - -def has_cmds(*cmds): - """ - This function is for use in a @skipUnless decorator and - allows checking for the availability of multiple commands, e.g.: - - @skipUnless(*has_cmds(('cmd1', ('cmd1', '--some-parameter')), - 'cmd2', 'cmd3')) - def test_something_that_needs_cmd1_and_cmd2(self): - ... +def which(tool): + """ looks up the full path for @tool, returns None if not found + or if @tool does not have executable permissions. """ + paths=os.getenv('PATH') + for p in paths.split(os.path.pathsep): + p = os.path.join(p, tool) + if os.access(p, os.X_OK): + return p + return None - for cmd in cmds: - if isinstance(cmd, str): - cmd = (cmd,) +def is_readable_executable_file(path): + return os.path.isfile(path) and os.access(path, os.R_OK | os.X_OK) - ok, errstr = has_cmd(*cmd) - if not ok: - return (False, errstr) +# @test: functional test to fail if @failure is seen +# @vm: the VM whose console to process +# @success: a non-None string to look for +# @failure: a string to look for that triggers test failure, or None +# +# Read up to 1 line of text from @vm, looking for @success +# and optionally @failure. +# +# If @success or @failure are seen, immediately return True, +# even if end of line is not yet seen. ie remainder of the +# line is left unread. +# +# If end of line is seen, with neither @success or @failure +# return False +# +# If @failure is seen, then mark @test as failed +def _console_read_line_until_match(test, vm, success, failure): + msg = bytes([]) + done = False + while True: + c = vm.console_socket.recv(1) + if c is None: + done = True + test.fail( + f"EOF in console, expected '{success}'") + break + msg += c - return (True, '') + if success in msg: + done = True + break + if failure and failure in msg: + done = True + vm.console_socket.close() + test.fail( + f"'{failure}' found in console, expected '{success}'") -def run_cmd(args): - subp = subprocess.Popen(args, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - universal_newlines=True) - stdout, stderr = subp.communicate() - ret = subp.returncode + if c == b'\n': + break - return (stdout, stderr, ret) + console_logger = logging.getLogger('console') + try: + console_logger.debug(msg.decode().strip()) + except: + console_logger.debug(msg) -def is_readable_executable_file(path): - return os.path.isfile(path) and os.access(path, os.R_OK | os.X_OK) + return done def _console_interaction(test, success_message, failure_message, send_string, keep_sending=False, vm=None): assert not keep_sending or send_string + assert success_message or send_string + if vm is None: vm = test.vm - console = vm.console_file - console_logger = logging.getLogger('console') + + test.log.debug( + f"Console interaction: success_msg='{success_message}' " + + f"failure_msg='{failure_message}' send_string='{send_string}'") + + # We'll process console in bytes, to avoid having to + # deal with unicode decode errors from receiving + # partial utf8 byte sequences + success_message_b = None + if success_message is not None: + success_message_b = success_message.encode() + + failure_message_b = None + if failure_message is not None: + failure_message_b = failure_message.encode() + while True: if send_string: vm.console_socket.sendall(send_string.encode()) @@ -92,25 +108,15 @@ def _console_interaction(test, success_message, failure_message, send_string = None # send only once # Only consume console output if waiting for something - if success_message is None and failure_message is None: + if success_message is None: if send_string is None: break continue - try: - msg = console.readline().decode().strip() - except UnicodeDecodeError: - msg = None - if not msg: - continue - console_logger.debug(msg) - if success_message is None or success_message in msg: + if _console_read_line_until_match(test, vm, + success_message_b, + failure_message_b): break - if failure_message and failure_message in msg: - console.close() - fail = 'Failure message found in console: "%s". Expected: "%s"' % \ - (failure_message, success_message) - test.fail(fail) def interrupt_interactive_console_until_pattern(test, success_message, failure_message=None, @@ -135,6 +141,7 @@ def interrupt_interactive_console_until_pattern(test, success_message, :param interrupt_string: a string to send to the console before trying to read a new line """ + assert success_message _console_interaction(test, success_message, failure_message, interrupt_string, True) @@ -149,6 +156,7 @@ def wait_for_console_pattern(test, success_message, failure_message=None, :param success_message: if this message appears, test succeeds :param failure_message: if this message appears, test fails """ + assert success_message _console_interaction(test, success_message, failure_message, None, vm=vm) def exec_command(test, command): @@ -177,6 +185,7 @@ def exec_command_and_wait_for_pattern(test, command, :param success_message: if this message appears, test succeeds :param failure_message: if this message appears, test fails """ + assert success_message _console_interaction(test, success_message, failure_message, command + '\r') def get_qemu_img(test): @@ -184,10 +193,10 @@ def get_qemu_img(test): # If qemu-img has been built, use it, otherwise the system wide one # will be used. - qemu_img = os.path.join(BUILD_DIR, 'qemu-img') + qemu_img = test.build_file('qemu-img') if os.path.exists(qemu_img): return qemu_img - if has_cmd('qemu-img'): - return 'qemu-img' - test.skipTest('Could not find "qemu-img", which is required to ' - 'create temporary images') + qemu_img = which('qemu-img') + if qemu_img is not None: + return qemu_img + test.skipTest(f"qemu-img not found in build dir or '$PATH'") diff --git a/tests/functional/qemu_test/config.py b/tests/functional/qemu_test/config.py index edd75b7..6d4c9c3 100644 --- a/tests/functional/qemu_test/config.py +++ b/tests/functional/qemu_test/config.py @@ -13,6 +13,7 @@ import os from pathlib import Path +import platform def _source_dir(): @@ -34,3 +35,14 @@ def _build_dir(): raise Exception("Cannot identify build dir, set QEMU_BUILD_ROOT") BUILD_DIR = _build_dir() + +def dso_suffix(): + '''Return the dynamic libraries suffix for the current platform''' + + if platform.system() == "Darwin": + return "dylib" + + if platform.system() == "Windows": + return "dll" + + return "so" diff --git a/tests/functional/qemu_test/decorators.py b/tests/functional/qemu_test/decorators.py new file mode 100644 index 0000000..c0d1567 --- /dev/null +++ b/tests/functional/qemu_test/decorators.py @@ -0,0 +1,151 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Decorators useful in functional tests + +import importlib +import os +import platform +import resource +from unittest import skipIf, skipUnless + +from .cmd import which + +''' +Decorator to skip execution of a test if the list +of command binaries is not available in $PATH. +Example: + + @skipIfMissingCommands("mkisofs", "losetup") +''' +def skipIfMissingCommands(*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 +host operating system does match one of the prohibited +ones. +Example + + @skipIfOperatingSystem("Linux", "Darwin") +''' +def skipIfOperatingSystem(*args): + return skipIf(platform.system() in args, + 'running on an OS (%s) that is not able to run this test' % + ", ".join(args)) + +''' +Decorator to skip execution of a test if the current +host machine does not match one of the permitted +machines. +Example + + @skipIfNotMachine("x86_64", "aarch64") +''' +def skipIfNotMachine(*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 +the $QEMU_TEST_FLAKY_TESTS environment variable is set. +A bug URL must be provided that documents the observed +failure behaviour, so it can be tracked & re-evaluated +in future. + +Historical tests may be providing "None" as the bug_url +but this should not be done for new test. + +Example: + + @skipFlakyTest("https://gitlab.com/qemu-project/qemu/-/issues/NNN") +''' +def skipFlakyTest(bug_url): + if bug_url is None: + bug_url = "FIXME: reproduce flaky test and file bug report or remove" + return skipUnless(os.getenv('QEMU_TEST_FLAKY_TESTS'), + f'Test is unstable: {bug_url}') + +''' +Decorator to skip execution of tests which are likely +to execute untrusted commands on the host, or commands +which process untrusted code, unless the +$QEMU_TEST_ALLOW_UNTRUSTED_CODE env var is set. +Example: + + @skipUntrustedTest() +''' +def skipUntrustedTest(): + return skipUnless(os.getenv('QEMU_TEST_ALLOW_UNTRUSTED_CODE'), + 'Test runs untrusted code / processes untrusted data') + +''' +Decorator to skip execution of tests which need large +data storage (over around 500MB-1GB mark) on the host, +unless the $QEMU_TEST_ALLOW_LARGE_STORAGE environment +variable is set + +Example: + + @skipBigDataTest() +''' +def skipBigDataTest(): + return skipUnless(os.getenv('QEMU_TEST_ALLOW_LARGE_STORAGE'), + '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: + + @skipIfMissingImports("numpy", "cv2") +''' +def skipIfMissingImports(*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)) + +''' +Decorator to skip execution of a test if the system's +locked memory limit is below the required threshold. +Takes required locked memory threshold in kB. +Example: + + @skipLockedMemoryTest(2_097_152) +''' +def skipLockedMemoryTest(locked_memory): + # get memlock hard limit in bytes + _, ulimit_memory = resource.getrlimit(resource.RLIMIT_MEMLOCK) + + return skipUnless( + ulimit_memory == resource.RLIM_INFINITY or ulimit_memory >= locked_memory * 1024, + f'Test required {locked_memory} kB of available locked memory', + ) diff --git a/tests/functional/qemu_test/linuxkernel.py b/tests/functional/qemu_test/linuxkernel.py new file mode 100644 index 0000000..2aca0ee --- /dev/null +++ b/tests/functional/qemu_test/linuxkernel.py @@ -0,0 +1,52 @@ +# Test class for testing the boot process of a Linux kernel +# +# 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 hashlib +import urllib.request + +from .cmd import wait_for_console_pattern, exec_command_and_wait_for_pattern +from .testcase import QemuSystemTest +from .utils import get_usernet_hostfwd_port + + +class LinuxKernelTest(QemuSystemTest): + KERNEL_COMMON_COMMAND_LINE = 'printk.time=0 ' + + def wait_for_console_pattern(self, success_message, vm=None): + wait_for_console_pattern(self, success_message, + failure_message='Kernel panic - not syncing', + vm=vm) + + def launch_kernel(self, kernel, initrd=None, dtb=None, console_index=0, + wait_for=None): + self.vm.set_console(console_index=console_index) + self.vm.add_args('-kernel', kernel) + if initrd: + self.vm.add_args('-initrd', initrd) + if dtb: + self.vm.add_args('-dtb', dtb) + self.vm.launch() + if wait_for: + self.wait_for_console_pattern(wait_for) + + def check_http_download(self, filename, hashsum, guestport=8080, + pythoncmd='python3 -m http.server'): + exec_command_and_wait_for_pattern(self, + f'{pythoncmd} {guestport} & sleep 1', + f'Serving HTTP on 0.0.0.0 port {guestport}') + hl = hashlib.sha256() + hostport = get_usernet_hostfwd_port(self.vm) + url = f'http://localhost:{hostport}{filename}' + self.log.info(f'Downloading {url} ...') + with urllib.request.urlopen(url) as response: + while True: + chunk = response.read(1 << 20) + if not chunk: + break + hl.update(chunk) + + digest = hl.hexdigest() + self.log.info(f'sha256sum of download is {digest}.') + self.assertEqual(digest, hashsum) diff --git a/tests/functional/qemu_test/ports.py b/tests/functional/qemu_test/ports.py new file mode 100644 index 0000000..631b77a --- /dev/null +++ b/tests/functional/qemu_test/ports.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python3 +# +# Simple functional tests for VNC functionality +# +# Copyright 2018, 2024 Red Hat, Inc. +# +# 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 fcntl +import os +import socket + +from .config import BUILD_DIR +from typing import List + + +class Ports(): + + PORTS_ADDR = '127.0.0.1' + PORTS_RANGE_SIZE = 1024 + PORTS_START = 49152 + ((os.getpid() * PORTS_RANGE_SIZE) % 16384) + PORTS_END = PORTS_START + PORTS_RANGE_SIZE + + def __enter__(self): + lock_file = os.path.join(BUILD_DIR, "tests", "functional", "port_lock") + self.lock_fh = os.open(lock_file, os.O_CREAT) + fcntl.flock(self.lock_fh, fcntl.LOCK_EX) + return self + + def __exit__(self, exc_type, exc_value, traceback): + fcntl.flock(self.lock_fh, fcntl.LOCK_UN) + os.close(self.lock_fh) + + def check_bind(self, port: int) -> bool: + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock: + try: + sock.bind((self.PORTS_ADDR, port)) + except OSError: + return False + + return True + + def find_free_ports(self, count: int) -> List[int]: + result = [] + for port in range(self.PORTS_START, self.PORTS_END): + if self.check_bind(port): + result.append(port) + if len(result) >= count: + break + assert len(result) == count + return result + + def find_free_port(self) -> int: + return self.find_free_ports(1)[0] diff --git a/tests/functional/qemu_test/tesseract.py b/tests/functional/qemu_test/tesseract.py index c4087b7..ede6c65 100644 --- a/tests/functional/qemu_test/tesseract.py +++ b/tests/functional/qemu_test/tesseract.py @@ -5,29 +5,19 @@ # 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 re import logging +from subprocess import run -from . import has_cmd, run_cmd - -def tesseract_available(expected_version): - if not has_cmd('tesseract'): - return False - (stdout, stderr, ret) = run_cmd([ 'tesseract', '--version']) - if ret: - return False - version = stdout.split()[1] - return int(version.split('.')[0]) >= expected_version def tesseract_ocr(image_path, tesseract_args=''): console_logger = logging.getLogger('console') console_logger.debug(image_path) - (stdout, stderr, ret) = run_cmd(['tesseract', image_path, - 'stdout']) - if ret: + proc = run(['tesseract', image_path, 'stdout'], + capture_output=True, encoding='utf8') + if proc.returncode: return None lines = [] - for line in stdout.split('\n'): + for line in proc.stdout.split('\n'): sline = line.strip() if len(sline): console_logger.debug(sline) diff --git a/tests/functional/qemu_test/testcase.py b/tests/functional/qemu_test/testcase.py index aa01462..2082c6f 100644 --- a/tests/functional/qemu_test/testcase.py +++ b/tests/functional/qemu_test/testcase.py @@ -13,49 +13,224 @@ import logging import os -import subprocess +from pathlib import Path import pycotap +import shutil +from subprocess import run import sys +import tempfile import unittest import uuid from qemu.machine import QEMUMachine -from qemu.utils import kvm_available, tcg_available +from qemu.utils import hvf_available, kvm_available, tcg_available +from .archive import archive_extract from .asset import Asset -from .cmd import run_cmd -from .config import BUILD_DIR +from .config import BUILD_DIR, dso_suffix +from .uncompress import uncompress class QemuBaseTest(unittest.TestCase): - qemu_bin = os.getenv('QEMU_TEST_QEMU_BINARY') - arch = None - - workdir = None - log = None - logdir = None + ''' + @params compressed: filename, Asset, or file-like object to uncompress + @params format: optional compression format (gzip, lzma) + + Uncompresses @compressed into the scratch directory. + + If @format is None, heuristics will be applied to guess the format + from the filename or Asset URL. @format must be non-None if @uncompressed + is a file-like object. + + Returns the fully qualified path to the uncompressed file + ''' + def uncompress(self, compressed, format=None): + self.log.debug(f"Uncompress {compressed} format={format}") + if type(compressed) == Asset: + compressed.fetch() + + (name, ext) = os.path.splitext(str(compressed)) + uncompressed = self.scratch_file(os.path.basename(name)) + + uncompress(compressed, uncompressed, format) + + return uncompressed + + ''' + @params archive: filename, Asset, or file-like object to extract + @params format: optional archive format (tar, zip, deb, cpio) + @params sub_dir: optional sub-directory to extract into + @params member: optional member file to limit extraction to + + Extracts @archive into the scratch directory, or a directory beneath + named by @sub_dir. All files are extracted unless @member specifies + a limit. + + If @format is None, heuristics will be applied to guess the format + from the filename or Asset URL. @format must be non-None if @archive + is a file-like object. + + If @member is non-None, returns the fully qualified path to @member + ''' + def archive_extract(self, archive, format=None, sub_dir=None, member=None): + self.log.debug(f"Extract {archive} format={format}" + + f"sub_dir={sub_dir} member={member}") + if type(archive) == Asset: + archive.fetch() + if sub_dir is None: + archive_extract(archive, self.scratch_file(), format, member) + else: + archive_extract(archive, self.scratch_file(sub_dir), + format, member) + + if member is not None: + return self.scratch_file(member) + return None + + ''' + Create a temporary directory suitable for storing UNIX + socket paths. + + Returns: a tempfile.TemporaryDirectory instance + ''' + def socket_dir(self): + if self.socketdir is None: + self.socketdir = tempfile.TemporaryDirectory( + prefix="qemu_func_test_sock_") + return self.socketdir + + ''' + @params args list of zero or more subdirectories or file + + Construct a path for accessing a data file located + relative to the source directory that is the root for + functional tests. + + @args may be an empty list to reference the root dir + itself, may be a single element to reference a file in + the root directory, or may be multiple elements to + reference a file nested below. The path components + will be joined using the platform appropriate path + separator. + + Returns: string representing a file path + ''' + def data_file(self, *args): + return str(Path(Path(__file__).parent.parent, *args)) + + ''' + @params args list of zero or more subdirectories or file + + Construct a path for accessing a data file located + relative to the build directory root. + + @args may be an empty list to reference the build dir + itself, may be a single element to reference a file in + the build directory, or may be multiple elements to + reference a file nested below. The path components + will be joined using the platform appropriate path + separator. + + Returns: string representing a file path + ''' + def build_file(self, *args): + return str(Path(BUILD_DIR, *args)) + + ''' + @params args list of zero or more subdirectories or file + + Construct a path for accessing/creating a scratch file + located relative to a temporary directory dedicated to + this test case. The directory and its contents will be + purged upon completion of the test. + + @args may be an empty list to reference the scratch dir + itself, may be a single element to reference a file in + the scratch directory, or may be multiple elements to + reference a file nested below. The path components + will be joined using the platform appropriate path + separator. + + Returns: string representing a file path + ''' + def scratch_file(self, *args): + return str(Path(self.workdir, *args)) + + ''' + @params args list of zero or more subdirectories or file + + Construct a path for accessing/creating a log file + located relative to a temporary directory dedicated to + this test case. The directory and its log files will be + preserved upon completion of the test. + + @args may be an empty list to reference the log dir + itself, may be a single element to reference a file in + the log directory, or may be multiple elements to + reference a file nested below. The path components + will be joined using the platform appropriate path + separator. + + Returns: string representing a file path + ''' + def log_file(self, *args): + return str(Path(self.outputdir, *args)) + + ''' + @params plugin name + + Return the full path to the plugin taking into account any host OS + specific suffixes. + ''' + def plugin_file(self, plugin_name): + sfx = dso_suffix() + return os.path.join('tests', 'tcg', 'plugins', f'{plugin_name}.{sfx}') + + def assets_available(self): + for name, asset in vars(self.__class__).items(): + if name.startswith("ASSET_") and type(asset) == Asset: + if not asset.available(): + self.log.debug(f"Asset {asset.url} not available") + return False + return True - def setUp(self, bin_prefix): + def setUp(self): + self.qemu_bin = os.getenv('QEMU_TEST_QEMU_BINARY') self.assertIsNotNone(self.qemu_bin, 'QEMU_TEST_QEMU_BINARY must be set') self.arch = self.qemu_bin.split('-')[-1] + self.socketdir = None - self.workdir = os.path.join(BUILD_DIR, 'tests/functional', self.arch, - self.id()) + self.outputdir = self.build_file('tests', 'functional', + self.arch, self.id()) + self.workdir = os.path.join(self.outputdir, 'scratch') os.makedirs(self.workdir, exist_ok=True) - self.logdir = self.workdir + self.log_filename = self.log_file('base.log') self.log = logging.getLogger('qemu-test') self.log.setLevel(logging.DEBUG) - self._log_fh = logging.FileHandler(os.path.join(self.logdir, - 'base.log'), mode='w') + self._log_fh = logging.FileHandler(self.log_filename, mode='w') self._log_fh.setLevel(logging.DEBUG) fileFormatter = logging.Formatter( '%(asctime)s - %(levelname)s: %(message)s') self._log_fh.setFormatter(fileFormatter) self.log.addHandler(self._log_fh) + # Capture QEMUMachine logging + self.machinelog = logging.getLogger('qemu.machine') + self.machinelog.setLevel(logging.DEBUG) + self.machinelog.addHandler(self._log_fh) + + if not self.assets_available(): + self.skipTest('One or more assets is not available') + def tearDown(self): + if "QEMU_TEST_KEEP_SCRATCH" not in os.environ: + shutil.rmtree(self.workdir) + if self.socketdir is not None: + shutil.rmtree(self.socketdir.name) + self.socketdir = None + self.machinelog.removeHandler(self._log_fh) self.log.removeHandler(self._log_fh) def main(): @@ -68,24 +243,33 @@ class QemuBaseTest(unittest.TestCase): tr = pycotap.TAPTestRunner(message_log = pycotap.LogMode.LogToError, test_output_log = pycotap.LogMode.LogToError) - unittest.main(module = None, testRunner = tr, argv=["__dummy__", path]) + res = unittest.main(module = None, testRunner = tr, exit = False, + argv=["__dummy__", path]) + for (test, message) in res.result.errors + res.result.failures: + + if hasattr(test, "log_filename"): + print('More information on ' + test.id() + ' could be found here:' + '\n %s' % test.log_filename, file=sys.stderr) + if hasattr(test, 'console_log_name'): + print(' %s' % test.console_log_name, file=sys.stderr) + sys.exit(not res.result.wasSuccessful()) class QemuUserTest(QemuBaseTest): def setUp(self): - super().setUp('qemu-') + super().setUp() self._ldpath = [] def add_ldpath(self, ldpath): self._ldpath.append(os.path.abspath(ldpath)) def run_cmd(self, bin_path, args=[]): - return subprocess.run([self.qemu_bin] - + ["-L %s" % ldpath for ldpath in self._ldpath] - + [bin_path] - + args, - text=True, capture_output=True) + return run([self.qemu_bin] + + ["-L %s" % ldpath for ldpath in self._ldpath] + + [bin_path] + + args, + text=True, capture_output=True) class QemuSystemTest(QemuBaseTest): """Facilitates system emulation tests.""" @@ -97,12 +281,13 @@ class QemuSystemTest(QemuBaseTest): def setUp(self): self._vms = {} - super().setUp('qemu-system-') + super().setUp() console_log = logging.getLogger('console') console_log.setLevel(logging.DEBUG) - self._console_log_fh = logging.FileHandler(os.path.join(self.workdir, - 'console.log'), mode='w') + self.console_log_name = self.log_file('console.log') + self._console_log_fh = logging.FileHandler(self.console_log_name, + mode='w') self._console_log_fh.setLevel(logging.DEBUG) fileFormatter = logging.Formatter('%(asctime)s: %(message)s') self._console_log_fh.setFormatter(fileFormatter) @@ -111,7 +296,9 @@ class QemuSystemTest(QemuBaseTest): def set_machine(self, machinename): # TODO: We should use QMP to get the list of available machines if not self._machinehelp: - self._machinehelp = run_cmd([self.qemu_bin, '-M', 'help'])[0]; + self._machinehelp = run( + [self.qemu_bin, '-M', 'help'], + capture_output=True, check=True, encoding='utf8').stdout if self._machinehelp.find(machinename) < 0: self.skipTest('no support for machine ' + machinename) self.machine = machinename @@ -130,7 +317,9 @@ class QemuSystemTest(QemuBaseTest): :type accelerator: str """ checker = {'tcg': tcg_available, - 'kvm': kvm_available}.get(accelerator) + 'kvm': kvm_available, + 'hvf': hvf_available, + }.get(accelerator) if checker is None: self.skipTest("Don't know how to check for the presence " "of accelerator %s" % accelerator) @@ -139,22 +328,33 @@ class QemuSystemTest(QemuBaseTest): "available" % accelerator) def require_netdev(self, netdevname): - netdevhelp = run_cmd([self.qemu_bin, - '-M', 'none', '-netdev', 'help'])[0]; - if netdevhelp.find('\n' + netdevname + '\n') < 0: + help = run([self.qemu_bin, + '-M', 'none', '-netdev', 'help'], + capture_output=True, check=True, encoding='utf8').stdout; + if help.find('\n' + netdevname + '\n') < 0: self.skipTest('no support for " + netdevname + " networking') def require_device(self, devicename): - devhelp = run_cmd([self.qemu_bin, - '-M', 'none', '-device', 'help'])[0]; - if devhelp.find(devicename) < 0: + help = run([self.qemu_bin, + '-M', 'none', '-device', 'help'], + capture_output=True, check=True, encoding='utf8').stdout; + if help.find(devicename) < 0: self.skipTest('no support for device ' + devicename) def _new_vm(self, name, *args): - vm = QEMUMachine(self.qemu_bin, base_temp_dir=self.workdir) + vm = QEMUMachine(self.qemu_bin, + name=name, + base_temp_dir=self.workdir, + log_dir=self.log_file()) self.log.debug('QEMUMachine "%s" created', name) self.log.debug('QEMUMachine "%s" temp_dir: %s', name, vm.temp_dir) - self.log.debug('QEMUMachine "%s" log_dir: %s', name, vm.log_dir) + + sockpath = os.environ.get("QEMU_TEST_QMP_BACKDOOR", None) + if sockpath is not None: + vm.add_args("-chardev", + f"socket,id=backdoor,path={sockpath},server=on,wait=off", + "-mon", "chardev=backdoor,mode=control") + if args: vm.add_args(*args) return vm diff --git a/tests/functional/qemu_test/tuxruntest.py b/tests/functional/qemu_test/tuxruntest.py new file mode 100644 index 0000000..6c442ff --- /dev/null +++ b/tests/functional/qemu_test/tuxruntest.py @@ -0,0 +1,136 @@ +# Functional test that boots known good tuxboot images the same way +# that tuxrun (www.tuxrun.org) does. This tool is used by things like +# the LKFT project to run regression tests on kernels. +# +# Copyright (c) 2023 Linaro Ltd. +# +# Author: +# Alex Bennée <alex.bennee@linaro.org> +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import os + +from qemu_test import QemuSystemTest +from qemu_test import exec_command_and_wait_for_pattern +from qemu_test import wait_for_console_pattern +from qemu_test import which, get_qemu_img + +class TuxRunBaselineTest(QemuSystemTest): + + KERNEL_COMMON_COMMAND_LINE = 'printk.time=0' + # Tests are ~10-40s, allow for --debug/--enable-gcov overhead + timeout = 100 + + def setUp(self): + super().setUp() + + # We need zstd for all the tuxrun tests + if which('zstd') is None: + self.skipTest("zstd not found in $PATH") + + # Pre-init TuxRun specific settings: Most machines work with + # reasonable defaults but we sometimes need to tweak the + # config. To avoid open coding everything we store all these + # details in the metadata for each test. + + # The tuxboot tag matches the root directory + self.tuxboot = self.arch + + # Most Linux's use ttyS0 for their serial port + self.console = "ttyS0" + + # Does the machine shutdown QEMU nicely on "halt" + self.wait_for_shutdown = True + + self.root = "vda" + + # Occasionally we need extra devices to hook things up + self.extradev = None + + self.qemu_img = get_qemu_img(self) + + def wait_for_console_pattern(self, success_message, vm=None): + wait_for_console_pattern(self, success_message, + failure_message='Kernel panic - not syncing', + vm=vm) + + def fetch_tuxrun_assets(self, kernel_asset, rootfs_asset, dtb_asset=None): + """ + Fetch the TuxBoot assets. + """ + kernel_image = kernel_asset.fetch() + disk_image = self.uncompress(rootfs_asset) + dtb = dtb_asset.fetch() if dtb_asset is not None else None + + return (kernel_image, disk_image, dtb) + + def prepare_run(self, kernel, disk, drive, dtb=None, console_index=0): + """ + Setup to run and add the common parameters to the system + """ + self.vm.set_console(console_index=console_index) + + # all block devices are raw ext4's + blockdev = "driver=raw,file.driver=file," \ + + f"file.filename={disk},node-name=hd0" + + self.kcmd_line = self.KERNEL_COMMON_COMMAND_LINE + self.kcmd_line += f" root=/dev/{self.root}" + self.kcmd_line += f" console={self.console}" + + self.vm.add_args('-kernel', kernel, + '-append', self.kcmd_line, + '-blockdev', blockdev) + + # Sometimes we need extra devices attached + if self.extradev: + self.vm.add_args('-device', self.extradev) + + self.vm.add_args('-device', + f"{drive},drive=hd0") + + # Some machines need an explicit DTB + if dtb: + self.vm.add_args('-dtb', dtb) + + def run_tuxtest_tests(self, haltmsg): + """ + Wait for the system to boot up, wait for the login prompt and + then do a few things on the console. Trigger a shutdown and + wait to exit cleanly. + """ + ps1='root@tuxtest:~#' + self.wait_for_console_pattern(self.kcmd_line) + self.wait_for_console_pattern('tuxtest login:') + exec_command_and_wait_for_pattern(self, 'root', ps1) + exec_command_and_wait_for_pattern(self, 'cat /proc/interrupts', ps1) + exec_command_and_wait_for_pattern(self, 'cat /proc/self/maps', ps1) + exec_command_and_wait_for_pattern(self, 'uname -a', ps1) + exec_command_and_wait_for_pattern(self, 'halt', haltmsg) + + # Wait for VM to shut down gracefully if it can + if self.wait_for_shutdown: + self.vm.wait() + else: + self.vm.shutdown() + + def common_tuxrun(self, + kernel_asset, + rootfs_asset, + dtb_asset=None, + drive="virtio-blk-device", + haltmsg="reboot: System halted", + console_index=0): + """ + Common path for LKFT tests. Unless we need to do something + special with the command line we can process most things using + the tag metadata. + """ + (kernel, disk, dtb) = self.fetch_tuxrun_assets(kernel_asset, rootfs_asset, + dtb_asset) + + self.prepare_run(kernel, disk, drive, dtb, console_index) + self.vm.launch() + self.run_tuxtest_tests(haltmsg) + os.remove(disk) diff --git a/tests/functional/qemu_test/uncompress.py b/tests/functional/qemu_test/uncompress.py new file mode 100644 index 0000000..b7ef8f7 --- /dev/null +++ b/tests/functional/qemu_test/uncompress.py @@ -0,0 +1,107 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Utilities for python-based QEMU tests +# +# Copyright 2024 Red Hat, Inc. +# +# Authors: +# Thomas Huth <thuth@redhat.com> + +import gzip +import lzma +import os +import stat +import shutil +from urllib.parse import urlparse +from subprocess import run, CalledProcessError + +from .asset import Asset + + +def gzip_uncompress(gz_path, output_path): + if os.path.exists(output_path): + return + with gzip.open(gz_path, 'rb') as gz_in: + try: + with open(output_path, 'wb') as raw_out: + shutil.copyfileobj(gz_in, raw_out) + except: + os.remove(output_path) + raise + +def lzma_uncompress(xz_path, output_path): + if os.path.exists(output_path): + return + with lzma.open(xz_path, 'rb') as lzma_in: + try: + with open(output_path, 'wb') as raw_out: + shutil.copyfileobj(lzma_in, raw_out) + except: + os.remove(output_path) + raise + + +def zstd_uncompress(zstd_path, output_path): + if os.path.exists(output_path): + return + + try: + run(['zstd', "-f", "-d", zstd_path, + "-o", output_path], capture_output=True, check=True) + except CalledProcessError as e: + os.remove(output_path) + raise Exception( + f"Unable to decompress zstd file {zstd_path} with {e}") from e + + # zstd copies source archive permissions for the output + # file, so must make this writable for QEMU + os.chmod(output_path, stat.S_IRUSR | stat.S_IWUSR) + + +''' +@params compressed: filename, Asset, or file-like object to uncompress +@params uncompressed: filename to uncompress into +@params format: optional compression format (gzip, lzma) + +Uncompresses @compressed into @uncompressed + +If @format is None, heuristics will be applied to guess the format +from the filename or Asset URL. @format must be non-None if @uncompressed +is a file-like object. + +Returns the fully qualified path to the uncompessed file +''' +def uncompress(compressed, uncompressed, format=None): + if format is None: + format = guess_uncompress_format(compressed) + + if format == "xz": + lzma_uncompress(str(compressed), uncompressed) + elif format == "gz": + gzip_uncompress(str(compressed), uncompressed) + elif format == "zstd": + zstd_uncompress(str(compressed), uncompressed) + else: + raise Exception(f"Unknown compression format {format}") + +''' +@params compressed: filename, Asset, or file-like object to guess + +Guess the format of @compressed, raising an exception if +no format can be determined +''' +def guess_uncompress_format(compressed): + if type(compressed) == Asset: + compressed = urlparse(compressed.url).path + elif type(compressed) != str: + raise Exception(f"Unable to guess compression cformat for {compressed}") + + (name, ext) = os.path.splitext(compressed) + if ext == ".xz": + return "xz" + elif ext == ".gz": + return "gz" + elif ext in [".zstd", ".zst"]: + return 'zstd' + else: + raise Exception(f"Unknown compression format for {compressed}") diff --git a/tests/functional/qemu_test/utils.py b/tests/functional/qemu_test/utils.py index 2a1cb60..e7c8de8 100644 --- a/tests/functional/qemu_test/utils.py +++ b/tests/functional/qemu_test/utils.py @@ -8,49 +8,32 @@ # 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 gzip -import lzma import os -import shutil -import subprocess -import tarfile -def archive_extract(archive, dest_dir, member=None): - with tarfile.open(archive) as tf: - if hasattr(tarfile, 'data_filter'): - tf.extraction_filter = getattr(tarfile, 'data_filter', - (lambda member, path: member)) - if member: - tf.extract(member=member, path=dest_dir) - else: - tf.extractall(path=dest_dir) +from qemu.utils import get_info_usernet_hostfwd_port -def gzip_uncompress(gz_path, output_path): - if os.path.exists(output_path): - return - with gzip.open(gz_path, 'rb') as gz_in: - try: - with open(output_path, 'wb') as raw_out: - shutil.copyfileobj(gz_in, raw_out) - except: - os.remove(output_path) - raise -def lzma_uncompress(xz_path, output_path): - if os.path.exists(output_path): - return - with lzma.open(xz_path, 'rb') as lzma_in: - try: - with open(output_path, 'wb') as raw_out: - shutil.copyfileobj(lzma_in, raw_out) - except: - os.remove(output_path) - raise +def get_usernet_hostfwd_port(vm): + res = vm.cmd('human-monitor-command', command_line='info usernet') + return get_info_usernet_hostfwd_port(res) -def cpio_extract(cpio_handle, output_path): - cwd = os.getcwd() - os.chdir(output_path) - subprocess.run(['cpio', '-i'], - input=cpio_handle.read(), - stderr=subprocess.DEVNULL) - os.chdir(cwd) +""" +Round up to next power of 2 +""" +def pow2ceil(x): + return 1 if x == 0 else 2**(x - 1).bit_length() + +def file_truncate(path, size): + if size != os.path.getsize(path): + with open(path, 'ab+') as fd: + fd.truncate(size) + +""" +Expand file size to next power of 2 +""" +def image_pow2ceil_expand(path): + size = os.path.getsize(path) + size_aligned = pow2ceil(size) + if size != size_aligned: + with open(path, 'ab+') as fd: + fd.truncate(size_aligned) diff --git a/tests/functional/replay_kernel.py b/tests/functional/replay_kernel.py new file mode 100644 index 0000000..80795eb --- /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(name='recording' if record else 'replay') + 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/avocado/reverse_debugging.py b/tests/functional/reverse_debugging.py index f24287c..f9a1d39 100644 --- a/tests/avocado/reverse_debugging.py +++ b/tests/functional/reverse_debugging.py @@ -1,5 +1,7 @@ # Reverse debugging test # +# SPDX-License-Identifier: GPL-2.0-or-later +# # Copyright (c) 2020 ISP RAS # # Author: @@ -10,14 +12,9 @@ import os import logging -from avocado import skipUnless -from avocado_qemu import BUILD_DIR -from avocado.utils import datadrainer -from avocado.utils import gdb -from avocado.utils import process -from avocado.utils.network.ports import find_free_port -from avocado.utils.path import find_command -from boot_linux_console import LinuxKernelTest +from qemu_test import LinuxKernelTest, get_qemu_img +from qemu_test.ports import Ports + class ReverseDebugging(LinuxKernelTest): """ @@ -36,8 +33,10 @@ class ReverseDebugging(LinuxKernelTest): endian_is_le = True def run_vm(self, record, shift, args, replay_path, image_path, port): + from avocado.utils import datadrainer + logger = logging.getLogger('replay') - vm = self.get_vm() + vm = self.get_vm(name='record' if record else 'replay') vm.set_console() if record: logger.info('recording the execution...') @@ -100,25 +99,25 @@ class ReverseDebugging(LinuxKernelTest): return vm.qmp('query-replay')['return']['icount'] def reverse_debugging(self, shift=7, args=None): + from avocado.utils import gdb + from avocado.utils import process + logger = logging.getLogger('replay') # create qcow2 for snapshots logger.info('creating qcow2 image for VM snapshots') image_path = os.path.join(self.workdir, 'disk.qcow2') - qemu_img = os.path.join(BUILD_DIR, 'qemu-img') - if not os.path.exists(qemu_img): - qemu_img = find_command('qemu-img', False) - if qemu_img is False: - self.cancel('Could not find "qemu-img", which is required to ' - 'create the temporary qcow2 image') + qemu_img = get_qemu_img(self) + if qemu_img is None: + self.skipTest('Could not find "qemu-img", which is required to ' + 'create the temporary qcow2 image') cmd = '%s create -f qcow2 %s 128M' % (qemu_img, image_path) process.run(cmd) replay_path = os.path.join(self.workdir, 'replay.bin') - port = find_free_port() # record the log - vm = self.run_vm(True, shift, args, replay_path, image_path, port) + vm = self.run_vm(True, shift, args, replay_path, image_path, -1) while self.vm_get_icount(vm) <= self.STEPS: pass last_icount = self.vm_get_icount(vm) @@ -127,7 +126,9 @@ class ReverseDebugging(LinuxKernelTest): logger.info("recorded log with %s+ steps" % last_icount) # replay and run debug commands - vm = self.run_vm(False, shift, args, replay_path, image_path, port) + with Ports() as ports: + port = ports.find_free_port() + vm = self.run_vm(False, shift, args, replay_path, image_path, port) logger.info('connecting to gdbstub') g = gdb.GDBRemote('127.0.0.1', port, False, False) g.connect() @@ -193,80 +194,3 @@ class ReverseDebugging(LinuxKernelTest): logger.info('exiting gdb and qemu') vm.shutdown() - -class ReverseDebugging_X86_64(ReverseDebugging): - """ - :avocado: tags=accel:tcg - """ - - REG_PC = 0x10 - REG_CS = 0x12 - def get_pc(self, g): - return self.get_reg_le(g, self.REG_PC) \ - + self.get_reg_le(g, self.REG_CS) * 0x10 - - # unidentified gitlab timeout problem - @skipUnless(os.getenv('QEMU_TEST_FLAKY_TESTS'), 'Test is unstable on GitLab') - def test_x86_64_pc(self): - """ - :avocado: tags=arch:x86_64 - :avocado: tags=machine:pc - """ - # start with BIOS only - self.reverse_debugging() - -class ReverseDebugging_AArch64(ReverseDebugging): - """ - :avocado: tags=accel:tcg - """ - - REG_PC = 32 - - # unidentified gitlab timeout problem - @skipUnless(os.getenv('QEMU_TEST_FLAKY_TESTS'), 'Test is unstable on GitLab') - def test_aarch64_virt(self): - """ - :avocado: tags=arch:aarch64 - :avocado: tags=machine:virt - :avocado: tags=cpu:cortex-a53 - """ - kernel_url = ('https://archives.fedoraproject.org/pub/archive/fedora' - '/linux/releases/29/Everything/aarch64/os/images/pxeboot' - '/vmlinuz') - kernel_hash = '8c73e469fc6ea06a58dc83a628fc695b693b8493' - kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) - - self.reverse_debugging( - args=('-kernel', kernel_path)) - -class ReverseDebugging_ppc64(ReverseDebugging): - """ - :avocado: tags=accel:tcg - """ - - REG_PC = 0x40 - - # unidentified gitlab timeout problem - @skipUnless(os.getenv('QEMU_TEST_FLAKY_TESTS'), 'Test is unstable on GitLab') - def test_ppc64_pseries(self): - """ - :avocado: tags=arch:ppc64 - :avocado: tags=machine:pseries - :avocado: tags=flaky - """ - # SLOF branches back to its entry point, which causes this test - # to take the 'hit a breakpoint again' path. That's not a problem, - # just slightly different than the other machines. - self.endian_is_le = False - self.reverse_debugging() - - # See https://gitlab.com/qemu-project/qemu/-/issues/1992 - @skipUnless(os.getenv('QEMU_TEST_FLAKY_TESTS'), 'Test is unstable on GitLab') - def test_ppc64_powernv(self): - """ - :avocado: tags=arch:ppc64 - :avocado: tags=machine:powernv - :avocado: tags=flaky - """ - self.endian_is_le = False - self.reverse_debugging() diff --git a/tests/functional/test_aarch64_aspeed_ast2700.py b/tests/functional/test_aarch64_aspeed_ast2700.py new file mode 100755 index 0000000..d02dc79 --- /dev/null +++ b/tests/functional/test_aarch64_aspeed_ast2700.py @@ -0,0 +1,140 @@ +#!/usr/bin/env python3 +# +# Functional test that boots the ASPEED SoCs with firmware +# +# Copyright (C) 2022 ASPEED Technology Inc +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import os + +from qemu_test import QemuSystemTest, Asset +from qemu_test import wait_for_console_pattern +from qemu_test import exec_command_and_wait_for_pattern + + +class AST2x00MachineSDK(QemuSystemTest): + + def do_test_aarch64_aspeed_sdk_start(self, image): + self.require_netdev('user') + self.vm.set_console() + self.vm.add_args('-device', + 'tmp105,bus=aspeed.i2c.bus.1,address=0x4d,id=tmp-test') + self.vm.add_args('-drive', 'file=' + image + ',if=mtd,format=raw', + '-net', 'nic', '-net', 'user', '-snapshot') + + self.vm.launch() + + def verify_vbootrom_firmware_flow(self): + wait_for_console_pattern(self, 'Found valid FIT image') + wait_for_console_pattern(self, '[uboot] loading') + wait_for_console_pattern(self, 'done') + wait_for_console_pattern(self, '[fdt] loading') + wait_for_console_pattern(self, 'done') + wait_for_console_pattern(self, '[tee] loading') + wait_for_console_pattern(self, 'done') + wait_for_console_pattern(self, '[atf] loading') + wait_for_console_pattern(self, 'done') + wait_for_console_pattern(self, 'Jumping to BL31 (Trusted Firmware-A)') + + def verify_openbmc_boot_and_login(self, name): + wait_for_console_pattern(self, 'U-Boot 2023.10') + wait_for_console_pattern(self, '## Loading kernel from FIT Image') + wait_for_console_pattern(self, 'Starting kernel ...') + + wait_for_console_pattern(self, f'{name} login:') + exec_command_and_wait_for_pattern(self, 'root', 'Password:') + exec_command_and_wait_for_pattern(self, '0penBmc', f'root@{name}:~#') + + ASSET_SDK_V906_AST2700 = Asset( + 'https://github.com/AspeedTech-BMC/openbmc/releases/download/v09.06/ast2700-a0-default-obmc.tar.gz', + '7247b6f19dbfb700686f8d9f723ac23f3eb229226c0589cb9b06b80d1b61f3cb') + + ASSET_SDK_V906_AST2700A1 = Asset( + 'https://github.com/AspeedTech-BMC/openbmc/releases/download/v09.06/ast2700-default-obmc.tar.gz', + 'f1d53e0be8a404ecce3e105f72bc50fa4e090ad13160ffa91b10a6e0233a9dc6') + + def do_ast2700_i2c_test(self): + exec_command_and_wait_for_pattern(self, + 'echo lm75 0x4d > /sys/class/i2c-dev/i2c-1/device/new_device ', + 'i2c i2c-1: new_device: Instantiated device lm75 at 0x4d') + exec_command_and_wait_for_pattern(self, + 'cat /sys/bus/i2c/devices/1-004d/hwmon/hwmon*/temp1_input', '0') + self.vm.cmd('qom-set', path='/machine/peripheral/tmp-test', + property='temperature', value=18000) + exec_command_and_wait_for_pattern(self, + 'cat /sys/bus/i2c/devices/1-004d/hwmon/hwmon*/temp1_input', '18000') + + def start_ast2700_test(self, name): + num_cpu = 4 + uboot_size = os.path.getsize(self.scratch_file(name, + 'u-boot-nodtb.bin')) + uboot_dtb_load_addr = hex(0x400000000 + uboot_size) + + load_images_list = [ + { + 'addr': '0x400000000', + 'file': self.scratch_file(name, + 'u-boot-nodtb.bin') + }, + { + 'addr': str(uboot_dtb_load_addr), + 'file': self.scratch_file(name, 'u-boot.dtb') + }, + { + 'addr': '0x430000000', + 'file': self.scratch_file(name, 'bl31.bin') + }, + { + 'addr': '0x430080000', + 'file': self.scratch_file(name, 'optee', + 'tee-raw.bin') + } + ] + + for load_image in load_images_list: + addr = load_image['addr'] + file = load_image['file'] + self.vm.add_args('-device', + f'loader,force-raw=on,addr={addr},file={file}') + + for i in range(num_cpu): + self.vm.add_args('-device', + f'loader,addr=0x430000000,cpu-num={i}') + + self.vm.add_args('-smp', str(num_cpu)) + self.do_test_aarch64_aspeed_sdk_start( + self.scratch_file(name, 'image-bmc')) + + def start_ast2700_test_vbootrom(self, name): + self.vm.add_args('-bios', 'ast27x0_bootrom.bin') + self.do_test_aarch64_aspeed_sdk_start( + self.scratch_file(name, 'image-bmc')) + + def test_aarch64_ast2700_evb_sdk_v09_06(self): + self.set_machine('ast2700-evb') + + self.archive_extract(self.ASSET_SDK_V906_AST2700) + self.start_ast2700_test('ast2700-a0-default') + self.verify_openbmc_boot_and_login('ast2700-a0-default') + self.do_ast2700_i2c_test() + + def test_aarch64_ast2700a1_evb_sdk_v09_06(self): + self.set_machine('ast2700a1-evb') + + self.archive_extract(self.ASSET_SDK_V906_AST2700A1) + self.start_ast2700_test('ast2700-default') + self.verify_openbmc_boot_and_login('ast2700-default') + self.do_ast2700_i2c_test() + + def test_aarch64_ast2700a1_evb_sdk_vbootrom_v09_06(self): + self.set_machine('ast2700a1-evb') + + self.archive_extract(self.ASSET_SDK_V906_AST2700A1) + self.start_ast2700_test_vbootrom('ast2700-default') + self.verify_vbootrom_firmware_flow() + self.verify_openbmc_boot_and_login('ast2700-default') + self.do_ast2700_i2c_test() + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/functional/test_aarch64_aspeed_ast2700fc.py b/tests/functional/test_aarch64_aspeed_ast2700fc.py new file mode 100755 index 0000000..b85370e --- /dev/null +++ b/tests/functional/test_aarch64_aspeed_ast2700fc.py @@ -0,0 +1,135 @@ +#!/usr/bin/env python3 +# +# Functional test that boots the ASPEED SoCs with firmware +# +# Copyright (C) 2022 ASPEED Technology Inc +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import os + +from qemu_test import QemuSystemTest, Asset +from qemu_test import wait_for_console_pattern +from qemu_test import exec_command_and_wait_for_pattern + + +class AST2x00MachineSDK(QemuSystemTest): + + def do_test_aarch64_aspeed_sdk_start(self, image): + self.require_netdev('user') + self.vm.set_console() + self.vm.add_args('-device', + 'tmp105,bus=aspeed.i2c.bus.1,address=0x4d,id=tmp-test') + self.vm.add_args('-drive', 'file=' + image + ',if=mtd,format=raw', + '-net', 'nic', '-net', 'user', '-snapshot') + + self.vm.launch() + + def verify_openbmc_boot_and_login(self, name): + wait_for_console_pattern(self, 'U-Boot 2023.10') + wait_for_console_pattern(self, '## Loading kernel from FIT Image') + wait_for_console_pattern(self, 'Starting kernel ...') + + wait_for_console_pattern(self, f'{name} login:') + exec_command_and_wait_for_pattern(self, 'root', 'Password:') + exec_command_and_wait_for_pattern(self, '0penBmc', f'root@{name}:~#') + + ASSET_SDK_V906_AST2700 = Asset( + 'https://github.com/AspeedTech-BMC/openbmc/releases/download/v09.06/ast2700-default-obmc.tar.gz', + 'f1d53e0be8a404ecce3e105f72bc50fa4e090ad13160ffa91b10a6e0233a9dc6') + + def do_ast2700_i2c_test(self): + exec_command_and_wait_for_pattern(self, + 'echo lm75 0x4d > /sys/class/i2c-dev/i2c-1/device/new_device ', + 'i2c i2c-1: new_device: Instantiated device lm75 at 0x4d') + exec_command_and_wait_for_pattern(self, + 'cat /sys/bus/i2c/devices/1-004d/hwmon/hwmon*/temp1_input', '0') + self.vm.cmd('qom-set', path='/machine/peripheral/tmp-test', + property='temperature', value=18000) + exec_command_and_wait_for_pattern(self, + 'cat /sys/bus/i2c/devices/1-004d/hwmon/hwmon*/temp1_input', '18000') + + def do_ast2700fc_ssp_test(self): + self.vm.shutdown() + self.vm.set_console(console_index=1) + self.vm.launch() + + exec_command_and_wait_for_pattern(self, '\012', 'ssp:~$') + exec_command_and_wait_for_pattern(self, 'version', + 'Zephyr version 3.7.1') + exec_command_and_wait_for_pattern(self, 'md 72c02000 1', + '[72c02000] 06010103') + + def do_ast2700fc_tsp_test(self): + self.vm.shutdown() + self.vm.set_console(console_index=2) + self.vm.launch() + + exec_command_and_wait_for_pattern(self, '\012', 'tsp:~$') + exec_command_and_wait_for_pattern(self, 'version', + 'Zephyr version 3.7.1') + exec_command_and_wait_for_pattern(self, 'md 72c02000 1', + '[72c02000] 06010103') + + def start_ast2700fc_test(self, name): + ca35_core = 4 + uboot_size = os.path.getsize(self.scratch_file(name, + 'u-boot-nodtb.bin')) + uboot_dtb_load_addr = hex(0x400000000 + uboot_size) + + load_images_list = [ + { + 'addr': '0x400000000', + 'file': self.scratch_file(name, + 'u-boot-nodtb.bin') + }, + { + 'addr': str(uboot_dtb_load_addr), + 'file': self.scratch_file(name, 'u-boot.dtb') + }, + { + 'addr': '0x430000000', + 'file': self.scratch_file(name, 'bl31.bin') + }, + { + 'addr': '0x430080000', + 'file': self.scratch_file(name, 'optee', + 'tee-raw.bin') + } + ] + + for load_image in load_images_list: + addr = load_image['addr'] + file = load_image['file'] + self.vm.add_args('-device', + f'loader,force-raw=on,addr={addr},file={file}') + + for i in range(ca35_core): + self.vm.add_args('-device', + f'loader,addr=0x430000000,cpu-num={i}') + + load_elf_list = { + 'ssp': self.scratch_file(name, 'zephyr-aspeed-ssp.elf'), + 'tsp': self.scratch_file(name, 'zephyr-aspeed-tsp.elf') + } + + for cpu_num, key in enumerate(load_elf_list, start=4): + file = load_elf_list[key] + self.vm.add_args('-device', + f'loader,file={file},cpu-num={cpu_num}') + + self.do_test_aarch64_aspeed_sdk_start( + self.scratch_file(name, 'image-bmc')) + + def test_aarch64_ast2700fc_sdk_v09_06(self): + self.set_machine('ast2700fc') + + self.archive_extract(self.ASSET_SDK_V906_AST2700) + self.start_ast2700fc_test('ast2700-default') + self.verify_openbmc_boot_and_login('ast2700-default') + self.do_ast2700_i2c_test() + self.do_ast2700fc_ssp_test() + self.do_ast2700fc_tsp_test() + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/functional/test_aarch64_device_passthrough.py b/tests/functional/test_aarch64_device_passthrough.py new file mode 100755 index 0000000..1f3f158 --- /dev/null +++ b/tests/functional/test_aarch64_device_passthrough.py @@ -0,0 +1,142 @@ +#!/usr/bin/env python3 +# +# Boots a nested guest and compare content of a device (passthrough) to a +# reference image. Both vfio group and iommufd passthrough methods are tested. +# +# Copyright (c) 2025 Linaro Ltd. +# +# Author: Pierrick Bouvier <pierrick.bouvier@linaro.org> +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import os + +from qemu_test import QemuSystemTest, Asset +from qemu_test import exec_command, wait_for_console_pattern +from qemu_test import exec_command_and_wait_for_pattern +from random import randbytes + +guest_script = ''' +#!/usr/bin/env bash + +set -euo pipefail +set -x + +# find disks from nvme serial +dev_vfio=$(lsblk --nvme | grep vfio | cut -f 1 -d ' ') +dev_iommufd=$(lsblk --nvme | grep iommufd | cut -f 1 -d ' ') +pci_vfio=$(basename $(readlink -f /sys/block/$dev_vfio/../../../)) +pci_iommufd=$(basename $(readlink -f /sys/block/$dev_iommufd/../../../)) + +# bind disks to vfio +for p in "$pci_vfio" "$pci_iommufd"; do + if [ "$(cat /sys/bus/pci/devices/$p/driver_override)" == vfio-pci ]; then + continue + fi + echo $p > /sys/bus/pci/drivers/nvme/unbind + echo vfio-pci > /sys/bus/pci/devices/$p/driver_override + echo $p > /sys/bus/pci/drivers/vfio-pci/bind +done + +# boot nested guest and execute /host/nested_guest.sh +# one disk is passed through vfio group, the other, through iommufd +qemu-system-aarch64 \ +-M virt \ +-display none \ +-serial stdio \ +-cpu host \ +-enable-kvm \ +-m 1G \ +-kernel /host/Image.gz \ +-drive format=raw,file=/host/guest.ext4,if=virtio \ +-append "root=/dev/vda init=/init -- bash /host/nested_guest.sh" \ +-virtfs local,path=/host,mount_tag=host,security_model=mapped,readonly=off \ +-device vfio-pci,host=$pci_vfio \ +-object iommufd,id=iommufd0 \ +-device vfio-pci,host=$pci_iommufd,iommufd=iommufd0 +''' + +nested_guest_script = ''' +#!/usr/bin/env bash + +set -euo pipefail +set -x + +image_vfio=/host/disk_vfio +image_iommufd=/host/disk_iommufd + +dev_vfio=$(lsblk --nvme | grep vfio | cut -f 1 -d ' ') +dev_iommufd=$(lsblk --nvme | grep iommufd | cut -f 1 -d ' ') + +# compare if devices are identical to original images +diff $image_vfio /dev/$dev_vfio +diff $image_iommufd /dev/$dev_iommufd + +echo device_passthrough_test_ok +''' + +class Aarch64DevicePassthrough(QemuSystemTest): + + # https://github.com/pbo-linaro/qemu-linux-stack + # + # Linux kernel is compiled with defconfig + + # IOMMUFD + VFIO_DEVICE_CDEV + ARM_SMMU_V3_IOMMUFD + # https://docs.kernel.org/driver-api/vfio.html#vfio-device-cde + ASSET_DEVICE_PASSTHROUGH_STACK = Asset( + ('https://fileserver.linaro.org/s/fx5DXxBYme8dw2G/' + 'download/device_passthrough.tar.xz'), + '812750b664d61c2986f2b149939ae28cafbd60d53e9c7e4b16e97143845e196d') + + # This tests the device passthrough implementation, by booting a VM + # supporting it with two nvme disks attached, and launching a nested VM + # reading their content. + def test_aarch64_device_passthrough(self): + self.set_machine('virt') + self.require_accelerator('tcg') + + self.vm.set_console() + + stack_path_tar_gz = self.ASSET_DEVICE_PASSTHROUGH_STACK.fetch() + self.archive_extract(stack_path_tar_gz, format="tar") + + stack = self.scratch_file('out') + kernel = os.path.join(stack, 'Image.gz') + rootfs_host = os.path.join(stack, 'host.ext4') + disk_vfio = os.path.join(stack, 'disk_vfio') + disk_iommufd = os.path.join(stack, 'disk_iommufd') + guest_cmd = os.path.join(stack, 'guest.sh') + nested_guest_cmd = os.path.join(stack, 'nested_guest.sh') + # we generate two random disks + with open(disk_vfio, "wb") as d: d.write(randbytes(512)) + with open(disk_iommufd, "wb") as d: d.write(randbytes(1024)) + with open(guest_cmd, 'w') as s: s.write(guest_script) + with open(nested_guest_cmd, 'w') as s: s.write(nested_guest_script) + + self.vm.add_args('-cpu', 'max') + self.vm.add_args('-m', '2G') + self.vm.add_args('-M', 'virt,' + 'virtualization=on,' + 'gic-version=max,' + 'iommu=smmuv3') + self.vm.add_args('-kernel', kernel) + self.vm.add_args('-drive', f'format=raw,file={rootfs_host}') + self.vm.add_args('-drive', + f'file={disk_vfio},if=none,id=vfio,format=raw') + self.vm.add_args('-device', 'nvme,serial=vfio,drive=vfio') + self.vm.add_args('-drive', + f'file={disk_iommufd},if=none,id=iommufd,format=raw') + self.vm.add_args('-device', 'nvme,serial=iommufd,drive=iommufd') + self.vm.add_args('-virtfs', + f'local,path={stack}/,mount_tag=host,' + 'security_model=mapped,readonly=off') + # boot and execute guest script + # init will trigger a kernel panic if script fails + self.vm.add_args('-append', + 'root=/dev/vda init=/init -- bash /host/guest.sh') + + self.vm.launch() + wait_for_console_pattern(self, 'device_passthrough_test_ok', + failure_message='Kernel panic') + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/functional/test_aarch64_hotplug_pci.py b/tests/functional/test_aarch64_hotplug_pci.py new file mode 100755 index 0000000..c9bb7f1 --- /dev/null +++ b/tests/functional/test_aarch64_hotplug_pci.py @@ -0,0 +1,72 @@ +#!/usr/bin/env python3 +# +# The test hotplugs a PCI device and checks it on a Linux guest. +# +# Copyright (c) 2025 Linaro Ltd. +# +# Author: +# Gustavo Romero <gustavo.romero@linaro.org> +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import LinuxKernelTest, Asset, exec_command_and_wait_for_pattern +from qemu_test import BUILD_DIR + +class HotplugPCI(LinuxKernelTest): + + ASSET_KERNEL = Asset( + ('https://ftp.debian.org/debian/dists/stable/main/installer-arm64/' + '20230607+deb12u11/images/netboot/debian-installer/arm64/linux'), + 'd92a60392ce1e379ca198a1a820899f8f0d39a62d047c41ab79492f81541a9d9') + + ASSET_INITRD = Asset( + ('https://ftp.debian.org/debian/dists/stable/main/installer-arm64/' + '20230607+deb12u11/images/netboot/debian-installer/arm64/initrd.gz'), + '9f817f76951f3237bca8216bee35267bfb826815687f4b2fcdd5e6c2a917790c') + + def test_hotplug_pci(self): + + self.set_machine('virt') + + self.vm.add_args('-m', '512M', + '-cpu', 'cortex-a57', + '-append', + 'console=ttyAMA0,115200 init=/bin/sh', + '-device', + 'pcie-root-port,bus=pcie.0,chassis=1,slot=1,id=pcie.1', + '-bios', + self.build_file('pc-bios', 'edk2-aarch64-code.fd')) + + # BusyBox prompt + prompt = "~ #" + self.launch_kernel(self.ASSET_KERNEL.fetch(), + self.ASSET_INITRD.fetch(), + wait_for=prompt) + + # Check for initial state: 2 network adapters, lo and enp0s1. + exec_command_and_wait_for_pattern(self, + 'ls /sys/class/net | wc -l', + '2') + + # Hotplug one network adapter to the root port, i.e. pcie.1 bus. + self.vm.cmd('device_add', + driver='virtio-net-pci', + bus='pcie.1', + addr=0, + id='na') + # Wait for the kernel to recognize the new device. + self.wait_for_console_pattern('virtio-pci') + self.wait_for_console_pattern('virtio_net') + + # Check if there is a new network adapter. + exec_command_and_wait_for_pattern(self, + 'ls /sys/class/net | wc -l', + '3') + + self.vm.cmd('device_del', id='na') + exec_command_and_wait_for_pattern(self, + 'ls /sys/class/net | wc -l', + '2') + +if __name__ == '__main__': + LinuxKernelTest.main() diff --git a/tests/functional/test_aarch64_imx8mp_evk.py b/tests/functional/test_aarch64_imx8mp_evk.py new file mode 100755 index 0000000..99ddcde --- /dev/null +++ b/tests/functional/test_aarch64_imx8mp_evk.py @@ -0,0 +1,68 @@ +#!/usr/bin/env python3 +# +# Functional test that boots a Linux kernel and checks the console +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import LinuxKernelTest, Asset + + +class Imx8mpEvkMachine(LinuxKernelTest): + + ASSET_IMAGE = Asset( + ('https://cloud.debian.org/images/cloud/bookworm/20231210-1590/' + 'debian-12-generic-arm64-20231210-1590.tar.xz'), + '7ebf1577b32d5af6204df74b54ca2e4675de9b5a9fa14f3ff70b88eeb7b3b359') + + KERNEL_OFFSET = 0x51000000 + KERNEL_SIZE = 32622528 + INITRD_OFFSET = 0x76000000 + INITRD_SIZE = 30987766 + DTB_OFFSET = 0x64F51000 + DTB_SIZE = 45 * 1024 + + def extract(self, in_path, out_path, offset, size): + try: + with open(in_path, "rb") as source: + source.seek(offset) + data = source.read(size) + with open(out_path, "wb") as target: + target.write(data) + except (IOError, ValueError) as e: + self.log.error(f"Failed to extract {out_path}: {e}") + raise + + def setUp(self): + super().setUp() + + self.image_path = self.scratch_file("disk.raw") + self.kernel_path = self.scratch_file("linux") + self.initrd_path = self.scratch_file("initrd.zstd") + self.dtb_path = self.scratch_file("imx8mp-evk.dtb") + + self.archive_extract(self.ASSET_IMAGE) + self.extract(self.image_path, self.kernel_path, + self.KERNEL_OFFSET, self.KERNEL_SIZE) + self.extract(self.image_path, self.initrd_path, + self.INITRD_OFFSET, self.INITRD_SIZE) + self.extract(self.image_path, self.dtb_path, + self.DTB_OFFSET, self.DTB_SIZE) + + def test_aarch64_imx8mp_evk_usdhc(self): + self.require_accelerator("tcg") + self.set_machine('imx8mp-evk') + self.vm.set_console(console_index=1) + self.vm.add_args('-m', '2G', + '-smp', '4', + '-kernel', self.kernel_path, + '-initrd', self.initrd_path, + '-dtb', self.dtb_path, + '-append', 'root=/dev/mmcblk2p1', + '-drive', f'file={self.image_path},if=sd,bus=2,' + 'format=raw,id=mmcblk2,snapshot=on') + + self.vm.launch() + self.wait_for_console_pattern('Welcome to ') + +if __name__ == '__main__': + LinuxKernelTest.main() diff --git a/tests/functional/test_aarch64_raspi3.py b/tests/functional/test_aarch64_raspi3.py new file mode 100755 index 0000000..74f6630 --- /dev/null +++ b/tests/functional/test_aarch64_raspi3.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python3 +# +# Functional test that boots a Linux kernel on a Raspberry Pi machine +# and checks the console +# +# Copyright (c) 2020 Philippe Mathieu-Daudé <f4bug@amsat.org> +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import LinuxKernelTest, Asset + + +class Aarch64Raspi3Machine(LinuxKernelTest): + + ASSET_RPI3_UEFI = Asset( + ('https://github.com/pbatard/RPi3/releases/download/' + 'v1.15/RPi3_UEFI_Firmware_v1.15.zip'), + '8cff2e979560048b4c84921f41a91893240b9fb71a88f0b5c5d6c8edd994bd5b') + + def test_aarch64_raspi3_atf(self): + efi_name = 'RPI_EFI.fd' + efi_fd = self.archive_extract(self.ASSET_RPI3_UEFI, member=efi_name) + + self.set_machine('raspi3b') + self.vm.set_console(console_index=1) + self.vm.add_args('-cpu', 'cortex-a53', + '-nodefaults', + '-device', f'loader,file={efi_fd},force-raw=true') + self.vm.launch() + self.wait_for_console_pattern('version UEFI Firmware v1.15') + + +if __name__ == '__main__': + LinuxKernelTest.main() diff --git a/tests/functional/test_aarch64_raspi4.py b/tests/functional/test_aarch64_raspi4.py new file mode 100755 index 0000000..7a4302b --- /dev/null +++ b/tests/functional/test_aarch64_raspi4.py @@ -0,0 +1,96 @@ +#!/usr/bin/env python3 +# +# Functional test that boots a Linux kernel on a Raspberry Pi machine +# and checks the console +# +# 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 Aarch64Raspi4Machine(LinuxKernelTest): + + """ + The kernel can be rebuilt using the kernel source referenced + and following the instructions on the on: + https://www.raspberrypi.org/documentation/linux/kernel/building.md + """ + ASSET_KERNEL_20190215 = Asset( + ('http://archive.raspberrypi.org/debian/' + 'pool/main/r/raspberrypi-firmware/' + 'raspberrypi-kernel_1.20230106-1_arm64.deb'), + '56d5713c8f6eee8a0d3f0e73600ec11391144fef318b08943e9abd94c0a9baf7') + + ASSET_INITRD = Asset( + ('https://github.com/groeck/linux-build-test/raw/' + '86b2be1384d41c8c388e63078a847f1e1c4cb1de/rootfs/' + 'arm64/rootfs.cpio.gz'), + '7c0b16d1853772f6f4c3ca63e789b3b9ff4936efac9c8a01fb0c98c05c7a7648') + + def test_arm_raspi4(self): + kernel_path = self.archive_extract(self.ASSET_KERNEL_20190215, + member='boot/kernel8.img') + dtb_path = self.archive_extract(self.ASSET_KERNEL_20190215, + member='boot/bcm2711-rpi-4-b.dtb') + + self.set_machine('raspi4b') + self.vm.set_console() + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'earlycon=pl011,mmio32,0xfe201000 ' + + 'console=ttyAMA0,115200 ' + + 'root=/dev/mmcblk1p2 rootwait ' + + 'dwc_otg.fiq_fsm_enable=0') + self.vm.add_args('-kernel', kernel_path, + '-dtb', dtb_path, + '-append', kernel_command_line) + # When PCI is supported we can add a USB controller: + # '-device', 'qemu-xhci,bus=pcie.1,id=xhci', + # '-device', 'usb-kbd,bus=xhci.0', + self.vm.launch() + console_pattern = 'Kernel command line: %s' % kernel_command_line + self.wait_for_console_pattern(console_pattern) + # When USB is enabled we can look for this + # console_pattern = 'Product: QEMU USB Keyboard' + # self.wait_for_console_pattern(console_pattern) + console_pattern = 'Waiting for root device' + self.wait_for_console_pattern(console_pattern) + + + def test_arm_raspi4_initrd(self): + kernel_path = self.archive_extract(self.ASSET_KERNEL_20190215, + member='boot/kernel8.img') + dtb_path = self.archive_extract(self.ASSET_KERNEL_20190215, + member='boot/bcm2711-rpi-4-b.dtb') + initrd_path = self.uncompress(self.ASSET_INITRD) + + self.set_machine('raspi4b') + self.vm.set_console() + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'earlycon=pl011,mmio32,0xfe201000 ' + + 'console=ttyAMA0,115200 ' + + 'panic=-1 noreboot ' + + 'dwc_otg.fiq_fsm_enable=0') + self.vm.add_args('-kernel', kernel_path, + '-dtb', dtb_path, + '-initrd', initrd_path, + '-append', kernel_command_line, + '-no-reboot') + # When PCI is supported we can add a USB controller: + # '-device', 'qemu-xhci,bus=pcie.1,id=xhci', + # '-device', 'usb-kbd,bus=xhci.0', + self.vm.launch() + self.wait_for_console_pattern('Boot successful.') + + exec_command_and_wait_for_pattern(self, 'cat /proc/cpuinfo', + 'BCM2835') + exec_command_and_wait_for_pattern(self, 'cat /proc/iomem', + 'cprman@7e101000') + exec_command_and_wait_for_pattern(self, 'halt', 'reboot: System halted') + # TODO: Raspberry Pi4 doesn't shut down properly with recent kernels + # Wait for VM to shut down gracefully + #self.vm.wait() + + +if __name__ == '__main__': + LinuxKernelTest.main() diff --git a/tests/functional/test_aarch64_replay.py b/tests/functional/test_aarch64_replay.py new file mode 100755 index 0000000..db12e76 --- /dev/null +++ b/tests/functional/test_aarch64_replay.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python3 +# +# Replay test that boots a Linux kernel on an aarch64 machine +# and checks the console +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from subprocess import check_call, DEVNULL + +from qemu_test import Asset, skipIfOperatingSystem, get_qemu_img +from replay_kernel import ReplayKernelBase + + +class Aarch64Replay(ReplayKernelBase): + + ASSET_KERNEL = Asset( + 'https://storage.tuxboot.com/buildroot/20241119/arm64/Image', + 'b74743c5e89e1cea0f73368d24ae0ae85c5204ff84be3b5e9610417417d2f235') + + ASSET_ROOTFS = Asset( + 'https://storage.tuxboot.com/buildroot/20241119/arm64/rootfs.ext4.zst', + 'a1acaaae2068df4648d04ff75f532aaa8c5edcd6b936122b6f0db4848a07b465') + + def test_aarch64_virt(self): + self.require_netdev('user') + self.set_machine('virt') + self.cpu = 'cortex-a57' + kernel_path = self.ASSET_KERNEL.fetch() + + raw_disk = self.uncompress(self.ASSET_ROOTFS) + disk = self.scratch_file('scratch.qcow2') + qemu_img = get_qemu_img(self) + check_call([qemu_img, 'create', '-f', 'qcow2', '-b', raw_disk, + '-F', 'raw', disk], stdout=DEVNULL, stderr=DEVNULL) + + args = ('-drive', 'file=%s,snapshot=on,id=hd0,if=none' % disk, + '-drive', 'driver=blkreplay,id=hd0-rr,if=none,image=hd0', + '-device', 'virtio-blk-device,drive=hd0-rr', + '-netdev', 'user,id=vnet,hostfwd=:127.0.0.1:0-:22', + '-device', 'virtio-net,netdev=vnet', + '-object', 'filter-replay,id=replay,netdev=vnet') + + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'console=ttyAMA0 root=/dev/vda') + console_pattern = 'Welcome to TuxTest' + self.run_rr(kernel_path, kernel_command_line, console_pattern, + args=args) + + +if __name__ == '__main__': + ReplayKernelBase.main() diff --git a/tests/functional/test_aarch64_reverse_debug.py b/tests/functional/test_aarch64_reverse_debug.py new file mode 100755 index 0000000..58d4532 --- /dev/null +++ b/tests/functional/test_aarch64_reverse_debug.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python3 +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Reverse debugging test +# +# Copyright (c) 2020 ISP RAS +# +# Author: +# Pavel Dovgalyuk <Pavel.Dovgalyuk@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. + +from qemu_test import Asset, skipIfMissingImports, skipFlakyTest +from reverse_debugging import ReverseDebugging + + +@skipIfMissingImports('avocado.utils') +class ReverseDebugging_AArch64(ReverseDebugging): + + REG_PC = 32 + + KERNEL_ASSET = Asset( + ('https://archives.fedoraproject.org/pub/archive/fedora/linux/' + 'releases/29/Everything/aarch64/os/images/pxeboot/vmlinuz'), + '7e1430b81c26bdd0da025eeb8fbd77b5dc961da4364af26e771bd39f379cbbf7') + + @skipFlakyTest("https://gitlab.com/qemu-project/qemu/-/issues/2921") + def test_aarch64_virt(self): + self.set_machine('virt') + self.cpu = 'cortex-a53' + kernel_path = self.KERNEL_ASSET.fetch() + self.reverse_debugging(args=('-kernel', kernel_path)) + + +if __name__ == '__main__': + ReverseDebugging.main() diff --git a/tests/functional/test_aarch64_rme_sbsaref.py b/tests/functional/test_aarch64_rme_sbsaref.py new file mode 100755 index 0000000..746770e --- /dev/null +++ b/tests/functional/test_aarch64_rme_sbsaref.py @@ -0,0 +1,69 @@ +#!/usr/bin/env python3 +# +# Functional test that boots a Realms environment on sbsa-ref machine and a +# nested guest VM using it. +# +# Copyright (c) 2024 Linaro Ltd. +# +# Author: Pierrick Bouvier <pierrick.bouvier@linaro.org> +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import os + +from qemu_test import QemuSystemTest, Asset, wait_for_console_pattern +from qemu_test import exec_command_and_wait_for_pattern +from test_aarch64_rme_virt import test_realms_guest + + +class Aarch64RMESbsaRefMachine(QemuSystemTest): + + # Stack is built with OP-TEE build environment from those instructions: + # https://linaro.atlassian.net/wiki/spaces/QEMU/pages/29051027459/ + # https://github.com/pbo-linaro/qemu-rme-stack + ASSET_RME_STACK_SBSA = Asset( + ('https://fileserver.linaro.org/s/KJyeBxL82mz2r7F/' + 'download/rme-stack-op-tee-4.2.0-cca-v4-sbsa.tar.gz'), + 'dd9ab28ec869bdf3b5376116cb3689103b43433fd5c4bca0f4a8d8b3c104999e') + + # This tests the FEAT_RME cpu implementation, by booting a VM supporting it, + # and launching a nested VM using it. + def test_aarch64_rme_sbsaref(self): + self.set_machine('sbsa-ref') + self.require_accelerator('tcg') + self.require_netdev('user') + + self.vm.set_console() + + stack_path_tar_gz = self.ASSET_RME_STACK_SBSA.fetch() + self.archive_extract(stack_path_tar_gz, format="tar") + + rme_stack = self.scratch_file('rme-stack-op-tee-4.2.0-cca-v4-sbsa') + pflash0 = os.path.join(rme_stack, 'images', 'SBSA_FLASH0.fd') + pflash1 = os.path.join(rme_stack, 'images', 'SBSA_FLASH1.fd') + virtual = os.path.join(rme_stack, 'images', 'disks', 'virtual') + drive = os.path.join(rme_stack, 'out-br', 'images', 'rootfs.ext4') + + self.vm.add_args('-cpu', 'max,x-rme=on,pauth-impdef=on') + self.vm.add_args('-m', '2G') + self.vm.add_args('-M', 'sbsa-ref') + self.vm.add_args('-drive', f'file={pflash0},format=raw,if=pflash') + self.vm.add_args('-drive', f'file={pflash1},format=raw,if=pflash') + self.vm.add_args('-drive', f'file=fat:rw:{virtual},format=raw') + self.vm.add_args('-drive', f'format=raw,if=none,file={drive},id=hd0') + self.vm.add_args('-device', 'virtio-blk-pci,drive=hd0') + self.vm.add_args('-device', 'virtio-9p-pci,fsdev=shr0,mount_tag=shr0') + self.vm.add_args('-fsdev', f'local,security_model=none,path={rme_stack},id=shr0') + self.vm.add_args('-device', 'virtio-net-pci,netdev=net0') + self.vm.add_args('-netdev', 'user,id=net0') + + self.vm.launch() + # Wait for host VM boot to complete. + wait_for_console_pattern(self, 'Welcome to Buildroot', + failure_message='Synchronous Exception at') + exec_command_and_wait_for_pattern(self, 'root', '#') + + test_realms_guest(self) + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/functional/test_aarch64_rme_virt.py b/tests/functional/test_aarch64_rme_virt.py new file mode 100755 index 0000000..8452d27 --- /dev/null +++ b/tests/functional/test_aarch64_rme_virt.py @@ -0,0 +1,101 @@ +#!/usr/bin/env python3 +# +# Functional test that boots a Realms environment on virt machine and a nested +# guest VM using it. +# +# Copyright (c) 2024 Linaro Ltd. +# +# Author: Pierrick Bouvier <pierrick.bouvier@linaro.org> +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import os + +from qemu_test import QemuSystemTest, Asset +from qemu_test import exec_command, wait_for_console_pattern +from qemu_test import exec_command_and_wait_for_pattern + +def test_realms_guest(test_rme_instance): + + # Boot the (nested) guest VM + exec_command(test_rme_instance, + 'qemu-system-aarch64 -M virt,gic-version=3 ' + '-cpu host -enable-kvm -m 512M ' + '-M confidential-guest-support=rme0 ' + '-object rme-guest,id=rme0 ' + '-device virtio-net-pci,netdev=net0,romfile= ' + '-netdev user,id=net0 ' + '-kernel /mnt/out/bin/Image ' + '-initrd /mnt/out-br/images/rootfs.cpio ' + '-serial stdio') + # Detect Realm activation during (nested) guest boot. + wait_for_console_pattern(test_rme_instance, + 'SMC_RMI_REALM_ACTIVATE') + # Wait for (nested) guest boot to complete. + wait_for_console_pattern(test_rme_instance, + 'Welcome to Buildroot') + exec_command_and_wait_for_pattern(test_rme_instance, 'root', '#') + # query (nested) guest cca report + exec_command(test_rme_instance, 'cca-workload-attestation report') + wait_for_console_pattern(test_rme_instance, + '"cca-platform-hash-algo-id": "sha-256"') + wait_for_console_pattern(test_rme_instance, + '"cca-realm-hash-algo-id": "sha-512"') + wait_for_console_pattern(test_rme_instance, + '"cca-realm-public-key-hash-algo-id": "sha-256"') + +class Aarch64RMEVirtMachine(QemuSystemTest): + + # Stack is built with OP-TEE build environment from those instructions: + # https://linaro.atlassian.net/wiki/spaces/QEMU/pages/29051027459/ + # https://github.com/pbo-linaro/qemu-rme-stack + ASSET_RME_STACK_VIRT = Asset( + ('https://fileserver.linaro.org/s/iaRsNDJp2CXHMSJ/' + 'download/rme-stack-op-tee-4.2.0-cca-v4-qemu_v8.tar.gz'), + '1851adc232b094384d8b879b9a2cfff07ef3d6205032b85e9b3a4a9ae6b0b7ad') + + # This tests the FEAT_RME cpu implementation, by booting a VM supporting it, + # and launching a nested VM using it. + def test_aarch64_rme_virt(self): + self.set_machine('virt') + self.require_accelerator('tcg') + self.require_netdev('user') + + self.vm.set_console() + + stack_path_tar_gz = self.ASSET_RME_STACK_VIRT.fetch() + self.archive_extract(stack_path_tar_gz, format="tar") + + rme_stack = self.scratch_file('rme-stack-op-tee-4.2.0-cca-v4-qemu_v8') + kernel = os.path.join(rme_stack, 'out', 'bin', 'Image') + bios = os.path.join(rme_stack, 'out', 'bin', 'flash.bin') + drive = os.path.join(rme_stack, 'out-br', 'images', 'rootfs.ext4') + + self.vm.add_args('-cpu', 'max,x-rme=on,pauth-impdef=on') + self.vm.add_args('-m', '2G') + self.vm.add_args('-M', 'virt,acpi=off,' + 'virtualization=on,' + 'secure=on,' + 'gic-version=3') + self.vm.add_args('-bios', bios) + self.vm.add_args('-kernel', kernel) + self.vm.add_args('-drive', f'format=raw,if=none,file={drive},id=hd0') + self.vm.add_args('-device', 'virtio-blk-pci,drive=hd0') + self.vm.add_args('-device', 'virtio-9p-device,fsdev=shr0,mount_tag=shr0') + self.vm.add_args('-fsdev', f'local,security_model=none,path={rme_stack},id=shr0') + self.vm.add_args('-device', 'virtio-net-pci,netdev=net0') + self.vm.add_args('-netdev', 'user,id=net0') + # We need to add nokaslr to avoid triggering this sporadic bug: + # https://gitlab.com/qemu-project/qemu/-/issues/2823 + self.vm.add_args('-append', 'root=/dev/vda nokaslr') + + self.vm.launch() + # Wait for host VM boot to complete. + wait_for_console_pattern(self, 'Welcome to Buildroot', + failure_message='Synchronous Exception at') + exec_command_and_wait_for_pattern(self, 'root', '#') + + test_realms_guest(self) + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/functional/test_aarch64_sbsaref.py b/tests/functional/test_aarch64_sbsaref.py index f31c2a6..d3402f5 100755 --- a/tests/functional/test_aarch64_sbsaref.py +++ b/tests/functional/test_aarch64_sbsaref.py @@ -1,20 +1,49 @@ #!/usr/bin/env python3 # -# Functional test that boots a Linux kernel and checks the console +# 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 wait_for_console_pattern from qemu_test import interrupt_interactive_console_until_pattern -from qemu_test.utils import lzma_uncompress -from unittest import skipUnless + + +def fetch_firmware(test): + """ + Flash volumes generated using: + + Toolchain from Debian: + aarch64-linux-gnu-gcc (Debian 12.2.0-14) 12.2.0 + + Used components: + + - Trusted Firmware v2.12.0 + - Tianocore EDK2 edk2-stable202411 + - Tianocore EDK2-platforms 4b3530d + + """ + + # Secure BootRom (TF-A code) + fs0_path = test.uncompress(Aarch64SbsarefMachine.ASSET_FLASH0) + + # Non-secure rom (UEFI and EFI variables) + fs1_path = test.uncompress(Aarch64SbsarefMachine.ASSET_FLASH1) + + for path in [fs0_path, fs1_path]: + with open(path, "ab+") as fd: + fd.truncate(256 << 20) # Expand volumes to 256MiB + + test.vm.add_args( + "-drive", f"if=pflash,file={fs0_path},format=raw", + "-drive", f"if=pflash,file={fs1_path},format=raw", + ) class Aarch64SbsarefMachine(QemuSystemTest): @@ -27,54 +56,21 @@ class Aarch64SbsarefMachine(QemuSystemTest): ASSET_FLASH0 = Asset( ('https://artifacts.codelinaro.org/artifactory/linaro-419-sbsa-ref/' - '20240619-148232/edk2/SBSA_FLASH0.fd.xz'), - '0c954842a590988f526984de22e21ae0ab9cb351a0c99a8a58e928f0c7359cf7') + '20241122-189881/edk2/SBSA_FLASH0.fd.xz'), + '76eb89d42eebe324e4395329f47447cda9ac920aabcf99aca85424609c3384a5') ASSET_FLASH1 = Asset( ('https://artifacts.codelinaro.org/artifactory/linaro-419-sbsa-ref/' - '20240619-148232/edk2/SBSA_FLASH1.fd.xz'), - 'c6ec39374c4d79bb9e9cdeeb6db44732d90bb4a334cec92002b3f4b9cac4b5ee') + '20241122-189881/edk2/SBSA_FLASH1.fd.xz'), + 'f850f243bd8dbd49c51e061e0f79f1697546938f454aeb59ab7d93e5f0d412fc') - def fetch_firmware(self): - """ - Flash volumes generated using: - - Toolchain from Debian: - aarch64-linux-gnu-gcc (Debian 12.2.0-14) 12.2.0 - - Used components: - - - Trusted Firmware v2.11.0 - - Tianocore EDK2 4d4f569924 - - Tianocore EDK2-platforms 3f08401 - - """ - - # Secure BootRom (TF-A code) - fs0_xz_path = self.ASSET_FLASH0.fetch() - fs0_path = os.path.join(self.workdir, "SBSA_FLASH0.fd") - lzma_uncompress(fs0_xz_path, fs0_path) - - # Non-secure rom (UEFI and EFI variables) - fs1_xz_path = self.ASSET_FLASH1.fetch() - fs1_path = os.path.join(self.workdir, "SBSA_FLASH1.fd") - lzma_uncompress(fs1_xz_path, fs1_path) - - for path in [fs0_path, fs1_path]: - with open(path, "ab+") as fd: - fd.truncate(256 << 20) # Expand volumes to 256MiB + def test_sbsaref_edk2_firmware(self): self.set_machine('sbsa-ref') - self.vm.set_console() - self.vm.add_args( - "-drive", f"if=pflash,file={fs0_path},format=raw", - "-drive", f"if=pflash,file={fs1_path},format=raw", - ) - - def test_sbsaref_edk2_firmware(self): - self.fetch_firmware() + fetch_firmware(self) + self.vm.set_console() self.vm.add_args('-cpu', 'cortex-a57') self.vm.launch() @@ -87,100 +83,19 @@ class Aarch64SbsarefMachine(QemuSystemTest): # AP Trusted ROM wait_for_console_pattern(self, "Booting Trusted Firmware") - wait_for_console_pattern(self, "BL1: v2.11.0(release):") + wait_for_console_pattern(self, "BL1: v2.12.0(release):") wait_for_console_pattern(self, "BL1: Booting BL2") # Trusted Boot Firmware - wait_for_console_pattern(self, "BL2: v2.11.0(release)") + wait_for_console_pattern(self, "BL2: v2.12.0(release)") wait_for_console_pattern(self, "Booting BL31") # EL3 Runtime Software - wait_for_console_pattern(self, "BL31: v2.11.0(release)") + wait_for_console_pattern(self, "BL31: v2.12.0(release)") # Non-trusted Firmware wait_for_console_pattern(self, "UEFI firmware (version 1.0") interrupt_interactive_console_until_pattern(self, "QEMU SBSA-REF Machine") - - ASSET_ALPINE_ISO = Asset( - ('https://dl-cdn.alpinelinux.org/' - 'alpine/v3.17/releases/aarch64/alpine-standard-3.17.2-aarch64.iso'), - '5a36304ecf039292082d92b48152a9ec21009d3a62f459de623e19c4bd9dc027') - - # This tests the whole boot chain from EFI to Userspace - # We only boot a whole OS for the current top level CPU and GIC - # Other test profiles should use more minimal boots - def boot_alpine_linux(self, cpu): - self.fetch_firmware() - - iso_path = self.ASSET_ALPINE_ISO.fetch() - - self.vm.set_console() - self.vm.add_args( - "-cpu", cpu, - "-drive", f"file={iso_path},media=cdrom,format=raw", - ) - - self.vm.launch() - wait_for_console_pattern(self, "Welcome to Alpine Linux 3.17") - - def test_sbsaref_alpine_linux_cortex_a57(self): - self.boot_alpine_linux("cortex-a57") - - def test_sbsaref_alpine_linux_neoverse_n1(self): - self.boot_alpine_linux("neoverse-n1") - - def test_sbsaref_alpine_linux_max_pauth_off(self): - self.boot_alpine_linux("max,pauth=off") - - 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') - def test_sbsaref_alpine_linux_max(self): - self.boot_alpine_linux("max") - - - ASSET_OPENBSD_ISO = Asset( - ('https://cdn.openbsd.org/pub/OpenBSD/7.3/arm64/miniroot73.img'), - '7fc2c75401d6f01fbfa25f4953f72ad7d7c18650056d30755c44b9c129b707e5') - - # This tests the whole boot chain from EFI to Userspace - # We only boot a whole OS for the current top level CPU and GIC - # Other test profiles should use more minimal boots - def boot_openbsd73(self, cpu): - self.fetch_firmware() - - img_path = self.ASSET_OPENBSD_ISO.fetch() - - self.vm.set_console() - self.vm.add_args( - "-cpu", cpu, - "-drive", f"file={img_path},format=raw,snapshot=on", - ) - - self.vm.launch() - wait_for_console_pattern(self, - "Welcome to the OpenBSD/arm64" - " 7.3 installation program.") - - def test_sbsaref_openbsd73_cortex_a57(self): - self.boot_openbsd73("cortex-a57") - - def test_sbsaref_openbsd73_neoverse_n1(self): - self.boot_openbsd73("neoverse-n1") - - def test_sbsaref_openbsd73_max_pauth_off(self): - self.boot_openbsd73("max,pauth=off") - - @skipUnless(os.getenv('QEMU_TEST_TIMEOUT_EXPECTED'), 'Test might timeout') - def test_sbsaref_openbsd73_max_pauth_impdef(self): - self.boot_openbsd73("max,pauth-impdef=on") - - @skipUnless(os.getenv('QEMU_TEST_TIMEOUT_EXPECTED'), 'Test might timeout') - def test_sbsaref_openbsd73_max(self): - self.boot_openbsd73("max") - - if __name__ == '__main__': QemuSystemTest.main() diff --git a/tests/functional/test_aarch64_sbsaref_alpine.py b/tests/functional/test_aarch64_sbsaref_alpine.py new file mode 100755 index 0000000..8776999 --- /dev/null +++ b/tests/functional/test_aarch64_sbsaref_alpine.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python3 +# +# Functional test that boots a kernel and checks the console +# +# Copyright (c) 2023-2024 Linaro Ltd. +# +# Authors: +# Philippe Mathieu-Daudé +# Marcin Juszkiewicz +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import QemuSystemTest, Asset, skipSlowTest +from qemu_test import wait_for_console_pattern +from test_aarch64_sbsaref import fetch_firmware + + +class Aarch64SbsarefAlpine(QemuSystemTest): + + ASSET_ALPINE_ISO = Asset( + ('https://dl-cdn.alpinelinux.org/' + 'alpine/v3.17/releases/aarch64/alpine-standard-3.17.2-aarch64.iso'), + '5a36304ecf039292082d92b48152a9ec21009d3a62f459de623e19c4bd9dc027') + + # This tests the whole boot chain from EFI to Userspace + # We only boot a whole OS for the current top level CPU and GIC + # Other test profiles should use more minimal boots + def boot_alpine_linux(self, cpu=None): + self.set_machine('sbsa-ref') + + fetch_firmware(self) + iso_path = self.ASSET_ALPINE_ISO.fetch() + + self.vm.set_console() + self.vm.add_args( + "-drive", f"file={iso_path},media=cdrom,format=raw", + ) + if cpu: + self.vm.add_args("-cpu", cpu) + + self.vm.launch() + wait_for_console_pattern(self, "Welcome to Alpine Linux 3.17") + + def test_sbsaref_alpine_linux_cortex_a57(self): + self.boot_alpine_linux("cortex-a57") + + def test_sbsaref_alpine_linux_default_cpu(self): + self.boot_alpine_linux() + + def test_sbsaref_alpine_linux_max_pauth_off(self): + self.boot_alpine_linux("max,pauth=off") + + def test_sbsaref_alpine_linux_max_pauth_impdef(self): + self.boot_alpine_linux("max,pauth-impdef=on") + + @skipSlowTest() # Test might timeout due to PAuth emulation + def test_sbsaref_alpine_linux_max(self): + self.boot_alpine_linux("max") + + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/functional/test_aarch64_sbsaref_freebsd.py b/tests/functional/test_aarch64_sbsaref_freebsd.py new file mode 100755 index 0000000..7ef016f --- /dev/null +++ b/tests/functional/test_aarch64_sbsaref_freebsd.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python3 +# +# Functional test that boots a kernel and checks the console +# +# Copyright (c) 2023-2024 Linaro Ltd. +# +# Authors: +# Philippe Mathieu-Daudé +# Marcin Juszkiewicz +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import QemuSystemTest, Asset, skipSlowTest +from qemu_test import wait_for_console_pattern +from test_aarch64_sbsaref import fetch_firmware + + +class Aarch64SbsarefFreeBSD(QemuSystemTest): + + ASSET_FREEBSD_ISO = Asset( + ('http://ftp-archive.freebsd.org/pub/FreeBSD-Archive/old-releases/arm64' + '/aarch64/ISO-IMAGES/14.1/FreeBSD-14.1-RELEASE-arm64-aarch64-bootonly.iso.xz'), + '7313a4495ffd71ab77b49b1e83f571521c32756e1d75bf48bd890e0ab0f75827') + + # This tests the whole boot chain from EFI to Userspace + # We only boot a whole OS for the current top level CPU and GIC + # Other test profiles should use more minimal boots + def boot_freebsd14(self, cpu=None): + self.set_machine('sbsa-ref') + + fetch_firmware(self) + img_path = self.uncompress(self.ASSET_FREEBSD_ISO) + + self.vm.set_console() + self.vm.add_args( + "-drive", f"file={img_path},format=raw,snapshot=on", + ) + if cpu: + self.vm.add_args("-cpu", cpu) + + self.vm.launch() + wait_for_console_pattern(self, 'Welcome to FreeBSD!') + + def test_sbsaref_freebsd14_cortex_a57(self): + self.boot_freebsd14("cortex-a57") + + def test_sbsaref_freebsd14_default_cpu(self): + self.boot_freebsd14() + + def test_sbsaref_freebsd14_max_pauth_off(self): + self.boot_freebsd14("max,pauth=off") + + @skipSlowTest() # Test might timeout due to PAuth emulation + def test_sbsaref_freebsd14_max_pauth_impdef(self): + self.boot_freebsd14("max,pauth-impdef=on") + + @skipSlowTest() # Test might timeout due to PAuth emulation + def test_sbsaref_freebsd14_max(self): + self.boot_freebsd14("max") + + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/functional/test_aarch64_smmu.py b/tests/functional/test_aarch64_smmu.py new file mode 100755 index 0000000..e0f4a92 --- /dev/null +++ b/tests/functional/test_aarch64_smmu.py @@ -0,0 +1,211 @@ +#!/usr/bin/env python3 +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# SMMUv3 Functional tests +# +# Copyright (c) 2021 Red Hat, Inc. +# +# Author: +# Eric Auger <eric.auger@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 os +import time + +from qemu_test import LinuxKernelTest, Asset, exec_command_and_wait_for_pattern +from qemu_test import BUILD_DIR +from qemu.utils import kvm_available, hvf_available + + +class SMMU(LinuxKernelTest): + + default_kernel_params = ('earlyprintk=pl011,0x9000000 no_timer_check ' + 'printk.time=1 rd_NO_PLYMOUTH net.ifnames=0 ' + 'console=ttyAMA0 rd.rescue') + IOMMU_ADDON = ',iommu_platform=on,disable-modern=off,disable-legacy=on' + kernel_path = None + initrd_path = None + kernel_params = None + + GUEST_PORT = 8080 + + def set_up_boot(self, path): + self.vm.add_args('-device', 'virtio-blk-pci,bus=pcie.0,' + + 'drive=drv0,id=virtio-disk0,bootindex=1,' + 'werror=stop,rerror=stop' + self.IOMMU_ADDON) + self.vm.add_args('-drive', + f'file={path},if=none,cache=writethrough,id=drv0,snapshot=on') + + self.vm.add_args('-netdev', + 'user,id=n1,hostfwd=tcp:127.0.0.1:0-:%d' % + self.GUEST_PORT) + self.vm.add_args('-device', 'virtio-net,netdev=n1' + self.IOMMU_ADDON) + + def common_vm_setup(self, kernel, initrd, disk): + if hvf_available(self.qemu_bin): + accel = "hvf" + elif kvm_available(self.qemu_bin): + accel = "kvm" + else: + self.skipTest("Neither HVF nor KVM accelerator is available") + self.require_accelerator(accel) + self.require_netdev('user') + self.set_machine("virt") + self.vm.add_args('-m', '1G') + self.vm.add_args("-accel", accel) + self.vm.add_args("-cpu", "host") + self.vm.add_args("-machine", "iommu=smmuv3") + self.vm.add_args("-d", "guest_errors") + self.vm.add_args('-bios', os.path.join(BUILD_DIR, 'pc-bios', + 'edk2-aarch64-code.fd')) + self.vm.add_args('-device', 'virtio-rng-pci,rng=rng0') + self.vm.add_args('-object', + 'rng-random,id=rng0,filename=/dev/urandom') + + self.kernel_path = kernel.fetch() + self.initrd_path = initrd.fetch() + self.set_up_boot(disk.fetch()) + + def run_and_check(self, filename, hashsum): + self.vm.add_args('-initrd', self.initrd_path) + self.vm.add_args('-append', self.kernel_params) + self.launch_kernel(self.kernel_path, initrd=self.initrd_path, + wait_for='attach it to a bug report.') + prompt = '# ' + # Fedora 33 requires 'return' to be pressed to enter the shell. + # There seems to be a small race between detecting the previous ':' + # and sending the newline, so we need to add a small delay here. + self.wait_for_console_pattern(':') + time.sleep(0.2) + exec_command_and_wait_for_pattern(self, '\n', prompt) + exec_command_and_wait_for_pattern(self, 'cat /proc/cmdline', + self.kernel_params) + + # Checking for SMMU enablement: + self.log.info("Checking whether SMMU has been enabled...") + exec_command_and_wait_for_pattern(self, 'dmesg | grep smmu', + 'arm-smmu-v3') + self.wait_for_console_pattern(prompt) + exec_command_and_wait_for_pattern(self, + 'find /sys/kernel/iommu_groups/ -type l', + 'devices/0000:00:') + self.wait_for_console_pattern(prompt) + + # Copy a file (checked later), umount afterwards to drop disk cache: + self.log.info("Checking hard disk...") + exec_command_and_wait_for_pattern(self, + "while ! (dmesg -c | grep vda:) ; do sleep 1 ; done", + "vda2") + exec_command_and_wait_for_pattern(self, 'mount /dev/vda2 /sysroot', + 'mounted filesystem') + exec_command_and_wait_for_pattern(self, 'cp /bin/vi /sysroot/root/vi', + prompt) + exec_command_and_wait_for_pattern(self, 'umount /sysroot', prompt) + # Switch from initrd to the cloud image filesystem: + exec_command_and_wait_for_pattern(self, 'mount /dev/vda2 /sysroot', + prompt) + exec_command_and_wait_for_pattern(self, + ('for d in dev proc sys run ; do ' + 'mount -o bind /$d /sysroot/$d ; done'), prompt) + exec_command_and_wait_for_pattern(self, 'chroot /sysroot', prompt) + # Check files on the hard disk: + exec_command_and_wait_for_pattern(self, + ('if diff -q /root/vi /usr/bin/vi ; then echo "file" "ok" ; ' + 'else echo "files differ"; fi'), 'file ok') + self.wait_for_console_pattern(prompt) + exec_command_and_wait_for_pattern(self, f'sha256sum {filename}', + hashsum) + + # Check virtio-net via HTTP: + exec_command_and_wait_for_pattern(self, 'dhclient eth0', prompt) + self.check_http_download(filename, hashsum, self.GUEST_PORT) + + + # 5.3 kernel without RIL # + + ASSET_KERNEL_F31 = Asset( + ('https://archives.fedoraproject.org/pub/archive/fedora/linux/' + 'releases/31/Server/aarch64/os/images/pxeboot/vmlinuz'), + '3ae07fcafbfc8e4abeb693035a74fe10698faae15e9ccd48882a9167800c1527') + + ASSET_INITRD_F31 = Asset( + ('https://archives.fedoraproject.org/pub/archive/fedora/linux/' + 'releases/31/Server/aarch64/os/images/pxeboot/initrd.img'), + '9f3146b28bc531c689f3c5f114cb74e4bd7bd548e0ba19fa77921d8bd256755a') + + ASSET_DISK_F31 = Asset( + ('https://archives.fedoraproject.org/pub/archive/fedora/linux/releases' + '/31/Cloud/aarch64/images/Fedora-Cloud-Base-31-1.9.aarch64.qcow2'), + '1e18d9c0cf734940c4b5d5ec592facaed2af0ad0329383d5639c997fdf16fe49') + + F31_FILENAME = '/boot/initramfs-5.3.7-301.fc31.aarch64.img' + F31_HSUM = '1a4beec6607d94df73d9dd1b4985c9c23dd0fdcf4e6ca1351d477f190df7bef9' + + def test_smmu_noril(self): + self.common_vm_setup(self.ASSET_KERNEL_F31, self.ASSET_INITRD_F31, + self.ASSET_DISK_F31) + self.kernel_params = self.default_kernel_params + self.run_and_check(self.F31_FILENAME, self.F31_HSUM) + + def test_smmu_noril_passthrough(self): + self.common_vm_setup(self.ASSET_KERNEL_F31, self.ASSET_INITRD_F31, + self.ASSET_DISK_F31) + self.kernel_params = (self.default_kernel_params + + ' iommu.passthrough=on') + self.run_and_check(self.F31_FILENAME, self.F31_HSUM) + + def test_smmu_noril_nostrict(self): + self.common_vm_setup(self.ASSET_KERNEL_F31, self.ASSET_INITRD_F31, + self.ASSET_DISK_F31) + self.kernel_params = (self.default_kernel_params + + ' iommu.strict=0') + self.run_and_check(self.F31_FILENAME, self.F31_HSUM) + + + # 5.8 kernel featuring range invalidation + # >= v5.7 kernel + + ASSET_KERNEL_F33 = Asset( + ('https://archives.fedoraproject.org/pub/archive/fedora/linux/' + 'releases/33/Server/aarch64/os/images/pxeboot/vmlinuz'), + 'd8b1e6f7241f339d8e7609c456cf0461ffa4583ed07e0b55c7d1d8a0c154aa89') + + ASSET_INITRD_F33 = Asset( + ('https://archives.fedoraproject.org/pub/archive/fedora/linux/' + 'releases/33/Server/aarch64/os/images/pxeboot/initrd.img'), + '92513f55295c2c16a777f7b6c35ccd70a438e9e1e40b6ba39e0e60900615b3df') + + ASSET_DISK_F33 = Asset( + ('https://archives.fedoraproject.org/pub/archive/fedora/linux/releases' + '/33/Cloud/aarch64/images/Fedora-Cloud-Base-33-1.2.aarch64.qcow2'), + 'e7f75cdfd523fe5ac2ca9eeece68edc1a81f386a17f969c1d1c7c87031008a6b') + + F33_FILENAME = '/boot/initramfs-5.8.15-301.fc33.aarch64.img' + F33_HSUM = '079cfad0caa82e84c8ca1fb0897a4999dd769f262216099f518619e807a550d9' + + def test_smmu_ril(self): + self.common_vm_setup(self.ASSET_KERNEL_F33, self.ASSET_INITRD_F33, + self.ASSET_DISK_F33) + self.kernel_params = self.default_kernel_params + self.run_and_check(self.F33_FILENAME, self.F33_HSUM) + + def test_smmu_ril_passthrough(self): + self.common_vm_setup(self.ASSET_KERNEL_F33, self.ASSET_INITRD_F33, + self.ASSET_DISK_F33) + self.kernel_params = (self.default_kernel_params + + ' iommu.passthrough=on') + self.run_and_check(self.F33_FILENAME, self.F33_HSUM) + + def test_smmu_ril_nostrict(self): + self.common_vm_setup(self.ASSET_KERNEL_F33, self.ASSET_INITRD_F33, + self.ASSET_DISK_F33) + self.kernel_params = (self.default_kernel_params + + ' iommu.strict=0') + self.run_and_check(self.F33_FILENAME, self.F33_HSUM) + + +if __name__ == '__main__': + LinuxKernelTest.main() diff --git a/tests/avocado/tcg_plugins.py b/tests/functional/test_aarch64_tcg_plugins.py index a6ff457..cb7e929 100644..100755 --- a/tests/avocado/tcg_plugins.py +++ b/tests/functional/test_aarch64_tcg_plugins.py @@ -1,3 +1,5 @@ +#!/usr/bin/env python3 +# # TCG Plugins tests # # These are a little more involved than the basic tests run by check-tcg. @@ -13,7 +15,8 @@ import tempfile import mmap import re -from boot_linux_console import LinuxKernelTest +from qemu.machine.machine import VMLaunchFailure +from qemu_test import LinuxKernelTest, Asset class PluginKernelBase(LinuxKernelTest): @@ -41,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 @@ -53,31 +58,23 @@ class PluginKernelBase(LinuxKernelTest): class PluginKernelNormal(PluginKernelBase): - def _grab_aarch64_kernel(self): - kernel_url = ('https://storage.tuxboot.com/20230331/arm64/Image') - kernel_sha256 = 'ce95a7101a5fecebe0fe630deee6bd97b32ba41bc8754090e9ad8961ea8674c7' - kernel_path = self.fetch_asset(kernel_url, - asset_hash=kernel_sha256, - algorithm = "sha256") - return kernel_path + ASSET_KERNEL = Asset( + ('https://storage.tuxboot.com/20230331/arm64/Image'), + 'ce95a7101a5fecebe0fe630deee6bd97b32ba41bc8754090e9ad8961ea8674c7') def test_aarch64_virt_insn(self): - """ - :avocado: tags=accel:tcg - :avocado: tags=arch:aarch64 - :avocado: tags=machine:virt - :avocado: tags=cpu:cortex-a53 - """ - kernel_path = self._grab_aarch64_kernel() + self.set_machine('virt') + self.cpu='cortex-a53' + 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") self.run_vm(kernel_path, kernel_command_line, - "tests/tcg/plugins/libinsn.so", plugin_log.name, + self.plugin_file('libinsn'), plugin_log.name, console_pattern) with plugin_log as lf, \ @@ -92,22 +89,18 @@ class PluginKernelNormal(PluginKernelBase): def test_aarch64_virt_insn_icount(self): - """ - :avocado: tags=accel:tcg - :avocado: tags=arch:aarch64 - :avocado: tags=machine:virt - :avocado: tags=cpu:cortex-a53 - """ - kernel_path = self._grab_aarch64_kernel() + self.set_machine('virt') + self.cpu='cortex-a53' + 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") self.run_vm(kernel_path, kernel_command_line, - "tests/tcg/plugins/libinsn.so", plugin_log.name, + self.plugin_file('libinsn'), plugin_log.name, console_pattern, args=('-icount', 'shift=1')) @@ -120,3 +113,6 @@ class PluginKernelNormal(PluginKernelBase): else: count = int(m.group("count")) self.log.info(f"Counted: {count} instructions") + +if __name__ == '__main__': + LinuxKernelTest.main() diff --git a/tests/functional/test_aarch64_tuxrun.py b/tests/functional/test_aarch64_tuxrun.py new file mode 100755 index 0000000..75adc8a --- /dev/null +++ b/tests/functional/test_aarch64_tuxrun.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python3 +# +# Functional test that boots known good tuxboot images the same way +# that tuxrun (www.tuxrun.org) does. This tool is used by things like +# the LKFT project to run regression tests on kernels. +# +# Copyright (c) 2023 Linaro Ltd. +# +# Author: +# Alex Bennée <alex.bennee@linaro.org> +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import Asset +from qemu_test.tuxruntest import TuxRunBaselineTest + +class TuxRunAarch64Test(TuxRunBaselineTest): + + ASSET_ARM64_KERNEL = Asset( + 'https://storage.tuxboot.com/buildroot/20241119/arm64/Image', + 'b74743c5e89e1cea0f73368d24ae0ae85c5204ff84be3b5e9610417417d2f235') + ASSET_ARM64_ROOTFS = Asset( + 'https://storage.tuxboot.com/buildroot/20241119/arm64/rootfs.ext4.zst', + 'a1acaaae2068df4648d04ff75f532aaa8c5edcd6b936122b6f0db4848a07b465') + + def test_arm64(self): + self.set_machine('virt') + self.cpu='cortex-a57' + self.console='ttyAMA0' + self.wait_for_shutdown=False + self.common_tuxrun(kernel_asset=self.ASSET_ARM64_KERNEL, + rootfs_asset=self.ASSET_ARM64_ROOTFS) + + ASSET_ARM64BE_KERNEL = Asset( + 'https://storage.tuxboot.com/buildroot/20241119/arm64be/Image', + 'fd6af4f16689d17a2c24fe0053cc212edcdf77abdcaf301800b8d38fa9f6e109') + ASSET_ARM64BE_ROOTFS = Asset( + 'https://storage.tuxboot.com/buildroot/20241119/arm64be/rootfs.ext4.zst', + 'f5e9371b62701aab8dead52592ca7488c8a9e255c9be8d7635c7f30f477c2c21') + + def test_arm64be(self): + self.set_machine('virt') + self.cpu='cortex-a57' + self.console='ttyAMA0' + self.wait_for_shutdown=False + self.common_tuxrun(kernel_asset=self.ASSET_ARM64BE_KERNEL, + rootfs_asset=self.ASSET_ARM64BE_ROOTFS) + +if __name__ == '__main__': + TuxRunBaselineTest.main() diff --git a/tests/functional/test_aarch64_virt.py b/tests/functional/test_aarch64_virt.py index c967da4..4d0ad90 100755 --- a/tests/functional/test_aarch64_virt.py +++ b/tests/functional/test_aarch64_virt.py @@ -10,14 +10,11 @@ # # SPDX-License-Identifier: GPL-2.0-or-later -import time -import os import logging +from subprocess import check_call, DEVNULL -from qemu_test import BUILD_DIR -from qemu_test import QemuSystemTest, Asset -from qemu_test import exec_command, wait_for_console_pattern -from qemu_test import get_qemu_img, run_cmd +from qemu_test import QemuSystemTest, Asset, exec_command_and_wait_for_pattern +from qemu_test import wait_for_console_pattern, get_qemu_img class Aarch64VirtMachine(QemuSystemTest): @@ -41,11 +38,9 @@ class Aarch64VirtMachine(QemuSystemTest): iso_path = self.ASSET_ALPINE_ISO.fetch() self.set_machine('virt') - self.vm.set_console() - kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + - 'console=ttyAMA0') self.require_accelerator("tcg") + self.vm.set_console() self.vm.add_args("-accel", "tcg") self.vm.add_args("-cpu", "max,pauth-impdef=on") self.vm.add_args("-machine", @@ -54,8 +49,8 @@ class Aarch64VirtMachine(QemuSystemTest): "mte=on," "gic-version=max,iommu=smmuv3") self.vm.add_args("-smp", "2", "-m", "1024") - self.vm.add_args('-bios', os.path.join(BUILD_DIR, 'pc-bios', - 'edk2-aarch64-code.fd')) + self.vm.add_args('-bios', self.build_file('pc-bios', + 'edk2-aarch64-code.fd')) self.vm.add_args("-drive", f"file={iso_path},media=cdrom,format=raw") self.vm.add_args('-device', 'virtio-rng-pci,rng=rng0') self.vm.add_args('-object', 'rng-random,id=rng0,filename=/dev/urandom') @@ -74,15 +69,16 @@ class Aarch64VirtMachine(QemuSystemTest): Common code to launch basic virt machine with kernel+initrd and a scratch disk. """ + self.set_machine('virt') + self.require_accelerator("tcg") + logger = logging.getLogger('aarch64_virt') kernel_path = self.ASSET_KERNEL.fetch() - self.set_machine('virt') self.vm.set_console() kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + 'console=ttyAMA0') - self.require_accelerator("tcg") self.vm.add_args('-cpu', 'max,pauth-impdef=on', '-machine', machine, '-accel', 'tcg', @@ -96,29 +92,36 @@ class Aarch64VirtMachine(QemuSystemTest): # Also add a scratch block device logger.info('creating scratch qcow2 image') - image_path = os.path.join(self.workdir, 'scratch.qcow2') + image_path = self.scratch_file('scratch.qcow2') qemu_img = get_qemu_img(self) - run_cmd([qemu_img, 'create', '-f', 'qcow2', image_path, '8M']) + check_call([qemu_img, 'create', '-f', 'qcow2', image_path, '8M'], + stdout=DEVNULL, stderr=DEVNULL) # Add the device self.vm.add_args('-blockdev', - f"driver=qcow2,file.driver=file,file.filename={image_path},node-name=scratch") + "driver=qcow2," + "file.driver=file," + f"file.filename={image_path},node-name=scratch") self.vm.add_args('-device', 'virtio-blk-device,drive=scratch') self.vm.launch() - self.wait_for_console_pattern('Welcome to Buildroot') - time.sleep(0.1) - exec_command(self, 'root') - time.sleep(0.1) - exec_command(self, 'dd if=/dev/hwrng of=/dev/vda bs=512 count=4') - time.sleep(0.1) - exec_command(self, 'md5sum /dev/vda') - time.sleep(0.1) - exec_command(self, 'cat /proc/interrupts') - time.sleep(0.1) - exec_command(self, 'cat /proc/self/maps') - time.sleep(0.1) + + ps1='#' + self.wait_for_console_pattern('login:') + + commands = [ + ('root', ps1), + ('cat /proc/interrupts', ps1), + ('cat /proc/self/maps', ps1), + ('uname -a', ps1), + ('dd if=/dev/hwrng of=/dev/vda bs=512 count=4', ps1), + ('md5sum /dev/vda', ps1), + ('halt -n', 'reboot: System halted') + ] + + for cmd, pattern in commands: + exec_command_and_wait_for_pattern(self, cmd, pattern) def test_aarch64_virt_gicv3(self): self.common_aarch64_virt("virt,gic_version=3") @@ -127,5 +130,6 @@ class Aarch64VirtMachine(QemuSystemTest): self.common_aarch64_virt("virt,gic-version=2") + if __name__ == '__main__': QemuSystemTest.main() diff --git a/tests/functional/test_aarch64_virt_gpu.py b/tests/functional/test_aarch64_virt_gpu.py new file mode 100755 index 0000000..3844727 --- /dev/null +++ b/tests/functional/test_aarch64_virt_gpu.py @@ -0,0 +1,140 @@ +#!/usr/bin/env python3 +# +# Functional tests for the various graphics modes we can support. +# +# Copyright (c) 2024, 2025 Linaro Ltd. +# +# Author: +# Alex Bennée <alex.bennee@linaro.org> +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu.machine.machine import VMLaunchFailure + +from qemu_test import Asset +from qemu_test import exec_command_and_wait_for_pattern as ec_and_wait +from qemu_test import skipIfMissingCommands + +from qemu_test.linuxkernel import LinuxKernelTest + +from re import search +from subprocess import check_output, CalledProcessError + +class Aarch64VirtGPUMachine(LinuxKernelTest): + + ASSET_VIRT_GPU_KERNEL = Asset( + 'https://fileserver.linaro.org/s/ce5jXBFinPxtEdx/' + 'download?path=%2F&files=' + 'Image.6.12.16.aarch64', + '7888c51c55d37e86bbbdeb5acea9f08c34e6b0f03c1f5b2463285f6a6f6eec8b') + + ASSET_VIRT_GPU_ROOTFS = Asset( + 'https://fileserver.linaro.org/s/ce5jXBFinPxtEdx/' + 'download?path=%2F&files=' + 'rootfs.aarch64.ext2.zstd', + 'd45118c899420b7e673f1539a37a35480134b3e36e3a59e2cb69b1781cbb14ef') + + def _launch_virt_gpu(self, gpu_device): + + self.set_machine('virt') + self.require_accelerator("tcg") + + kernel_path = self.ASSET_VIRT_GPU_KERNEL.fetch() + image_path = self.uncompress(self.ASSET_VIRT_GPU_ROOTFS, format="zstd") + + self.vm.set_console() + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'console=ttyAMA0 root=/dev/vda') + + self.vm.add_args("-accel", "tcg") + self.vm.add_args("-cpu", "cortex-a72") + self.vm.add_args("-machine", "virt,gic-version=max", + '-kernel', kernel_path, + '-append', kernel_command_line) + self.vm.add_args("-smp", "2", "-m", "2048") + self.vm.add_args("-device", gpu_device) + self.vm.add_args("-display", "egl-headless") + self.vm.add_args("-display", "dbus,gl=on") + + self.vm.add_args("-device", "virtio-blk-device,drive=hd0") + self.vm.add_args("-blockdev", + "driver=raw,file.driver=file," + "node-name=hd0,read-only=on," + f"file.filename={image_path}") + self.vm.add_args("-snapshot") + + try: + self.vm.launch() + except VMLaunchFailure as excp: + if "old virglrenderer, blob resources unsupported" in excp.output: + self.skipTest("No blob support for virtio-gpu") + elif "old virglrenderer, venus unsupported" in excp.output: + self.skipTest("No venus support for virtio-gpu") + elif "egl: no drm render node available" in excp.output: + self.skipTest("Can't access host DRM render node") + elif "'type' does not accept value 'egl-headless'" in excp.output: + self.skipTest("egl-headless support is not available") + elif "'type' does not accept value 'dbus'" in excp.output: + self.skipTest("dbus display support is not available") + else: + self.log.info("unhandled launch failure: %s", excp.output) + raise excp + + self.wait_for_console_pattern('buildroot login:') + ec_and_wait(self, 'root', '#') + + def _run_virt_weston_test(self, cmd, fail = None): + + # make it easier to detect successful return to shell + PS1 = 'RES=[$?] # ' + OK_CMD = 'RES=[0] # ' + + ec_and_wait(self, 'export XDG_RUNTIME_DIR=/tmp', '#') + ec_and_wait(self, f"export PS1='{PS1}'", OK_CMD) + full_cmd = f"weston -B headless --renderer gl --shell kiosk -- {cmd}" + ec_and_wait(self, full_cmd, OK_CMD, fail) + + @skipIfMissingCommands('zstd') + def test_aarch64_virt_with_virgl_gpu(self): + + self.require_device('virtio-gpu-gl-pci') + + self._launch_virt_gpu("virtio-gpu-gl-pci") + + # subset of the glmark tests + tests = " ".join([f"-b {test}" for test in + ["build", "texture", "shading", + "bump", "desktop", "buffer"]]) + + self._run_virt_weston_test("glmark2-wayland --validate " + tests) + + @skipIfMissingCommands('zstd') + def test_aarch64_virt_with_virgl_blobs_gpu(self): + + self.require_device('virtio-gpu-gl-pci') + + self._launch_virt_gpu("virtio-gpu-gl-pci,hostmem=4G,blob=on") + self._run_virt_weston_test("glmark2-wayland -b:duration=1.0") + + @skipIfMissingCommands('zstd') + @skipIfMissingCommands('vulkaninfo') + def test_aarch64_virt_with_vulkan_gpu(self): + + self.require_device('virtio-gpu-gl-pci') + + try: + vk_info = check_output(["vulkaninfo", "--summary"], + encoding="utf-8") + except CalledProcessError as excp: + self.skipTest(f"Miss-configured host Vulkan: {excp.output}") + + if search(r"driverID\s+=\s+DRIVER_ID_NVIDIA_PROPRIETARY", vk_info): + self.skipTest("Test skipped on NVIDIA proprietary driver") + + self._launch_virt_gpu("virtio-gpu-gl-pci,hostmem=4G,blob=on,venus=on") + self._run_virt_weston_test("vkmark -b:duration=1.0", + "debug: stuck in fence wait with iter at") + + +if __name__ == '__main__': + LinuxKernelTest.main() diff --git a/tests/functional/test_aarch64_xen.py b/tests/functional/test_aarch64_xen.py new file mode 100755 index 0000000..261d796 --- /dev/null +++ b/tests/functional/test_aarch64_xen.py @@ -0,0 +1,91 @@ +#!/usr/bin/env python3 +# +# Functional test that boots a Xen hypervisor with a domU kernel and +# checks the console output is vaguely sane . +# +# Copyright (c) 2020 Linaro +# +# Author: +# Alex Bennée <alex.bennee@linaro.org> +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# 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 qemu_test import Asset, LinuxKernelTest, wait_for_console_pattern + + +class BootXen(LinuxKernelTest): + """ + Boots a Xen hypervisor with a Linux DomU kernel. + """ + + timeout = 90 + XEN_COMMON_COMMAND_LINE = 'dom0_mem=128M loglvl=all guest_loglvl=all' + + ASSET_KERNEL = Asset( + ('https://fileserver.linaro.org/s/JSsewXGZ6mqxPr5/' + 'download?path=%2F&files=linux-5.9.9-arm64-ajb'), + '00366fa51ea957c19462d2e2aefd480bef80ce727120e714ae48e0c88f261edb') + + def launch_xen(self, xen_path): + """ + Launch Xen with a dom0 guest kernel + """ + self.require_accelerator("tcg") # virtualization=on + self.set_machine('virt') + self.cpu = "cortex-a57" + self.kernel_path = self.ASSET_KERNEL.fetch() + self.log.info("launch with xen_path: %s", xen_path) + + self.vm.set_console() + + self.vm.add_args('-machine', 'virtualization=on', + '-m', '768', + '-kernel', xen_path, + '-append', self.XEN_COMMON_COMMAND_LINE, + '-device', + 'guest-loader,addr=0x47000000,kernel=%s,bootargs=console=hvc0' + % (self.kernel_path)) + + self.vm.launch() + + console_pattern = 'VFS: Cannot open root device' + wait_for_console_pattern(self, console_pattern, "Panic on CPU 0:") + + ASSET_XEN_4_11 = Asset( + ('https://fileserver.linaro.org/s/JSsewXGZ6mqxPr5/download?path=%2F&' + 'files=xen-hypervisor-4.11-arm64_4.11.4%2B37-g3263f257ca-1_arm64.deb'), + 'b745c2631342f9fcc0147ddc364edb62c20ecfebd430e5a3546e7d7c6891c0bc') + + def test_arm64_xen_411_and_dom0(self): + # archive of file from https://deb.debian.org/debian/pool/main/x/xen/ + xen_path = self.archive_extract(self.ASSET_XEN_4_11, format='deb', + member="boot/xen-4.11-arm64") + self.launch_xen(xen_path) + + ASSET_XEN_4_14 = Asset( + ('https://fileserver.linaro.org/s/JSsewXGZ6mqxPr5/download?path=%2F&' + 'files=xen-hypervisor-4.14-arm64_4.14.0%2B80-gd101b417b7-1_arm64.deb'), + 'e930a3293248edabd367d5b4b3b6448b9c99c057096ea8b47228a7870661d5cb') + + def test_arm64_xen_414_and_dom0(self): + # archive of file from https://deb.debian.org/debian/pool/main/x/xen/ + xen_path = self.archive_extract(self.ASSET_XEN_4_14, format='deb', + member="boot/xen-4.14-arm64") + self.launch_xen(xen_path) + + ASSET_XEN_4_15 = Asset( + ('https://fileserver.linaro.org/s/JSsewXGZ6mqxPr5/download?path=%2F&' + 'files=xen-upstream-4.15-unstable.deb'), + '2a9a8af8acf0231844657cc28baab95bd918b0ee2d493ee4ee6f8846e1358bc9') + + def test_arm64_xen_415_and_dom0(self): + xen_path = self.archive_extract(self.ASSET_XEN_4_15, format='deb', + member="boot/xen-4.15-unstable") + self.launch_xen(xen_path) + + +if __name__ == '__main__': + LinuxKernelTest.main() diff --git a/tests/functional/test_aarch64_xlnx_versal.py b/tests/functional/test_aarch64_xlnx_versal.py new file mode 100755 index 0000000..4b9c49e --- /dev/null +++ b/tests/functional/test_aarch64_xlnx_versal.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python3 +# +# Functional test that boots a Linux kernel and checks the console +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import LinuxKernelTest, Asset + +class XlnxVersalVirtMachine(LinuxKernelTest): + + ASSET_KERNEL = Asset( + ('http://ports.ubuntu.com/ubuntu-ports/dists/bionic-updates/main/' + 'installer-arm64/20101020ubuntu543.19/images/netboot/' + 'ubuntu-installer/arm64/linux'), + 'ce54f74ab0b15cfd13d1a293f2d27ffd79d8a85b7bb9bf21093ae9513864ac79') + + ASSET_INITRD = Asset( + ('http://ports.ubuntu.com/ubuntu-ports/dists/bionic-updates/main/' + 'installer-arm64/20101020ubuntu543.19/images/netboot/' + '/ubuntu-installer/arm64/initrd.gz'), + 'e7a5e716b6f516d8be315c06e7331aaf16994fe4222e0e7cfb34bc015698929e') + + def test_aarch64_xlnx_versal_virt(self): + self.set_machine('xlnx-versal-virt') + kernel_path = self.ASSET_KERNEL.fetch() + initrd_path = self.ASSET_INITRD.fetch() + + self.vm.set_console() + self.vm.add_args('-m', '2G', + '-accel', 'tcg', + '-kernel', kernel_path, + '-initrd', initrd_path) + self.vm.launch() + self.wait_for_console_pattern('Checked W+X mappings: passed') + +if __name__ == '__main__': + LinuxKernelTest.main() diff --git a/tests/functional/test_acpi_bits.py b/tests/functional/test_acpi_bits.py index ee40647..8e0563a 100755 --- a/tests/functional/test_acpi_bits.py +++ b/tests/functional/test_acpi_bits.py @@ -31,57 +31,24 @@ including an upgraded acpica. The fork is located here: https://gitlab.com/qemu-project/biosbits-bits . """ -import logging import os -import platform import re import shutil import subprocess -import tarfile -import tempfile -import time -import zipfile -from pathlib import Path from typing import ( List, Optional, Sequence, ) from qemu.machine import QEMUMachine -from unittest import skipIf -from qemu_test import QemuBaseTest, Asset +from qemu_test import (QemuSystemTest, Asset, skipIfMissingCommands, + skipIfNotMachine) -deps = ["xorriso", "mformat"] # dependent tools needed in the test setup/box. -supported_platforms = ['x86_64'] # supported test platforms. # default timeout of 120 secs is sometimes not enough for bits test. BITS_TIMEOUT = 200 -def which(tool): - """ looks up the full path for @tool, returns None if not found - or if @tool does not have executable permissions. - """ - paths=os.getenv('PATH') - for p in paths.split(os.path.pathsep): - p = os.path.join(p, tool) - if os.path.exists(p) and os.access(p, os.X_OK): - return p - return None - -def missing_deps(): - """ returns True if any of the test dependent tools are absent. - """ - for dep in deps: - if which(dep) is None: - return True - return False - -def supported_platform(): - """ checks if the test is running on a supported platform. - """ - return platform.machine() in supported_platforms - class QEMUBitsMachine(QEMUMachine): # pylint: disable=too-few-public-methods """ A QEMU VM, with isa-debugcon enabled and bits iso passed @@ -124,10 +91,9 @@ class QEMUBitsMachine(QEMUMachine): # pylint: disable=too-few-public-methods """return the base argument to QEMU binary""" return self._base_args -@skipIf(not supported_platform() or missing_deps(), - 'unsupported platform or dependencies (%s) not installed' \ - % ','.join(deps)) -class AcpiBitsTest(QemuBaseTest): #pylint: disable=too-many-instance-attributes +@skipIfMissingCommands("xorriso", "mformat") +@skipIfNotMachine("x86_64") +class AcpiBitsTest(QemuSystemTest): #pylint: disable=too-many-instance-attributes """ ACPI and SMBIOS tests using biosbits. """ @@ -150,12 +116,9 @@ class AcpiBitsTest(QemuBaseTest): #pylint: disable=too-many-instance-attributes def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self._vm = None - self._workDir = None - self._baseDir = None self._debugcon_addr = '0x403' self._debugcon_log = 'debugcon-log.txt' - self.logger = self.log def _print_log(self, log): self.logger.info('\nlogs from biosbits follows:') @@ -166,29 +129,24 @@ class AcpiBitsTest(QemuBaseTest): #pylint: disable=too-many-instance-attributes def copy_bits_config(self): """ copies the bios bits config file into bits. """ - config_file = 'bits-cfg.txt' - bits_config_dir = os.path.join(self._baseDir, 'acpi-bits', - 'bits-config') - target_config_dir = os.path.join(self._workDir, - 'bits-%d' %self.BITS_INTERNAL_VER, - 'boot') - self.assertTrue(os.path.exists(bits_config_dir)) + bits_config_file = self.data_file('acpi-bits', + 'bits-config', + 'bits-cfg.txt') + target_config_dir = self.scratch_file('bits-%d' % + self.BITS_INTERNAL_VER, + 'boot') + self.assertTrue(os.path.exists(bits_config_file)) self.assertTrue(os.path.exists(target_config_dir)) - self.assertTrue(os.access(os.path.join(bits_config_dir, - config_file), os.R_OK)) - shutil.copy2(os.path.join(bits_config_dir, config_file), - target_config_dir) + shutil.copy2(bits_config_file, target_config_dir) self.logger.info('copied config file %s to %s', - config_file, target_config_dir) + bits_config_file, target_config_dir) def copy_test_scripts(self): """copies the python test scripts into bits. """ - bits_test_dir = os.path.join(self._baseDir, 'acpi-bits', - 'bits-tests') - target_test_dir = os.path.join(self._workDir, - 'bits-%d' %self.BITS_INTERNAL_VER, - 'boot', 'python') + bits_test_dir = self.data_file('acpi-bits', 'bits-tests') + target_test_dir = self.scratch_file('bits-%d' % self.BITS_INTERNAL_VER, + 'boot', 'python') self.assertTrue(os.path.exists(bits_test_dir)) self.assertTrue(os.path.exists(target_test_dir)) @@ -196,11 +154,12 @@ class AcpiBitsTest(QemuBaseTest): #pylint: disable=too-many-instance-attributes for filename in os.listdir(bits_test_dir): if os.path.isfile(os.path.join(bits_test_dir, filename)) and \ filename.endswith('.py2'): - # all test scripts are named with extension .py2 so that - # avocado does not try to load them. These scripts are - # written for python 2.7 not python 3 and hence if avocado - # loaded them, it would complain about python 3 specific - # syntaxes. + # All test scripts are named with extension .py2 so that + # they are not run by accident. + # + # These scripts are intended to run inside the test VM + # and are written for python 2.7 not python 3, hence + # would cause syntax errors if loaded ouside the VM. newfilename = os.path.splitext(filename)[0] + '.py' shutil.copy2(os.path.join(bits_test_dir, filename), os.path.join(target_test_dir, newfilename)) @@ -224,8 +183,8 @@ class AcpiBitsTest(QemuBaseTest): #pylint: disable=too-many-instance-attributes the directory where we have extracted our pre-built bits grub tarball. """ - grub_x86_64_mods = os.path.join(self._workDir, 'grub-inst-x86_64-efi') - grub_i386_mods = os.path.join(self._workDir, 'grub-inst') + grub_x86_64_mods = self.scratch_file('grub-inst-x86_64-efi') + grub_i386_mods = self.scratch_file('grub-inst') self.assertTrue(os.path.exists(grub_x86_64_mods)) self.assertTrue(os.path.exists(grub_i386_mods)) @@ -246,13 +205,11 @@ class AcpiBitsTest(QemuBaseTest): #pylint: disable=too-many-instance-attributes """ Uses grub-mkrescue to generate a fresh bits iso with the python test scripts """ - bits_dir = os.path.join(self._workDir, - 'bits-%d' %self.BITS_INTERNAL_VER) - iso_file = os.path.join(self._workDir, - 'bits-%d.iso' %self.BITS_INTERNAL_VER) - mkrescue_script = os.path.join(self._workDir, - 'grub-inst-x86_64-efi', 'bin', - 'grub-mkrescue') + bits_dir = self.scratch_file('bits-%d' % self.BITS_INTERNAL_VER) + iso_file = self.scratch_file('bits-%d.iso' % self.BITS_INTERNAL_VER) + mkrescue_script = self.scratch_file('grub-inst-x86_64-efi', + 'bin', + 'grub-mkrescue') self.assertTrue(os.access(mkrescue_script, os.R_OK | os.W_OK | os.X_OK)) @@ -284,46 +241,28 @@ class AcpiBitsTest(QemuBaseTest): #pylint: disable=too-many-instance-attributes self.logger.info('iso file %s successfully generated.', iso_file) def setUp(self): # pylint: disable=arguments-differ - super().setUp('qemu-system-') + super().setUp() self.logger = self.log - self._baseDir = Path(__file__).parent - - # workdir could also be avocado's own workdir in self.workdir. - # At present, I prefer to maintain my own temporary working - # directory. It gives us more control over the generated bits - # log files and also for debugging, we may chose not to remove - # this working directory so that the logs and iso can be - # inspected manually and archived if needed. - self._workDir = tempfile.mkdtemp(prefix='acpi-bits-', - suffix='.tmp') - self.logger.info('working dir: %s', self._workDir) - - prebuiltDir = os.path.join(self._workDir, 'prebuilt') + prebuiltDir = self.scratch_file('prebuilt') if not os.path.isdir(prebuiltDir): os.mkdir(prebuiltDir, mode=0o775) - bits_zip_file = os.path.join(prebuiltDir, 'bits-%d-%s.zip' - %(self.BITS_INTERNAL_VER, - self.BITS_COMMIT_HASH)) - grub_tar_file = os.path.join(prebuiltDir, - 'bits-%d-%s-grub.tar.gz' - %(self.BITS_INTERNAL_VER, - self.BITS_COMMIT_HASH)) - - bitsLocalArtLoc = self.ASSET_BITS.fetch() - self.logger.info("downloaded bits artifacts to %s", bitsLocalArtLoc) + bits_zip_file = self.scratch_file('prebuilt', + 'bits-%d-%s.zip' + %(self.BITS_INTERNAL_VER, + self.BITS_COMMIT_HASH)) + grub_tar_file = self.scratch_file('prebuilt', + 'bits-%d-%s-grub.tar.gz' + %(self.BITS_INTERNAL_VER, + self.BITS_COMMIT_HASH)) # extract the bits artifact in the temp working directory - with zipfile.ZipFile(bitsLocalArtLoc, 'r') as zref: - zref.extractall(prebuiltDir) + self.archive_extract(self.ASSET_BITS, sub_dir='prebuilt', format='zip') # extract the bits software in the temp working directory - with zipfile.ZipFile(bits_zip_file, 'r') as zref: - zref.extractall(self._workDir) - - with tarfile.open(grub_tar_file, 'r', encoding='utf-8') as tarball: - tarball.extractall(self._workDir) + self.archive_extract(bits_zip_file) + self.archive_extract(grub_tar_file) self.copy_test_scripts() self.copy_bits_config() @@ -333,7 +272,7 @@ class AcpiBitsTest(QemuBaseTest): #pylint: disable=too-many-instance-attributes """parse the log generated by running bits tests and check for failures. """ - debugconf = os.path.join(self._workDir, self._debugcon_log) + debugconf = self.scratch_file(self._debugcon_log) log = "" with open(debugconf, 'r', encoding='utf-8') as filehandle: log = filehandle.read() @@ -359,25 +298,18 @@ class AcpiBitsTest(QemuBaseTest): #pylint: disable=too-many-instance-attributes """ if self._vm: self.assertFalse(not self._vm.is_running) - if not os.getenv('BITS_DEBUG') and self._workDir: - self.logger.info('removing the work directory %s', self._workDir) - shutil.rmtree(self._workDir) - else: - self.logger.info('not removing the work directory %s ' \ - 'as BITS_DEBUG is ' \ - 'passed in the environment', self._workDir) super().tearDown() def test_acpi_smbios_bits(self): """The main test case implementation.""" - iso_file = os.path.join(self._workDir, - 'bits-%d.iso' %self.BITS_INTERNAL_VER) + self.set_machine('pc') + iso_file = self.scratch_file('bits-%d.iso' % self.BITS_INTERNAL_VER) self.assertTrue(os.access(iso_file, os.R_OK)) self._vm = QEMUBitsMachine(binary=self.qemu_bin, - base_temp_dir=self._workDir, + base_temp_dir=self.workdir, debugcon_log=self._debugcon_log, debugcon_addr=self._debugcon_addr) @@ -399,12 +331,10 @@ class AcpiBitsTest(QemuBaseTest): #pylint: disable=too-many-instance-attributes # biosbits has been configured to run all the specified test suites # in batch mode and then automatically initiate a vm shutdown. - # Set timeout to BITS_TIMEOUT for SHUTDOWN event from bits VM at par - # with the avocado test timeout. self._vm.event_wait('SHUTDOWN', timeout=BITS_TIMEOUT) self._vm.wait(timeout=None) self.logger.debug("Checking console output ...") self.parse_log() if __name__ == '__main__': - QemuBaseTest.main() + QemuSystemTest.main() diff --git a/tests/functional/test_alpha_clipper.py b/tests/functional/test_alpha_clipper.py new file mode 100755 index 0000000..c5d7181 --- /dev/null +++ b/tests/functional/test_alpha_clipper.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python3 +# +# Functional test that boots a Linux kernel on an Alpha Clipper machine +# and checks the console +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import LinuxKernelTest, Asset + + +class AlphaClipperTest(LinuxKernelTest): + + ASSET_KERNEL = Asset( + ('http://archive.debian.org/debian/dists/lenny/main/' + 'installer-alpha/20090123lenny10/images/cdrom/vmlinuz'), + '34f53da3fa32212e4f00b03cb944b2ad81c06bc8faaf9b7193b2e544ceeca576') + + def test_alpha_clipper(self): + self.set_machine('clipper') + kernel_path = self.ASSET_KERNEL.fetch() + + uncompressed_kernel = self.uncompress(self.ASSET_KERNEL, format="gz") + + self.vm.set_console() + kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'console=ttyS0' + self.vm.add_args('-nodefaults', + '-kernel', uncompressed_kernel, + '-append', kernel_command_line) + self.vm.launch() + console_pattern = 'Kernel command line: %s' % kernel_command_line + self.wait_for_console_pattern(console_pattern) + +if __name__ == '__main__': + LinuxKernelTest.main() diff --git a/tests/functional/test_alpha_replay.py b/tests/functional/test_alpha_replay.py new file mode 100755 index 0000000..24a17ef --- /dev/null +++ b/tests/functional/test_alpha_replay.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python3 +# +# Replay test that boots a Linux kernel on an Alpha machine +# and checks the console +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import Asset +from replay_kernel import ReplayKernelBase + + +class AlphaReplay(ReplayKernelBase): + + ASSET_KERNEL = Asset( + ('http://archive.debian.org/debian/dists/lenny/main/installer-alpha/' + '20090123lenny10/images/cdrom/vmlinuz'), + '34f53da3fa32212e4f00b03cb944b2ad81c06bc8faaf9b7193b2e544ceeca576') + + def test_clipper(self): + self.set_machine('clipper') + kernel_path = self.uncompress(self.ASSET_KERNEL, format='gz') + 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=9, + args=('-nodefaults', )) + + +if __name__ == '__main__': + ReplayKernelBase.main() diff --git a/tests/functional/test_arm_aspeed_ast1030.py b/tests/functional/test_arm_aspeed_ast1030.py new file mode 100755 index 0000000..77037f0 --- /dev/null +++ b/tests/functional/test_arm_aspeed_ast1030.py @@ -0,0 +1,73 @@ +#!/usr/bin/env python3 +# +# Functional test that boots the ASPEED SoCs with firmware +# +# Copyright (C) 2022 ASPEED Technology Inc +# +# 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 AST1030Machine(LinuxKernelTest): + + ASSET_ZEPHYR_3_00 = Asset( + ('https://github.com/AspeedTech-BMC' + '/zephyr/releases/download/v00.03.00/ast1030-evb-demo.zip'), + '37fe3ecd4a1b9d620971a15b96492a81093435396eeac69b6f3e384262ff555f') + + def test_ast1030_zephyros_3_00(self): + self.set_machine('ast1030-evb') + + kernel_name = "ast1030-evb-demo/zephyr.elf" + kernel_file = self.archive_extract( + self.ASSET_ZEPHYR_3_00, member=kernel_name) + + self.vm.set_console() + self.vm.add_args('-kernel', kernel_file, '-nographic') + self.vm.launch() + self.wait_for_console_pattern("Booting Zephyr OS") + exec_command_and_wait_for_pattern(self, "help", + "Available commands") + + ASSET_ZEPHYR_1_07 = Asset( + ('https://github.com/AspeedTech-BMC' + '/zephyr/releases/download/v00.01.07/ast1030-evb-demo.zip'), + 'ad52e27959746988afaed8429bf4e12ab988c05c4d07c9d90e13ec6f7be4574c') + + def test_ast1030_zephyros_1_07(self): + self.set_machine('ast1030-evb') + + kernel_name = "ast1030-evb-demo/zephyr.bin" + kernel_file = self.archive_extract( + self.ASSET_ZEPHYR_1_07, member=kernel_name) + + self.vm.set_console() + self.vm.add_args('-kernel', kernel_file, '-nographic') + self.vm.launch() + self.wait_for_console_pattern("Booting Zephyr OS") + for shell_cmd in [ + 'kernel stacks', + 'otp info conf', + 'otp info scu', + 'hwinfo devid', + 'crypto aes256_cbc_vault', + 'random get', + 'jtag JTAG1 sw_xfer high TMS', + 'adc ADC0 resolution 12', + 'adc ADC0 read 42', + 'adc ADC1 read 69', + 'i2c scan I2C_0', + 'i3c attach I3C_0', + 'hash test', + 'kernel uptime', + 'kernel reboot warm', + 'kernel uptime', + 'kernel reboot cold', + 'kernel uptime', + ]: exec_command_and_wait_for_pattern(self, shell_cmd, "uart:~$") + + +if __name__ == '__main__': + LinuxKernelTest.main() diff --git a/tests/functional/test_arm_aspeed_ast2500.py b/tests/functional/test_arm_aspeed_ast2500.py new file mode 100755 index 0000000..6923fe8 --- /dev/null +++ b/tests/functional/test_arm_aspeed_ast2500.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python3 +# +# Functional test that boots the ASPEED machines +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import Asset, exec_command_and_wait_for_pattern +from aspeed import AspeedTest + + +class AST2500Machine(AspeedTest): + + ASSET_BR2_202411_AST2500_FLASH = Asset( + ('https://github.com/legoater/qemu-aspeed-boot/raw/master/' + 'images/ast2500-evb/buildroot-2024.11/flash.img'), + '641e6906c18c0f19a2aeb48099d66d4771929c361001d554d0d45c667413e13a') + + def test_arm_ast2500_evb_buildroot(self): + self.set_machine('ast2500-evb') + + image_path = self.ASSET_BR2_202411_AST2500_FLASH.fetch() + + self.vm.add_args('-device', + 'tmp105,bus=aspeed.i2c.bus.3,address=0x4d,id=tmp-test') + self.do_test_arm_aspeed_buildroot_start(image_path, '0x0', + 'ast2500-evb login:') + + exec_command_and_wait_for_pattern(self, + 'echo lm75 0x4d > /sys/class/i2c-dev/i2c-3/device/new_device', + 'i2c i2c-3: new_device: Instantiated device lm75 at 0x4d') + exec_command_and_wait_for_pattern(self, + 'cat /sys/class/hwmon/hwmon1/temp1_input', '0') + self.vm.cmd('qom-set', path='/machine/peripheral/tmp-test', + property='temperature', value=18000) + exec_command_and_wait_for_pattern(self, + 'cat /sys/class/hwmon/hwmon1/temp1_input', '18000') + + self.do_test_arm_aspeed_buildroot_poweroff() + + ASSET_SDK_V906_AST2500 = Asset( + 'https://github.com/AspeedTech-BMC/openbmc/releases/download/v09.06/ast2500-default-obmc.tar.gz', + '542db84645b4efd8aed50385d7f4dd1caff379a987032311cfa7b563a3addb2a') + + def test_arm_ast2500_evb_sdk(self): + self.set_machine('ast2500-evb') + + self.archive_extract(self.ASSET_SDK_V906_AST2500) + + self.do_test_arm_aspeed_sdk_start( + self.scratch_file("ast2500-default", "image-bmc")) + + self.wait_for_console_pattern('ast2500-default login:') + + +if __name__ == '__main__': + AspeedTest.main() diff --git a/tests/functional/test_arm_aspeed_ast2600.py b/tests/functional/test_arm_aspeed_ast2600.py new file mode 100755 index 0000000..fdae4c9 --- /dev/null +++ b/tests/functional/test_arm_aspeed_ast2600.py @@ -0,0 +1,140 @@ +#!/usr/bin/env python3 +# +# Functional test that boots the ASPEED machines +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import os +import time +import tempfile +import subprocess + +from qemu_test import Asset +from aspeed import AspeedTest +from qemu_test import exec_command_and_wait_for_pattern, skipIfMissingCommands + + +class AST2600Machine(AspeedTest): + + ASSET_BR2_202411_AST2600_FLASH = Asset( + ('https://github.com/legoater/qemu-aspeed-boot/raw/master/' + 'images/ast2600-evb/buildroot-2024.11/flash.img'), + '4bb2f3dfdea31199b51d66b42f686dc5374c144a7346fdc650194a5578b73609') + + def test_arm_ast2600_evb_buildroot(self): + self.set_machine('ast2600-evb') + + image_path = self.ASSET_BR2_202411_AST2600_FLASH.fetch() + + self.vm.add_args('-device', + 'tmp105,bus=aspeed.i2c.bus.3,address=0x4d,id=tmp-test') + self.vm.add_args('-device', + 'ds1338,bus=aspeed.i2c.bus.3,address=0x32') + self.vm.add_args('-device', + 'i2c-echo,bus=aspeed.i2c.bus.3,address=0x42') + self.do_test_arm_aspeed_buildroot_start(image_path, '0xf00', + 'ast2600-evb login:') + + exec_command_and_wait_for_pattern(self, + 'echo lm75 0x4d > /sys/class/i2c-dev/i2c-3/device/new_device', + 'i2c i2c-3: new_device: Instantiated device lm75 at 0x4d') + exec_command_and_wait_for_pattern(self, + 'cat /sys/class/hwmon/hwmon1/temp1_input', '0') + self.vm.cmd('qom-set', path='/machine/peripheral/tmp-test', + property='temperature', value=18000) + exec_command_and_wait_for_pattern(self, + 'cat /sys/class/hwmon/hwmon1/temp1_input', '18000') + + exec_command_and_wait_for_pattern(self, + 'echo ds1307 0x32 > /sys/class/i2c-dev/i2c-3/device/new_device', + 'i2c i2c-3: new_device: Instantiated device ds1307 at 0x32') + year = time.strftime("%Y") + exec_command_and_wait_for_pattern(self, 'hwclock -f /dev/rtc1', year) + + exec_command_and_wait_for_pattern(self, + 'echo slave-24c02 0x1064 > /sys/bus/i2c/devices/i2c-3/new_device', + 'i2c i2c-3: new_device: Instantiated device slave-24c02 at 0x64') + exec_command_and_wait_for_pattern(self, + 'i2cset -y 3 0x42 0x64 0x00 0xaa i', '#') + exec_command_and_wait_for_pattern(self, + 'hexdump /sys/bus/i2c/devices/3-1064/slave-eeprom', + '0000000 ffaa ffff ffff ffff ffff ffff ffff ffff') + self.do_test_arm_aspeed_buildroot_poweroff() + + ASSET_BR2_202302_AST2600_TPM_FLASH = Asset( + ('https://github.com/legoater/qemu-aspeed-boot/raw/master/' + 'images/ast2600-evb/buildroot-2023.02-tpm/flash.img'), + 'a46009ae8a5403a0826d607215e731a8c68d27c14c41e55331706b8f9c7bd997') + + @skipIfMissingCommands('swtpm') + def test_arm_ast2600_evb_buildroot_tpm(self): + self.set_machine('ast2600-evb') + + image_path = self.ASSET_BR2_202302_AST2600_TPM_FLASH.fetch() + + tpmstate_dir = tempfile.TemporaryDirectory(prefix="qemu_") + socket = os.path.join(tpmstate_dir.name, 'swtpm-socket') + + # We must put the TPM state dir in /tmp/, not the build dir, + # because some distros use AppArmor to lock down swtpm and + # restrict the set of locations it can access files in. + subprocess.run(['swtpm', 'socket', '-d', '--tpm2', + '--tpmstate', f'dir={tpmstate_dir.name}', + '--ctrl', f'type=unixio,path={socket}']) + + self.vm.add_args('-chardev', f'socket,id=chrtpm,path={socket}') + self.vm.add_args('-tpmdev', 'emulator,id=tpm0,chardev=chrtpm') + self.vm.add_args('-device', + 'tpm-tis-i2c,tpmdev=tpm0,bus=aspeed.i2c.bus.12,address=0x2e') + self.do_test_arm_aspeed_buildroot_start(image_path, '0xf00', 'Aspeed AST2600 EVB') + + exec_command_and_wait_for_pattern(self, + 'echo tpm_tis_i2c 0x2e > /sys/bus/i2c/devices/i2c-12/new_device', + 'tpm_tis_i2c 12-002e: 2.0 TPM (device-id 0x1, rev-id 1)') + exec_command_and_wait_for_pattern(self, + 'cat /sys/class/tpm/tpm0/pcr-sha256/0', + 'B804724EA13F52A9072BA87FE8FDCC497DFC9DF9AA15B9088694639C431688E0') + + self.do_test_arm_aspeed_buildroot_poweroff() + + ASSET_SDK_V906_AST2600 = Asset( + 'https://github.com/AspeedTech-BMC/openbmc/releases/download/v09.06/ast2600-default-obmc.tar.gz', + '768d76e247896ad78c154b9cff4f766da2ce65f217d620b286a4a03a8a4f68f5') + + def test_arm_ast2600_evb_sdk(self): + self.set_machine('ast2600-evb') + + self.archive_extract(self.ASSET_SDK_V906_AST2600) + + self.vm.add_args('-device', + 'tmp105,bus=aspeed.i2c.bus.5,address=0x4d,id=tmp-test') + self.vm.add_args('-device', + 'ds1338,bus=aspeed.i2c.bus.5,address=0x32') + self.do_test_arm_aspeed_sdk_start( + self.scratch_file("ast2600-default", "image-bmc")) + + self.wait_for_console_pattern('ast2600-default login:') + + exec_command_and_wait_for_pattern(self, 'root', 'Password:') + exec_command_and_wait_for_pattern(self, '0penBmc', + 'root@ast2600-default:~#') + + exec_command_and_wait_for_pattern(self, + 'echo lm75 0x4d > /sys/class/i2c-dev/i2c-5/device/new_device', + 'i2c i2c-5: new_device: Instantiated device lm75 at 0x4d') + exec_command_and_wait_for_pattern(self, + 'cat /sys/class/hwmon/hwmon19/temp1_input', '0') + self.vm.cmd('qom-set', path='/machine/peripheral/tmp-test', + property='temperature', value=18000) + exec_command_and_wait_for_pattern(self, + 'cat /sys/class/hwmon/hwmon19/temp1_input', '18000') + + exec_command_and_wait_for_pattern(self, + 'echo ds1307 0x32 > /sys/class/i2c-dev/i2c-5/device/new_device', + 'i2c i2c-5: new_device: Instantiated device ds1307 at 0x32') + year = time.strftime("%Y") + exec_command_and_wait_for_pattern(self, + '/sbin/hwclock -f /dev/rtc1', year) + +if __name__ == '__main__': + AspeedTest.main() diff --git a/tests/functional/test_arm_aspeed_bletchley.py b/tests/functional/test_arm_aspeed_bletchley.py new file mode 100644 index 0000000..5a60b24 --- /dev/null +++ b/tests/functional/test_arm_aspeed_bletchley.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python3 +# +# Functional test that boots the ASPEED machines +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import Asset +from aspeed import AspeedTest + + +class BletchleyMachine(AspeedTest): + + ASSET_BLETCHLEY_FLASH = Asset( + 'https://github.com/legoater/qemu-aspeed-boot/raw/master/images/bletchley-bmc/openbmc-20250128071329/obmc-phosphor-image-bletchley-20250128071329.static.mtd.xz', + 'db21d04d47d7bb2a276f59d308614b4dfb70b9c7c81facbbca40a3977a2d8844') + + def test_arm_ast2600_bletchley_openbmc(self): + image_path = self.uncompress(self.ASSET_BLETCHLEY_FLASH) + + self.do_test_arm_aspeed_openbmc('bletchley-bmc', image=image_path, + uboot='2019.04', cpu_id='0xf00', + soc='AST2600 rev A3') + +if __name__ == '__main__': + AspeedTest.main() diff --git a/tests/functional/test_arm_aspeed_palmetto.py b/tests/functional/test_arm_aspeed_palmetto.py new file mode 100755 index 0000000..ff0b821 --- /dev/null +++ b/tests/functional/test_arm_aspeed_palmetto.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python3 +# +# Functional test that boots the ASPEED machines +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import Asset +from aspeed import AspeedTest + + +class PalmettoMachine(AspeedTest): + + ASSET_PALMETTO_FLASH = Asset( + 'https://github.com/legoater/qemu-aspeed-boot/raw/master/images/palmetto-bmc/openbmc-20250128071432/obmc-phosphor-image-palmetto-20250128071432.static.mtd', + 'bce7c392eec75c707a91cfc8fad7ca9a69d7e4f10df936930d65c1cb9897ac81') + + def test_arm_ast2400_palmetto_openbmc(self): + image_path = self.ASSET_PALMETTO_FLASH.fetch() + + self.do_test_arm_aspeed_openbmc('palmetto-bmc', image=image_path, + uboot='2019.04', cpu_id='0x0', + soc='AST2400 rev A1') + +if __name__ == '__main__': + AspeedTest.main() diff --git a/tests/functional/test_arm_aspeed_rainier.py b/tests/functional/test_arm_aspeed_rainier.py new file mode 100755 index 0000000..602d619 --- /dev/null +++ b/tests/functional/test_arm_aspeed_rainier.py @@ -0,0 +1,65 @@ +#!/usr/bin/env python3 +# +# Functional test that boots the ASPEED machines +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import Asset +from aspeed import AspeedTest + +class RainierMachine(AspeedTest): + + ASSET_RAINIER_EMMC = Asset( + ('https://fileserver.linaro.org/s/B6pJTwWEkzSDi36/download/' + 'mmc-p10bmc-20240617.qcow2'), + 'd523fb478d2b84d5adc5658d08502bc64b1486955683814f89c6137518acd90b') + + def test_arm_aspeed_emmc_boot(self): + self.set_machine('rainier-bmc') + self.require_netdev('user') + + image_path = self.ASSET_RAINIER_EMMC.fetch() + + self.vm.set_console() + self.vm.add_args('-drive', + 'file=' + image_path + ',if=sd,id=sd2,index=2', + '-net', 'nic', '-net', 'user', '-snapshot') + self.vm.launch() + + self.wait_for_console_pattern('U-Boot SPL 2019.04') + self.wait_for_console_pattern('Trying to boot from MMC1') + self.wait_for_console_pattern('U-Boot 2019.04') + self.wait_for_console_pattern('eMMC 2nd Boot') + self.wait_for_console_pattern('## Loading kernel from FIT Image') + self.wait_for_console_pattern('Starting kernel ...') + self.wait_for_console_pattern('Booting Linux on physical CPU 0xf00') + self.wait_for_console_pattern('mmcblk0: p1 p2 p3 p4 p5 p6 p7') + self.wait_for_console_pattern('IBM eBMC (OpenBMC for IBM Enterprise') + + ASSET_DEBIAN_LINUX_ARMHF_DEB = Asset( + ('http://snapshot.debian.org/archive/debian/20220606T211338Z/pool/main/l/linux/linux-image-5.17.0-2-armmp_5.17.6-1%2Bb1_armhf.deb'), + '8acb2b4439faedc2f3ed4bdb2847ad4f6e0491f73debaeb7f660c8abe4dcdc0e') + + def test_arm_debian_kernel_boot(self): + self.set_machine('rainier-bmc') + + kernel_path = self.archive_extract( + self.ASSET_DEBIAN_LINUX_ARMHF_DEB, + member='boot/vmlinuz-5.17.0-2-armmp') + dtb_path = self.archive_extract( + self.ASSET_DEBIAN_LINUX_ARMHF_DEB, + member='usr/lib/linux-image-5.17.0-2-armmp/aspeed-bmc-ibm-rainier.dtb') + + self.vm.set_console() + self.vm.add_args('-kernel', kernel_path, + '-dtb', dtb_path, + '-net', 'nic') + self.vm.launch() + + self.wait_for_console_pattern("Booting Linux on physical CPU 0xf00") + self.wait_for_console_pattern("SMP: Total of 2 processors activated") + self.wait_for_console_pattern("No filesystem could mount root") + + +if __name__ == '__main__': + AspeedTest.main() diff --git a/tests/functional/test_arm_aspeed_romulus.py b/tests/functional/test_arm_aspeed_romulus.py new file mode 100755 index 0000000..0447212 --- /dev/null +++ b/tests/functional/test_arm_aspeed_romulus.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python3 +# +# Functional test that boots the ASPEED machines +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import Asset +from aspeed import AspeedTest + + +class RomulusMachine(AspeedTest): + + ASSET_ROMULUS_FLASH = Asset( + 'https://github.com/legoater/qemu-aspeed-boot/raw/master/images/romulus-bmc/openbmc-20250128071340/obmc-phosphor-image-romulus-20250128071340.static.mtd', + '6d031376440c82ed9d087d25e9fa76aea75b42f80daa252ec402c0bc3cf6cf5b') + + def test_arm_ast2500_romulus_openbmc(self): + image_path = self.ASSET_ROMULUS_FLASH.fetch() + + self.do_test_arm_aspeed_openbmc('romulus-bmc', image=image_path, + uboot='2019.04', cpu_id='0x0', + soc='AST2500 rev A1') + +if __name__ == '__main__': + AspeedTest.main() diff --git a/tests/functional/test_arm_aspeed_witherspoon.py b/tests/functional/test_arm_aspeed_witherspoon.py new file mode 100644 index 0000000..51a2d47 --- /dev/null +++ b/tests/functional/test_arm_aspeed_witherspoon.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python3 +# +# Functional test that boots the ASPEED machines +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import Asset +from aspeed import AspeedTest + + +class WitherspoonMachine(AspeedTest): + + ASSET_WITHERSPOON_FLASH = Asset( + 'https://github.com/legoater/qemu-aspeed-boot/raw/master/images/witherspoon-bmc/openbmc-20240618035022/obmc-phosphor-image-witherspoon-20240618035022.ubi.mtd', + '937d9ed449ea6c6cbed983519088a42d0cafe276bcfe4fce07772ca6673f9213') + + def test_arm_ast2500_witherspoon_openbmc(self): + image_path = self.ASSET_WITHERSPOON_FLASH.fetch() + + self.do_test_arm_aspeed_openbmc('witherspoon-bmc', image=image_path, + uboot='2016.07', cpu_id='0x0', + soc='AST2500 rev A1') + +if __name__ == '__main__': + AspeedTest.main() diff --git a/tests/functional/test_arm_bflt.py b/tests/functional/test_arm_bflt.py index 281925d..f273fc8 100755 --- a/tests/functional/test_arm_bflt.py +++ b/tests/functional/test_arm_bflt.py @@ -6,13 +6,10 @@ # # SPDX-License-Identifier: GPL-2.0-or-later -import os import bz2 from qemu_test import QemuUserTest, Asset -from qemu_test import has_cmd -from qemu_test.utils import cpio_extract -from unittest import skipUnless +from qemu_test import skipIfMissingCommands, skipUntrustedTest class LoadBFLT(QemuUserTest): @@ -21,15 +18,15 @@ class LoadBFLT(QemuUserTest): ('https://elinux.org/images/5/51/Stm32_mini_rootfs.cpio.bz2'), 'eefb788e4980c9e8d6c9d60ce7d15d4da6bf4fbc6a80f487673824600d5ba9cc') - @skipUnless(*has_cmd('cpio')) - @skipUnless(os.getenv('QEMU_TEST_ALLOW_UNTRUSTED_CODE'), 'untrusted code') + @skipIfMissingCommands('cpio') + @skipUntrustedTest() def test_stm32(self): # See https://elinux.org/STM32#User_Space rootfs_path_bz2 = self.ASSET_ROOTFS.fetch() - busybox_path = os.path.join(self.workdir, "bin/busybox") + busybox_path = self.scratch_file("bin", "busybox") with bz2.open(rootfs_path_bz2, 'rb') as cpio_handle: - cpio_extract(cpio_handle, self.workdir) + self.archive_extract(cpio_handle, format="cpio") res = self.run_cmd(busybox_path) ver = 'BusyBox v1.24.0.git (2015-02-03 22:17:13 CET) multi-call binary.' diff --git a/tests/functional/test_arm_bpim2u.py b/tests/functional/test_arm_bpim2u.py new file mode 100755 index 0000000..8bed64b --- /dev/null +++ b/tests/functional/test_arm_bpim2u.py @@ -0,0 +1,180 @@ +#!/usr/bin/env python3 +# +# Functional test that boots a Linux kernel on a Banana Pi machine +# and checks the console +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import os + +from qemu_test import LinuxKernelTest, exec_command_and_wait_for_pattern +from qemu_test import Asset, interrupt_interactive_console_until_pattern +from qemu_test import skipBigDataTest +from qemu_test.utils import image_pow2ceil_expand + + +class BananaPiMachine(LinuxKernelTest): + + ASSET_DEB = Asset( + ('https://apt.armbian.com/pool/main/l/linux-6.6.16/' + 'linux-image-current-sunxi_24.2.1_armhf__6.6.16-Seb3e-D6b4a-P2359-Ce96bHfe66-HK01ba-V014b-B067e-R448a.deb'), + '3d968c15b121ede871dce49d13ee7644d6f74b6b121b84c9a40f51b0c80d6d22') + + ASSET_INITRD = Asset( + ('https://github.com/groeck/linux-build-test/raw/' + '2eb0a73b5d5a28df3170c546ddaaa9757e1e0848/rootfs/' + 'arm/rootfs-armv7a.cpio.gz'), + '2c8dbdb16ea7af2dfbcbea96044dde639fb07d09fd3c4fb31f2027ef71e55ddd') + + ASSET_ROOTFS = Asset( + ('http://storage.kernelci.org/images/rootfs/buildroot/' + 'buildroot-baseline/20230703.0/armel/rootfs.ext2.xz'), + '42b44a12965ac0afe9a88378527fb698a7dc76af50495efc2361ee1595b4e5c6') + + ASSET_SD_IMAGE = Asset( + ('https://downloads.openwrt.org/releases/22.03.3/targets/sunxi/cortexa7/' + 'openwrt-22.03.3-sunxi-cortexa7-sinovoip_bananapi-m2-ultra-ext4-sdcard.img.gz'), + '5b41b4e11423e562c6011640f9a7cd3bdd0a3d42b83430f7caa70a432e6cd82c') + + def test_arm_bpim2u(self): + self.set_machine('bpim2u') + kernel_path = self.archive_extract( + self.ASSET_DEB, member='boot/vmlinuz-6.6.16-current-sunxi') + dtb_path = ('usr/lib/linux-image-6.6.16-current-sunxi/' + 'sun8i-r40-bananapi-m2-ultra.dtb') + dtb_path = self.archive_extract(self.ASSET_DEB, member=dtb_path) + + self.vm.set_console() + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'console=ttyS0,115200n8 ' + 'earlycon=uart,mmio32,0x1c28000') + self.vm.add_args('-kernel', kernel_path, + '-dtb', dtb_path, + '-append', kernel_command_line) + self.vm.launch() + console_pattern = 'Kernel command line: %s' % kernel_command_line + self.wait_for_console_pattern(console_pattern) + os.remove(kernel_path) + os.remove(dtb_path) + + def test_arm_bpim2u_initrd(self): + self.set_machine('bpim2u') + kernel_path = self.archive_extract( + self.ASSET_DEB, member='boot/vmlinuz-6.6.16-current-sunxi') + dtb_path = ('usr/lib/linux-image-6.6.16-current-sunxi/' + 'sun8i-r40-bananapi-m2-ultra.dtb') + dtb_path = self.archive_extract(self.ASSET_DEB, member=dtb_path) + initrd_path = self.uncompress(self.ASSET_INITRD) + + self.vm.set_console() + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'console=ttyS0,115200 ' + 'panic=-1 noreboot') + self.vm.add_args('-kernel', kernel_path, + '-dtb', dtb_path, + '-initrd', initrd_path, + '-append', kernel_command_line, + '-no-reboot') + self.vm.launch() + self.wait_for_console_pattern('Boot successful.') + + exec_command_and_wait_for_pattern(self, 'cat /proc/cpuinfo', + 'Allwinner sun8i Family') + exec_command_and_wait_for_pattern(self, 'cat /proc/iomem', + 'system-control@1c00000') + exec_command_and_wait_for_pattern(self, 'reboot', + 'reboot: Restarting system') + # Wait for VM to shut down gracefully + self.vm.wait() + os.remove(kernel_path) + os.remove(dtb_path) + os.remove(initrd_path) + + def test_arm_bpim2u_gmac(self): + self.set_machine('bpim2u') + self.require_netdev('user') + + deb_path = self.ASSET_DEB.fetch() + kernel_path = self.archive_extract( + self.ASSET_DEB, member='boot/vmlinuz-6.6.16-current-sunxi') + dtb_path = ('usr/lib/linux-image-6.6.16-current-sunxi/' + 'sun8i-r40-bananapi-m2-ultra.dtb') + dtb_path = self.archive_extract(self.ASSET_DEB, member=dtb_path) + rootfs_path = self.uncompress(self.ASSET_ROOTFS) + image_pow2ceil_expand(rootfs_path) + + self.vm.set_console() + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'console=ttyS0,115200 ' + 'root=b300 rootwait rw ' + 'panic=-1 noreboot') + self.vm.add_args('-kernel', kernel_path, + '-dtb', dtb_path, + '-drive', 'file=' + rootfs_path + ',if=sd,format=raw', + '-net', 'nic,model=gmac,netdev=host_gmac', + '-netdev', 'user,id=host_gmac', + '-append', kernel_command_line, + '-no-reboot') + self.vm.launch() + shell_ready = "/bin/sh: can't access tty; job control turned off" + self.wait_for_console_pattern(shell_ready) + + exec_command_and_wait_for_pattern(self, 'cat /proc/cpuinfo', + 'Allwinner sun8i Family') + exec_command_and_wait_for_pattern(self, 'cat /proc/partitions', + 'mmcblk') + exec_command_and_wait_for_pattern(self, 'ifconfig eth0 up', + 'eth0: Link is Up') + exec_command_and_wait_for_pattern(self, 'udhcpc eth0', + 'udhcpc: lease of 10.0.2.15 obtained') + exec_command_and_wait_for_pattern(self, 'ping -c 3 10.0.2.2', + '3 packets transmitted, 3 packets received, 0% packet loss') + exec_command_and_wait_for_pattern(self, 'reboot', + 'reboot: Restarting system') + # Wait for VM to shut down gracefully + self.vm.wait() + os.remove(kernel_path) + os.remove(dtb_path) + os.remove(rootfs_path) + + @skipBigDataTest() + def test_arm_bpim2u_openwrt_22_03_3(self): + self.set_machine('bpim2u') + self.require_netdev('user') + + # This test download a 8.9 MiB compressed image and expand it + # to 127 MiB. + image_path = self.uncompress(self.ASSET_SD_IMAGE) + image_pow2ceil_expand(image_path) + + self.vm.set_console() + self.vm.add_args('-drive', 'file=' + image_path + ',if=sd,format=raw', + '-nic', 'user', + '-no-reboot') + self.vm.launch() + + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'usbcore.nousb ' + 'noreboot') + + self.wait_for_console_pattern('U-Boot SPL') + + interrupt_interactive_console_until_pattern( + self, 'Hit any key to stop autoboot:', '=>') + exec_command_and_wait_for_pattern(self, "setenv extraargs '" + + kernel_command_line + "'", '=>') + exec_command_and_wait_for_pattern(self, 'boot', 'Starting kernel ...') + + self.wait_for_console_pattern( + 'Please press Enter to activate this console.') + + exec_command_and_wait_for_pattern(self, ' ', 'root@') + + exec_command_and_wait_for_pattern(self, 'cat /proc/cpuinfo', + 'Allwinner sun8i Family') + exec_command_and_wait_for_pattern(self, 'cat /proc/iomem', + 'system-control@1c00000') + os.remove(image_path) + +if __name__ == '__main__': + LinuxKernelTest.main() diff --git a/tests/functional/test_arm_canona1100.py b/tests/functional/test_arm_canona1100.py index 65f1228..21a1a59 100755 --- a/tests/functional/test_arm_canona1100.py +++ b/tests/functional/test_arm_canona1100.py @@ -12,7 +12,7 @@ from qemu_test import QemuSystemTest, Asset from qemu_test import wait_for_console_pattern -from qemu_test.utils import archive_extract + class CanonA1100Machine(QemuSystemTest): """Boots the barebox firmware and checks that the console is operational""" @@ -26,12 +26,10 @@ class CanonA1100Machine(QemuSystemTest): def test_arm_canona1100(self): self.set_machine('canon-a1100') - file_path = self.ASSET_BIOS.fetch() - archive_extract(file_path, dest_dir=self.workdir, - member="day18/barebox.canon-a1100.bin") + bios = self.archive_extract(self.ASSET_BIOS, + member="day18/barebox.canon-a1100.bin") self.vm.set_console() - self.vm.add_args('-bios', - self.workdir + '/day18/barebox.canon-a1100.bin') + self.vm.add_args('-bios', bios) self.vm.launch() wait_for_console_pattern(self, 'running /env/bin/init') diff --git a/tests/functional/test_arm_collie.py b/tests/functional/test_arm_collie.py new file mode 100755 index 0000000..fe1be3d --- /dev/null +++ b/tests/functional/test_arm_collie.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python3 +# +# Functional test that boots a Linux kernel on a collie machine +# and checks the console +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import LinuxKernelTest, Asset + + +class CollieTest(LinuxKernelTest): + + ASSET_ZIMAGE = Asset( + 'https://github.com/groeck/linux-test-downloads/raw/225223f2ad7d637b34426810bf6c3b727b76a718/collie/zImage', + '10ace8abf9e0875ef8a83b8829cc3b5b50bc6d7bc3ca29f19f49f5673a43c13b') + + ASSET_ROOTFS = Asset( + 'https://github.com/groeck/linux-test-downloads/raw/225223f2ad7d637b34426810bf6c3b727b76a718/collie/rootfs-sa110.cpio', + '89ccaaa5c6b33331887047e1618ffe81b0f55909173944347d5d2426f3bcc1f2') + + def test_arm_collie(self): + self.set_machine('collie') + zimage_path = self.ASSET_ZIMAGE.fetch() + rootfs_path = self.ASSET_ROOTFS.fetch() + self.vm.add_args('-append', 'rdinit=/sbin/init console=ttySA1') + self.launch_kernel(zimage_path, + initrd=rootfs_path, + wait_for='reboot: Restarting system') + +if __name__ == '__main__': + LinuxKernelTest.main() diff --git a/tests/functional/test_arm_cubieboard.py b/tests/functional/test_arm_cubieboard.py new file mode 100755 index 0000000..b536c2f --- /dev/null +++ b/tests/functional/test_arm_cubieboard.py @@ -0,0 +1,144 @@ +#!/usr/bin/env python3 +# +# Functional test that boots a Linux kernel and checks the console +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import LinuxKernelTest, Asset, exec_command_and_wait_for_pattern +from qemu_test import interrupt_interactive_console_until_pattern +from qemu_test import skipBigDataTest +from qemu_test.utils import image_pow2ceil_expand + + +class CubieboardMachine(LinuxKernelTest): + + ASSET_DEB = Asset( + ('https://apt.armbian.com/pool/main/l/linux-6.6.16/' + 'linux-image-current-sunxi_24.2.1_armhf__6.6.16-Seb3e-D6b4a-P2359-Ce96bHfe66-HK01ba-V014b-B067e-R448a.deb'), + '3d968c15b121ede871dce49d13ee7644d6f74b6b121b84c9a40f51b0c80d6d22') + + ASSET_INITRD = Asset( + ('https://github.com/groeck/linux-build-test/raw/' + '2eb0a73b5d5a28df3170c546ddaaa9757e1e0848/rootfs/' + 'arm/rootfs-armv5.cpio.gz'), + '334b8d256db67a3f2b3ad070aa08b5ade39624e0e7e35b02f4359a577bc8f39b') + + ASSET_SATA_ROOTFS = Asset( + ('https://github.com/groeck/linux-build-test/raw/' + '2eb0a73b5d5a28df3170c546ddaaa9757e1e0848/rootfs/' + 'arm/rootfs-armv5.ext2.gz'), + '17fc750da568580b39372133051ef2f0a963c0c0b369b845614442d025701745') + + ASSET_OPENWRT = Asset( + ('https://downloads.openwrt.org/releases/22.03.2/targets/sunxi/cortexa8/' + 'openwrt-22.03.2-sunxi-cortexa8-cubietech_a10-cubieboard-ext4-sdcard.img.gz'), + '94b5ecbfbc0b3b56276e5146b899eafa2ac5dc2d08733d6705af9f144f39f554') + + def test_arm_cubieboard_initrd(self): + self.set_machine('cubieboard') + kernel_path = self.archive_extract( + self.ASSET_DEB, member='boot/vmlinuz-6.6.16-current-sunxi') + dtb_path = ('usr/lib/linux-image-6.6.16-current-sunxi/' + + 'sun4i-a10-cubieboard.dtb') + dtb_path = self.archive_extract(self.ASSET_DEB, member=dtb_path) + initrd_path = self.uncompress(self.ASSET_INITRD) + + self.vm.set_console() + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'console=ttyS0,115200 ' + 'usbcore.nousb ' + 'panic=-1 noreboot') + self.vm.add_args('-kernel', kernel_path, + '-dtb', dtb_path, + '-initrd', initrd_path, + '-append', kernel_command_line, + '-no-reboot') + self.vm.launch() + self.wait_for_console_pattern('Boot successful.') + + exec_command_and_wait_for_pattern(self, 'cat /proc/cpuinfo', + 'Allwinner sun4i/sun5i') + exec_command_and_wait_for_pattern(self, 'cat /proc/iomem', + 'system-control@1c00000') + exec_command_and_wait_for_pattern(self, 'reboot', + 'reboot: Restarting system') + # Wait for VM to shut down gracefully + self.vm.wait() + + def test_arm_cubieboard_sata(self): + self.set_machine('cubieboard') + kernel_path = self.archive_extract( + self.ASSET_DEB, member='boot/vmlinuz-6.6.16-current-sunxi') + dtb_path = ('usr/lib/linux-image-6.6.16-current-sunxi/' + + 'sun4i-a10-cubieboard.dtb') + dtb_path = self.archive_extract(self.ASSET_DEB, member=dtb_path) + + rootfs_path = self.uncompress(self.ASSET_SATA_ROOTFS) + + self.vm.set_console() + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'console=ttyS0,115200 ' + 'usbcore.nousb ' + 'root=/dev/sda ro ' + 'panic=-1 noreboot') + self.vm.add_args('-kernel', kernel_path, + '-dtb', dtb_path, + '-drive', 'if=none,format=raw,id=disk0,file=' + + rootfs_path, + '-device', 'ide-hd,bus=ide.0,drive=disk0', + '-append', kernel_command_line, + '-no-reboot') + self.vm.launch() + self.wait_for_console_pattern('Boot successful.') + + exec_command_and_wait_for_pattern(self, 'cat /proc/cpuinfo', + 'Allwinner sun4i/sun5i') + exec_command_and_wait_for_pattern(self, 'cat /proc/partitions', + 'sda') + exec_command_and_wait_for_pattern(self, 'reboot', + 'reboot: Restarting system') + # Wait for VM to shut down gracefully + self.vm.wait() + + @skipBigDataTest() + def test_arm_cubieboard_openwrt_22_03_2(self): + # This test download a 7.5 MiB compressed image and expand it + # to 126 MiB. + self.set_machine('cubieboard') + self.require_netdev('user') + + image_path = self.uncompress(self.ASSET_OPENWRT) + image_pow2ceil_expand(image_path) + + self.vm.set_console() + self.vm.add_args('-drive', 'file=' + image_path + ',if=sd,format=raw', + '-nic', 'user', + '-no-reboot') + self.vm.launch() + + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'usbcore.nousb ' + 'noreboot') + + self.wait_for_console_pattern('U-Boot SPL') + + interrupt_interactive_console_until_pattern( + self, 'Hit any key to stop autoboot:', '=>') + exec_command_and_wait_for_pattern(self, "setenv extraargs '" + + kernel_command_line + "'", '=>') + exec_command_and_wait_for_pattern(self, 'boot', 'Starting kernel ...') + + self.wait_for_console_pattern( + 'Please press Enter to activate this console.') + + exec_command_and_wait_for_pattern(self, ' ', 'root@') + + exec_command_and_wait_for_pattern(self, 'cat /proc/cpuinfo', + 'Allwinner sun4i/sun5i') + exec_command_and_wait_for_pattern(self, 'reboot', + 'reboot: Restarting system') + # Wait for VM to shut down gracefully + self.vm.wait() + +if __name__ == '__main__': + LinuxKernelTest.main() diff --git a/tests/functional/test_arm_emcraft_sf2.py b/tests/functional/test_arm_emcraft_sf2.py new file mode 100755 index 0000000..f9f3f06 --- /dev/null +++ b/tests/functional/test_arm_emcraft_sf2.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python3 +# +# Functional test that boots a Linux kernel and checks the console +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import os +import shutil + +from qemu_test import LinuxKernelTest, Asset, exec_command_and_wait_for_pattern +from qemu_test.utils import file_truncate + +class EmcraftSf2Machine(LinuxKernelTest): + + ASSET_UBOOT = Asset( + ('https://raw.githubusercontent.com/Subbaraya-Sundeep/qemu-test-binaries/' + 'fe371d32e50ca682391e1e70ab98c2942aeffb01/u-boot'), + '5c6a15103375db11b21f2236473679a9dbbed6d89652bfcdd501c263d68ab725') + + ASSET_SPI = Asset( + ('https://raw.githubusercontent.com/Subbaraya-Sundeep/qemu-test-binaries/' + 'fe371d32e50ca682391e1e70ab98c2942aeffb01/spi.bin'), + 'cd9bdd2c4cb55a59c3adb6bcf74881667c4500dde0570a43aa3be2b17eecfdb6') + + def test_arm_emcraft_sf2(self): + self.set_machine('emcraft-sf2') + self.require_netdev('user') + + uboot_path = self.ASSET_UBOOT.fetch() + spi_path = self.ASSET_SPI.fetch() + spi_path_rw = self.scratch_file('spi.bin') + shutil.copy(spi_path, spi_path_rw) + os.chmod(spi_path_rw, 0o600) + + file_truncate(spi_path_rw, 16 << 20) # Spansion S25FL128SDPBHICO is 16 MiB + + self.vm.set_console() + kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + self.vm.add_args('-kernel', uboot_path, + '-append', kernel_command_line, + '-drive', 'file=' + spi_path_rw + ',if=mtd,format=raw', + '-no-reboot') + self.vm.launch() + self.wait_for_console_pattern('Enter \'help\' for a list') + + exec_command_and_wait_for_pattern(self, 'ifconfig eth0 10.0.2.15', + 'eth0: link becomes ready') + exec_command_and_wait_for_pattern(self, 'ping -c 3 10.0.2.2', + '3 packets transmitted, 3 packets received, 0% packet loss') + +if __name__ == '__main__': + LinuxKernelTest.main() diff --git a/tests/functional/test_arm_integratorcp.py b/tests/functional/test_arm_integratorcp.py index 0fe083f..4f00924 100755 --- a/tests/functional/test_arm_integratorcp.py +++ b/tests/functional/test_arm_integratorcp.py @@ -12,25 +12,11 @@ # # SPDX-License-Identifier: GPL-2.0-or-later -import os import logging from qemu_test import QemuSystemTest, Asset from qemu_test import wait_for_console_pattern -from unittest import skipUnless - - -NUMPY_AVAILABLE = True -try: - import numpy as np -except ImportError: - NUMPY_AVAILABLE = False - -CV2_AVAILABLE = True -try: - import cv2 -except ImportError: - CV2_AVAILABLE = False +from qemu_test import skipIfMissingImports, skipUntrustedTest class IntegratorMachine(QemuSystemTest): @@ -63,7 +49,7 @@ class IntegratorMachine(QemuSystemTest): '-append', 'printk.time=0 console=ttyAMA0') self.vm.launch() - @skipUnless(os.getenv('QEMU_TEST_ALLOW_UNTRUSTED_CODE'), 'untrusted code') + @skipUntrustedTest() def test_integratorcp_console(self): """ Boots the Linux kernel and checks that the console is operational @@ -71,22 +57,26 @@ class IntegratorMachine(QemuSystemTest): self.boot_integratorcp() wait_for_console_pattern(self, 'Log in as root') - @skipUnless(NUMPY_AVAILABLE, 'Python NumPy not installed') - @skipUnless(CV2_AVAILABLE, 'Python OpenCV not installed') - @skipUnless(os.getenv('QEMU_TEST_ALLOW_UNTRUSTED_CODE'), 'untrusted code') + @skipIfMissingImports("numpy", "cv2") + @skipUntrustedTest() def test_framebuffer_tux_logo(self): """ Boot Linux and verify the Tux logo is displayed on the framebuffer. """ - screendump_path = os.path.join(self.workdir, "screendump.pbm") + import numpy as np + import cv2 + + screendump_path = self.scratch_file("screendump.pbm") tuxlogo_path = self.ASSET_TUXLOGO.fetch() self.boot_integratorcp() framebuffer_ready = 'Console: switching to colour frame buffer device' wait_for_console_pattern(self, framebuffer_ready) self.vm.cmd('human-monitor-command', command_line='stop') - self.vm.cmd('human-monitor-command', - command_line='screendump %s' % screendump_path) + res = self.vm.cmd('human-monitor-command', + command_line='screendump %s' % screendump_path) + if 'unknown command' in res: + self.skipTest('screendump not available') logger = logging.getLogger('framebuffer') cpu_count = 1 diff --git a/tests/functional/test_arm_microbit.py b/tests/functional/test_arm_microbit.py new file mode 100755 index 0000000..68ea4e7 --- /dev/null +++ b/tests/functional/test_arm_microbit.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python3 +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Copyright 2025, The QEMU Project Developers. +# +# A functional test that runs MicroPython on the arm microbit machine. + +from qemu_test import QemuSystemTest, Asset, exec_command_and_wait_for_pattern +from qemu_test import wait_for_console_pattern + + +class MicrobitMachine(QemuSystemTest): + + ASSET_MICRO = Asset('https://ozlabs.org/~joel/microbit-micropython.hex', + '021641f93dfb11767d4978dbb3ca7f475d1b13c69e7f4aec3382f212636bffd6') + + def test_arm_microbit(self): + self.set_machine('microbit') + + micropython = self.ASSET_MICRO.fetch() + self.vm.set_console() + self.vm.add_args('-device', f'loader,file={micropython}') + self.vm.launch() + wait_for_console_pattern(self, 'Type "help()" for more information.') + exec_command_and_wait_for_pattern(self, 'import machine as mch', '>>>') + exec_command_and_wait_for_pattern(self, 'mch.reset()', 'MicroPython') + wait_for_console_pattern(self, '>>>') + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/functional/test_arm_orangepi.py b/tests/functional/test_arm_orangepi.py new file mode 100755 index 0000000..f9bfa8c --- /dev/null +++ b/tests/functional/test_arm_orangepi.py @@ -0,0 +1,237 @@ +#!/usr/bin/env python3 +# +# Functional test that boots a Linux kernel on an Orange Pi machine +# and checks the console +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import os +import shutil + +from qemu_test import LinuxKernelTest, exec_command_and_wait_for_pattern +from qemu_test import Asset, interrupt_interactive_console_until_pattern +from qemu_test import wait_for_console_pattern, skipBigDataTest +from qemu_test.utils import image_pow2ceil_expand + + +class OrangePiMachine(LinuxKernelTest): + + ASSET_DEB = Asset( + ('https://apt.armbian.com/pool/main/l/linux-6.6.16/' + 'linux-image-current-sunxi_24.2.1_armhf__6.6.16-Seb3e-D6b4a-P2359-Ce96bHfe66-HK01ba-V014b-B067e-R448a.deb'), + '3d968c15b121ede871dce49d13ee7644d6f74b6b121b84c9a40f51b0c80d6d22') + + ASSET_INITRD = Asset( + ('https://github.com/groeck/linux-build-test/raw/' + '2eb0a73b5d5a28df3170c546ddaaa9757e1e0848/rootfs/' + 'arm/rootfs-armv7a.cpio.gz'), + '2c8dbdb16ea7af2dfbcbea96044dde639fb07d09fd3c4fb31f2027ef71e55ddd') + + ASSET_ROOTFS = Asset( + ('http://storage.kernelci.org/images/rootfs/buildroot/' + 'buildroot-baseline/20230703.0/armel/rootfs.ext2.xz'), + '42b44a12965ac0afe9a88378527fb698a7dc76af50495efc2361ee1595b4e5c6') + + ASSET_ARMBIAN = Asset( + ('https://k-space.ee.armbian.com/archive/orangepipc/archive/' + 'Armbian_23.8.1_Orangepipc_jammy_current_6.1.47.img.xz'), + 'b386dff6552513b5f164ea00f94814a6b0f1da9fb90b83725e949cf797e11afb') + + ASSET_UBOOT = Asset( + ('http://snapshot.debian.org/archive/debian/20200108T145233Z/pool/' + 'main/u/u-boot/u-boot-sunxi_2020.01%2Bdfsg-1_armhf.deb'), + '9223d94dc283ab54df41ce9d6f69025a5b47fece29fb67a714e23aa0cdf3bdfa') + + ASSET_NETBSD = Asset( + ('https://archive.netbsd.org/pub/NetBSD-archive/NetBSD-9.0/' + 'evbarm-earmv7hf/binary/gzimg/armv7.img.gz'), + '20d3e07dc057e15c12452620e90ecab2047f0f7940d9cba8182ebc795927177f') + + def test_arm_orangepi(self): + self.set_machine('orangepi-pc') + kernel_path = self.archive_extract( + self.ASSET_DEB, member='boot/vmlinuz-6.6.16-current-sunxi') + dtb_path = ('usr/lib/linux-image-6.6.16-current-sunxi/' + + 'sun8i-h3-orangepi-pc.dtb') + dtb_path = self.archive_extract(self.ASSET_DEB, member=dtb_path) + + self.vm.set_console() + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'console=ttyS0,115200n8 ' + 'earlycon=uart,mmio32,0x1c28000') + self.vm.add_args('-kernel', kernel_path, + '-dtb', dtb_path, + '-append', kernel_command_line) + self.vm.launch() + console_pattern = 'Kernel command line: %s' % kernel_command_line + self.wait_for_console_pattern(console_pattern) + os.remove(kernel_path) + os.remove(dtb_path) + + def test_arm_orangepi_initrd(self): + self.set_machine('orangepi-pc') + kernel_path = self.archive_extract( + self.ASSET_DEB, member='boot/vmlinuz-6.6.16-current-sunxi') + dtb_path = ('usr/lib/linux-image-6.6.16-current-sunxi/' + + 'sun8i-h3-orangepi-pc.dtb') + dtb_path = self.archive_extract(self.ASSET_DEB, member=dtb_path) + initrd_path = self.uncompress(self.ASSET_INITRD) + + self.vm.set_console() + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'console=ttyS0,115200 ' + 'panic=-1 noreboot') + self.vm.add_args('-kernel', kernel_path, + '-dtb', dtb_path, + '-initrd', initrd_path, + '-append', kernel_command_line, + '-no-reboot') + self.vm.launch() + self.wait_for_console_pattern('Boot successful.') + + exec_command_and_wait_for_pattern(self, 'cat /proc/cpuinfo', + 'Allwinner sun8i Family') + exec_command_and_wait_for_pattern(self, 'cat /proc/iomem', + 'system-control@1c00000') + exec_command_and_wait_for_pattern(self, 'reboot', + 'reboot: Restarting system') + # Wait for VM to shut down gracefully + self.vm.wait() + os.remove(kernel_path) + os.remove(dtb_path) + os.remove(initrd_path) + + def test_arm_orangepi_sd(self): + self.set_machine('orangepi-pc') + self.require_netdev('user') + kernel_path = self.archive_extract( + self.ASSET_DEB, member='boot/vmlinuz-6.6.16-current-sunxi') + dtb_path = ('usr/lib/linux-image-6.6.16-current-sunxi/' + + 'sun8i-h3-orangepi-pc.dtb') + dtb_path = self.archive_extract(self.ASSET_DEB, member=dtb_path) + rootfs_path = self.uncompress(self.ASSET_ROOTFS) + image_pow2ceil_expand(rootfs_path) + + self.vm.set_console() + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'console=ttyS0,115200 ' + 'root=/dev/mmcblk0 rootwait rw ' + 'panic=-1 noreboot') + self.vm.add_args('-kernel', kernel_path, + '-dtb', dtb_path, + '-drive', 'file=' + rootfs_path + ',if=sd,format=raw', + '-append', kernel_command_line, + '-no-reboot') + self.vm.launch() + shell_ready = "/bin/sh: can't access tty; job control turned off" + self.wait_for_console_pattern(shell_ready) + + exec_command_and_wait_for_pattern(self, 'cat /proc/cpuinfo', + 'Allwinner sun8i Family') + exec_command_and_wait_for_pattern(self, 'cat /proc/partitions', + 'mmcblk0') + exec_command_and_wait_for_pattern(self, 'ifconfig eth0 up', + 'eth0: Link is Up') + exec_command_and_wait_for_pattern(self, 'udhcpc eth0', + 'udhcpc: lease of 10.0.2.15 obtained') + exec_command_and_wait_for_pattern(self, 'ping -c 3 10.0.2.2', + '3 packets transmitted, 3 packets received, 0% packet loss') + exec_command_and_wait_for_pattern(self, 'reboot', + 'reboot: Restarting system') + # Wait for VM to shut down gracefully + self.vm.wait() + os.remove(kernel_path) + os.remove(dtb_path) + os.remove(rootfs_path) + + @skipBigDataTest() + def test_arm_orangepi_armbian(self): + self.set_machine('orangepi-pc') + self.require_netdev('user') + + # This test download a 275 MiB compressed image and expand it + # to 1036 MiB, but the underlying filesystem is 1552 MiB... + # As we expand it to 2 GiB we are safe. + image_path = self.uncompress(self.ASSET_ARMBIAN) + image_pow2ceil_expand(image_path) + + self.vm.set_console() + self.vm.add_args('-drive', 'file=' + image_path + ',if=sd,format=raw', + '-nic', 'user', + '-no-reboot') + self.vm.launch() + + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'console=ttyS0,115200 ' + 'loglevel=7 ' + 'nosmp ' + 'systemd.default_timeout_start_sec=9000 ' + 'systemd.mask=armbian-zram-config.service ' + 'systemd.mask=armbian-ramlog.service') + + self.wait_for_console_pattern('U-Boot SPL') + self.wait_for_console_pattern('Autoboot in ') + exec_command_and_wait_for_pattern(self, ' ', '=>') + exec_command_and_wait_for_pattern(self, "setenv extraargs '" + + kernel_command_line + "'", '=>') + exec_command_and_wait_for_pattern(self, 'boot', 'Starting kernel ...') + + self.wait_for_console_pattern('systemd[1]: Hostname set ' + + 'to <orangepipc>') + self.wait_for_console_pattern('Starting Load Kernel Modules...') + + @skipBigDataTest() + def test_arm_orangepi_uboot_netbsd9(self): + self.set_machine('orangepi-pc') + self.require_netdev('user') + + # This test download a 304MB compressed image and expand it to 2GB + # We use the common OrangePi PC 'plus' build of U-Boot for our secondary + # program loader (SPL). We will then set the path to the more specific + # OrangePi "PC" device tree blob with 'setenv fdtfile' in U-Boot prompt, + # before to boot NetBSD. + uboot_path = 'usr/lib/u-boot/orangepi_plus/u-boot-sunxi-with-spl.bin' + uboot_path = self.archive_extract(self.ASSET_UBOOT, member=uboot_path) + image_path = self.uncompress(self.ASSET_NETBSD) + image_pow2ceil_expand(image_path) + image_drive_args = 'if=sd,format=raw,snapshot=on,file=' + image_path + + # dd if=u-boot-sunxi-with-spl.bin of=armv7.img bs=1K seek=8 conv=notrunc + with open(uboot_path, 'rb') as f_in: + with open(image_path, 'r+b') as f_out: + f_out.seek(8 * 1024) + shutil.copyfileobj(f_in, f_out) + + self.vm.set_console() + self.vm.add_args('-nic', 'user', + '-drive', image_drive_args, + '-global', 'allwinner-rtc.base-year=2000', + '-no-reboot') + self.vm.launch() + wait_for_console_pattern(self, 'U-Boot 2020.01+dfsg-1') + interrupt_interactive_console_until_pattern(self, + 'Hit any key to stop autoboot:', + 'switch to partitions #0, OK') + + exec_command_and_wait_for_pattern(self, '', '=>') + cmd = 'setenv bootargs root=ld0a' + exec_command_and_wait_for_pattern(self, cmd, '=>') + cmd = 'setenv kernel netbsd-GENERIC.ub' + exec_command_and_wait_for_pattern(self, cmd, '=>') + cmd = 'setenv fdtfile dtb/sun8i-h3-orangepi-pc.dtb' + exec_command_and_wait_for_pattern(self, cmd, '=>') + cmd = ("setenv bootcmd 'fatload mmc 0:1 ${kernel_addr_r} ${kernel}; " + "fatload mmc 0:1 ${fdt_addr_r} ${fdtfile}; " + "fdt addr ${fdt_addr_r}; " + "bootm ${kernel_addr_r} - ${fdt_addr_r}'") + exec_command_and_wait_for_pattern(self, cmd, '=>') + + exec_command_and_wait_for_pattern(self, 'boot', + 'Booting kernel from Legacy Image') + wait_for_console_pattern(self, 'Starting kernel ...') + wait_for_console_pattern(self, 'NetBSD 9.0 (GENERIC)') + # Wait for user-space + wait_for_console_pattern(self, 'Starting root file system check') + +if __name__ == '__main__': + LinuxKernelTest.main() diff --git a/tests/functional/test_arm_quanta_gsj.py b/tests/functional/test_arm_quanta_gsj.py new file mode 100755 index 0000000..cb0545f --- /dev/null +++ b/tests/functional/test_arm_quanta_gsj.py @@ -0,0 +1,92 @@ +#!/usr/bin/env python3 +# +# Functional test that boots a Linux kernel and checks the console +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import LinuxKernelTest, Asset, exec_command_and_wait_for_pattern +from qemu_test import interrupt_interactive_console_until_pattern, skipSlowTest + + +class EmcraftSf2Machine(LinuxKernelTest): + + ASSET_IMAGE = Asset( + ('https://github.com/hskinnemoen/openbmc/releases/download/' + '20200711-gsj-qemu-0/obmc-phosphor-image-gsj.static.mtd.gz'), + 'eccd4e375cde53034c84aece5c511932cacf838d9fd3f63da368a511757da72b') + + ASSET_INITRD = Asset( + ('https://github.com/hskinnemoen/openbmc/releases/download/' + '20200711-gsj-qemu-0/obmc-phosphor-initramfs-gsj.cpio.xz'), + '37b05009fc54db1434beac12bd7ff99a2e751a2f032ee18d9042f991dd0cdeaa') + + ASSET_KERNEL = Asset( + ('https://github.com/hskinnemoen/openbmc/releases/download/' + '20200711-gsj-qemu-0/uImage-gsj.bin'), + 'ce6d6b37bff46c74fc7b1e90da10a431cc37a62cdb35ec199fa73473d0790110') + + ASSET_DTB = Asset( + ('https://github.com/hskinnemoen/openbmc/releases/download/' + '20200711-gsj-qemu-0/nuvoton-npcm730-gsj.dtb'), + '3249b2da787d4b9ad4e61f315b160abfceb87b5e1895a7ce898ce7f40c8d4045') + + @skipSlowTest() + def test_arm_quanta_gsj(self): + self.set_machine('quanta-gsj') + image_path = self.uncompress(self.ASSET_IMAGE, format='gz') + + self.vm.set_console() + drive_args = 'file=' + image_path + ',if=mtd,bus=0,unit=0' + self.vm.add_args('-drive', drive_args) + self.vm.launch() + + # Disable drivers and services that stall for a long time during boot, + # to avoid running past the 90-second timeout. These may be removed + # as the corresponding device support is added. + kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + ( + 'console=${console} ' + 'mem=${mem} ' + 'initcall_blacklist=npcm_i2c_bus_driver_init ' + 'systemd.mask=systemd-random-seed.service ' + 'systemd.mask=dropbearkey.service ' + ) + + self.wait_for_console_pattern('> BootBlock by Nuvoton') + self.wait_for_console_pattern('>Device: Poleg BMC NPCM730') + self.wait_for_console_pattern('>Skip DDR init.') + self.wait_for_console_pattern('U-Boot ') + interrupt_interactive_console_until_pattern( + self, 'Hit any key to stop autoboot:', 'U-Boot>') + exec_command_and_wait_for_pattern( + self, "setenv bootargs ${bootargs} " + kernel_command_line, + 'U-Boot>') + exec_command_and_wait_for_pattern( + self, 'run romboot', 'Booting Kernel from flash') + self.wait_for_console_pattern('Booting Linux on physical CPU 0x0') + self.wait_for_console_pattern('CPU1: thread -1, cpu 1, socket 0') + self.wait_for_console_pattern('OpenBMC Project Reference Distro') + self.wait_for_console_pattern('gsj login:') + + def test_arm_quanta_gsj_initrd(self): + self.set_machine('quanta-gsj') + initrd_path = self.ASSET_INITRD.fetch() + kernel_path = self.ASSET_KERNEL.fetch() + dtb_path = self.ASSET_DTB.fetch() + + self.vm.set_console() + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'console=ttyS0,115200n8 ' + 'earlycon=uart8250,mmio32,0xf0001000') + self.vm.add_args('-kernel', kernel_path, + '-initrd', initrd_path, + '-dtb', dtb_path, + '-append', kernel_command_line) + self.vm.launch() + + self.wait_for_console_pattern('Booting Linux on physical CPU 0x0') + self.wait_for_console_pattern('CPU1: thread -1, cpu 1, socket 0') + self.wait_for_console_pattern( + 'Give root password for system maintenance') + +if __name__ == '__main__': + LinuxKernelTest.main() diff --git a/tests/functional/test_arm_raspi2.py b/tests/functional/test_arm_raspi2.py new file mode 100755 index 0000000..d3c7aaa --- /dev/null +++ b/tests/functional/test_arm_raspi2.py @@ -0,0 +1,92 @@ +#!/usr/bin/env python3 +# +# Functional test that boots a Linux kernel on a Raspberry Pi machine +# and checks the console +# +# Copyright (c) 2019 Philippe Mathieu-Daudé <f4bug@amsat.org> +# +# 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 ArmRaspi2Machine(LinuxKernelTest): + + ASSET_KERNEL_20190215 = Asset( + ('http://archive.raspberrypi.org/debian/' + 'pool/main/r/raspberrypi-firmware/' + 'raspberrypi-kernel_1.20190215-1_armhf.deb'), + '9f1759f7228113da24f5ee2aa6312946ec09a83e076aba9406c46ff776dfb291') + + ASSET_INITRD = Asset( + ('https://github.com/groeck/linux-build-test/raw/' + '2eb0a73b5d5a28df3170c546ddaaa9757e1e0848/rootfs/' + 'arm/rootfs-armv7a.cpio.gz'), + '2c8dbdb16ea7af2dfbcbea96044dde639fb07d09fd3c4fb31f2027ef71e55ddd') + + def do_test_arm_raspi2(self, uart_id): + """ + The kernel can be rebuilt using the kernel source referenced + and following the instructions on the on: + https://www.raspberrypi.org/documentation/linux/kernel/building.md + """ + serial_kernel_cmdline = { + 0: 'earlycon=pl011,0x3f201000 console=ttyAMA0', + } + kernel_path = self.archive_extract(self.ASSET_KERNEL_20190215, + member='boot/kernel7.img') + dtb_path = self.archive_extract(self.ASSET_KERNEL_20190215, + member='boot/bcm2709-rpi-2-b.dtb') + + self.set_machine('raspi2b') + self.vm.set_console() + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + serial_kernel_cmdline[uart_id] + + ' root=/dev/mmcblk0p2 rootwait ' + + 'dwc_otg.fiq_fsm_enable=0') + self.vm.add_args('-kernel', kernel_path, + '-dtb', dtb_path, + '-append', kernel_command_line, + '-device', 'usb-kbd') + self.vm.launch() + + console_pattern = 'Kernel command line: %s' % kernel_command_line + self.wait_for_console_pattern(console_pattern) + self.wait_for_console_pattern('Product: QEMU USB Keyboard') + + def test_arm_raspi2_uart0(self): + self.do_test_arm_raspi2(0) + + def test_arm_raspi2_initrd(self): + kernel_path = self.archive_extract(self.ASSET_KERNEL_20190215, + member='boot/kernel7.img') + dtb_path = self.archive_extract(self.ASSET_KERNEL_20190215, + member='boot/bcm2709-rpi-2-b.dtb') + initrd_path = self.uncompress(self.ASSET_INITRD) + + self.set_machine('raspi2b') + self.vm.set_console() + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'earlycon=pl011,0x3f201000 console=ttyAMA0 ' + 'panic=-1 noreboot ' + + 'dwc_otg.fiq_fsm_enable=0') + self.vm.add_args('-kernel', kernel_path, + '-dtb', dtb_path, + '-initrd', initrd_path, + '-append', kernel_command_line, + '-no-reboot') + self.vm.launch() + self.wait_for_console_pattern('Boot successful.') + + exec_command_and_wait_for_pattern(self, 'cat /proc/cpuinfo', + 'BCM2835') + exec_command_and_wait_for_pattern(self, 'cat /proc/iomem', + '/soc/cprman@7e101000') + exec_command_and_wait_for_pattern(self, 'halt', 'reboot: System halted') + # Wait for VM to shut down gracefully + self.vm.wait() + + +if __name__ == '__main__': + LinuxKernelTest.main() diff --git a/tests/functional/test_arm_realview.py b/tests/functional/test_arm_realview.py new file mode 100755 index 0000000..82cc964 --- /dev/null +++ b/tests/functional/test_arm_realview.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python3 +# +# Functional test that boots a Linux kernel on a realview arm machine +# and checks the console +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import LinuxKernelTest, exec_command_and_wait_for_pattern +from qemu_test import Asset + + +class RealviewMachine(LinuxKernelTest): + + ASSET_REALVIEW_MPCORE = Asset( + ('https://archive.openwrt.org/chaos_calmer/15.05.1/realview/generic/' + 'openwrt-15.05.1-realview-vmlinux-initramfs.elf'), + 'd3a01037f33e7512d46d50975588d5c3a0e0cbf25f37afab44775c2a2be523e6') + + def test_realview_ep_mpcore(self): + self.require_netdev('user') + self.set_machine('realview-eb-mpcore') + kernel_path = self.ASSET_REALVIEW_MPCORE.fetch() + self.vm.set_console() + kernel_param = 'console=ttyAMA0 mem=128M quiet' + self.vm.add_args('-kernel', kernel_path, + '-append', kernel_param) + self.vm.launch() + self.wait_for_console_pattern('Please press Enter to activate') + prompt = ':/#' + exec_command_and_wait_for_pattern(self, '', prompt) + exec_command_and_wait_for_pattern(self, 'dmesg', kernel_param) + self.wait_for_console_pattern(prompt) + exec_command_and_wait_for_pattern(self, + ('while ! dmesg | grep "br-lan: port 1(eth0) entered" ;' + ' do sleep 1 ; done'), + 'entered forwarding state') + self.wait_for_console_pattern(prompt) + exec_command_and_wait_for_pattern(self, + 'while ! ifconfig | grep "10.0.2.15" ; do sleep 1 ; done', + 'addr:10.0.2.15') + self.wait_for_console_pattern(prompt) + exec_command_and_wait_for_pattern(self, 'ping -c 1 10.0.2.2', + '1 packets received, 0% packet loss') + + +if __name__ == '__main__': + LinuxKernelTest.main() diff --git a/tests/functional/test_arm_replay.py b/tests/functional/test_arm_replay.py new file mode 100755 index 0000000..e002e6a --- /dev/null +++ b/tests/functional/test_arm_replay.py @@ -0,0 +1,69 @@ +#!/usr/bin/env python3 +# +# Replay test that boots a Linux kernel on arm machines and checks the console +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import Asset +from replay_kernel import ReplayKernelBase + + +class ArmReplay(ReplayKernelBase): + + ASSET_VIRT = Asset( + ('https://archives.fedoraproject.org/pub/archive/fedora/linux/' + 'releases/29/Everything/armhfp/os/images/pxeboot/vmlinuz'), + '18dd5f1a9a28bd539f9d047f7c0677211bae528e8712b40ca5a229a4ad8e2591') + + def test_virt(self): + self.set_machine('virt') + kernel_path = self.ASSET_VIRT.fetch() + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'console=ttyAMA0') + console_pattern = 'VFS: Cannot open root device' + self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=1) + + ASSET_CUBIE_KERNEL = Asset( + ('https://apt.armbian.com/pool/main/l/linux-6.6.16/' + 'linux-image-current-sunxi_24.2.1_armhf_' + '_6.6.16-Seb3e-D6b4a-P2359-Ce96bHfe66-HK01ba-V014b-B067e-R448a.deb'), + '3d968c15b121ede871dce49d13ee7644d6f74b6b121b84c9a40f51b0c80d6d22') + + ASSET_CUBIE_INITRD = Asset( + ('https://github.com/groeck/linux-build-test/raw/' + '2eb0a73b5d5a28df3170c546ddaaa9757e1e0848/rootfs/arm/rootfs-armv5.cpio.gz'), + '334b8d256db67a3f2b3ad070aa08b5ade39624e0e7e35b02f4359a577bc8f39b') + + def test_cubieboard(self): + self.set_machine('cubieboard') + kernel_path = self.archive_extract(self.ASSET_CUBIE_KERNEL, + member='boot/vmlinuz-6.6.16-current-sunxi') + dtb_path = self.archive_extract(self.ASSET_CUBIE_KERNEL, + member='usr/lib/linux-image-6.6.16-current-sunxi/sun4i-a10-cubieboard.dtb') + initrd_path = self.uncompress(self.ASSET_CUBIE_INITRD) + + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'console=ttyS0,115200 ' + 'usbcore.nousb ' + 'panic=-1 noreboot') + console_pattern = 'Boot successful.' + self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=1, + args=('-dtb', dtb_path, + '-initrd', initrd_path, + '-no-reboot')) + + ASSET_DAY16 = Asset( + 'https://qemu-advcal.gitlab.io/qac-best-of-multiarch/download/day16.tar.xz', + '63311adb2d4c4e7a73214a86d29988add87266a909719c56acfadd026b4110a7') + + def test_vexpressa9(self): + self.set_machine('vexpress-a9') + self.archive_extract(self.ASSET_DAY16) + kernel_path = self.scratch_file('day16', 'winter.zImage') + dtb_path = self.scratch_file('day16', 'vexpress-v2p-ca9.dtb') + self.run_rr(kernel_path, self.REPLAY_KERNEL_COMMAND_LINE, + 'QEMU advent calendar', args=('-dtb', dtb_path)) + + +if __name__ == '__main__': + ReplayKernelBase.main() diff --git a/tests/functional/test_arm_smdkc210.py b/tests/functional/test_arm_smdkc210.py new file mode 100755 index 0000000..3154e7f --- /dev/null +++ b/tests/functional/test_arm_smdkc210.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python3 +# +# Functional test that boots a Linux kernel and checks the console +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import LinuxKernelTest, Asset + + +class Smdkc210Machine(LinuxKernelTest): + + ASSET_DEB = Asset( + ('https://snapshot.debian.org/archive/debian/20190928T224601Z/pool/' + 'main/l/linux/linux-image-4.19.0-6-armmp_4.19.67-2+deb10u1_armhf.deb'), + '421804e7579ef40d554c962850dbdf1bfc79f7fa7faec9d391397170dc806c3e') + + ASSET_ROOTFS = Asset( + ('https://github.com/groeck/linux-build-test/raw/' + '2eb0a73b5d5a28df3170c546ddaaa9757e1e0848/rootfs/arm/' + 'rootfs-armv5.cpio.gz'), + '334b8d256db67a3f2b3ad070aa08b5ade39624e0e7e35b02f4359a577bc8f39b') + + def test_arm_exynos4210_initrd(self): + self.set_machine('smdkc210') + + kernel_path = self.archive_extract(self.ASSET_DEB, + member='boot/vmlinuz-4.19.0-6-armmp') + dtb_path = 'usr/lib/linux-image-4.19.0-6-armmp/exynos4210-smdkv310.dtb' + dtb_path = self.archive_extract(self.ASSET_DEB, member=dtb_path) + + initrd_path = self.uncompress(self.ASSET_ROOTFS) + + self.vm.set_console() + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'earlycon=exynos4210,0x13800000 earlyprintk ' + + 'console=ttySAC0,115200n8 ' + + 'random.trust_cpu=off cryptomgr.notests ' + + 'cpuidle.off=1 panic=-1 noreboot') + + self.vm.add_args('-kernel', kernel_path, + '-dtb', dtb_path, + '-initrd', initrd_path, + '-append', kernel_command_line, + '-no-reboot') + self.vm.launch() + + self.wait_for_console_pattern('Boot successful.') + # TODO user command, for now the uart is stuck + +if __name__ == '__main__': + LinuxKernelTest.main() diff --git a/tests/functional/test_arm_stellaris.py b/tests/functional/test_arm_stellaris.py new file mode 100755 index 0000000..cbd21cb --- /dev/null +++ b/tests/functional/test_arm_stellaris.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python3 +# +# Functional test that checks the serial console of the stellaris machines +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import QemuSystemTest, Asset, exec_command_and_wait_for_pattern +from qemu_test import wait_for_console_pattern + + +class StellarisMachine(QemuSystemTest): + + ASSET_DAY22 = Asset( + 'https://www.qemu-advent-calendar.org/2023/download/day22.tar.gz', + 'ae3a63ef4b7a22c21bfc7fc0d85e402fe95e223308ed23ac854405016431ff51') + + def test_lm3s6965evb(self): + self.set_machine('lm3s6965evb') + kernel_path = self.archive_extract(self.ASSET_DAY22, + member='day22/day22.bin') + self.vm.set_console() + self.vm.add_args('-kernel', kernel_path) + self.vm.launch() + + wait_for_console_pattern(self, 'In a one horse open') + + ASSET_NOTMAIN = Asset( + 'https://github.com/Ahelion/QemuArmM4FDemoSw/raw/master/build/notmain.bin', + '6ceda031aa081a420fca2fca9e137fa681d6e3820d820ad1917736cb265e611a') + + def test_lm3s811evb(self): + self.set_machine('lm3s811evb') + kernel_path = self.ASSET_NOTMAIN.fetch() + + self.vm.set_console() + self.vm.add_args('-cpu', 'cortex-m4') + self.vm.add_args('-kernel', kernel_path) + self.vm.launch() + + # The test kernel emits an initial '!' and then waits for input. + # For each character that we send it responds with a certain + # other ASCII character. + wait_for_console_pattern(self, '!') + exec_command_and_wait_for_pattern(self, '789', 'cdf') + + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/functional/test_arm_sx1.py b/tests/functional/test_arm_sx1.py new file mode 100755 index 0000000..25800b3 --- /dev/null +++ b/tests/functional/test_arm_sx1.py @@ -0,0 +1,73 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2024 Linaro Ltd. +# +# Functional test that boots a Linux kernel on an sx1 machine +# and checks the console. We have three variants: +# * just boot initrd +# * boot with filesystem on SD card +# * boot from flash +# In all cases these images have a userspace that is configured +# to immediately reboot the system on successful boot, so we +# only need to wait for QEMU to exit (via -no-reboot). +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import LinuxKernelTest, Asset + + +class SX1Test(LinuxKernelTest): + + ASSET_ZIMAGE = Asset( + 'https://github.com/groeck/linux-test-downloads/raw/225223f2ad7d637b34426810bf6c3b727b76a718/sx1/zImage', + 'a0271899a8dc2165f9e0adb2d0a57fc839ae3a469722ffc56c77e108a8887615') + + ASSET_INITRD = Asset( + 'https://github.com/groeck/linux-test-downloads/raw/225223f2ad7d637b34426810bf6c3b727b76a718/sx1/rootfs-armv4.cpio', + '35b0721249821aa544cd85b85d3cb8901db4c6d128eed86ab261e5d9e37d58f8') + + ASSET_SD_FS = Asset( + 'https://github.com/groeck/linux-test-downloads/raw/225223f2ad7d637b34426810bf6c3b727b76a718/sx1/rootfs-armv4.ext2', + 'c1db7f43ef92469ebc8605013728c8950e7608439f01d13678994f0ce101c3a8') + + ASSET_FLASH = Asset( + 'https://github.com/groeck/linux-test-downloads/raw/225223f2ad7d637b34426810bf6c3b727b76a718/sx1/flash', + '17e6a2758fa38efd2666be0879d4751fd37d194f25168a8deede420df519b676') + + CONSOLE_ARGS = 'console=ttyS0,115200 earlycon=uart8250,mmio32,0xfffb0000,115200n8' + + def test_arm_sx1_initrd(self): + self.set_machine('sx1') + zimage_path = self.ASSET_ZIMAGE.fetch() + initrd_path = self.ASSET_INITRD.fetch() + self.vm.add_args('-append', f'kunit.enable=0 rdinit=/sbin/init {self.CONSOLE_ARGS}') + self.vm.add_args('-no-reboot') + self.launch_kernel(zimage_path, + initrd=initrd_path, + wait_for='Boot successful') + self.vm.wait(timeout=120) + + def test_arm_sx1_sd(self): + self.set_machine('sx1') + zimage_path = self.ASSET_ZIMAGE.fetch() + sd_fs_path = self.ASSET_SD_FS.fetch() + self.vm.add_args('-append', f'kunit.enable=0 root=/dev/mmcblk0 rootwait {self.CONSOLE_ARGS}') + self.vm.add_args('-no-reboot') + self.vm.add_args('-snapshot') + self.vm.add_args('-drive', f'format=raw,if=sd,file={sd_fs_path}') + self.launch_kernel(zimage_path, wait_for='Boot successful') + self.vm.wait(timeout=120) + + def test_arm_sx1_flash(self): + self.set_machine('sx1') + zimage_path = self.ASSET_ZIMAGE.fetch() + flash_path = self.ASSET_FLASH.fetch() + self.vm.add_args('-append', f'kunit.enable=0 root=/dev/mtdblock3 rootwait {self.CONSOLE_ARGS}') + self.vm.add_args('-no-reboot') + self.vm.add_args('-snapshot') + self.vm.add_args('-drive', f'format=raw,if=pflash,file={flash_path}') + self.launch_kernel(zimage_path, wait_for='Boot successful') + self.vm.wait(timeout=120) + +if __name__ == '__main__': + LinuxKernelTest.main() diff --git a/tests/functional/test_arm_tuxrun.py b/tests/functional/test_arm_tuxrun.py new file mode 100755 index 0000000..4ac85f4 --- /dev/null +++ b/tests/functional/test_arm_tuxrun.py @@ -0,0 +1,70 @@ +#!/usr/bin/env python3 +# +# Functional test that boots known good tuxboot images the same way +# that tuxrun (www.tuxrun.org) does. This tool is used by things like +# the LKFT project to run regression tests on kernels. +# +# Copyright (c) 2023 Linaro Ltd. +# +# Author: +# Alex Bennée <alex.bennee@linaro.org> +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import Asset +from qemu_test.tuxruntest import TuxRunBaselineTest + +class TuxRunArmTest(TuxRunBaselineTest): + + ASSET_ARMV5_KERNEL = Asset( + 'https://storage.tuxboot.com/buildroot/20241119/armv5/zImage', + '3931a3908dbcf0ec0fe292d035ffc4dfed95f797dedd4a59ccfcf7a46e6f92d4') + ASSET_ARMV5_ROOTFS = Asset( + 'https://storage.tuxboot.com/buildroot/20241119/armv5/rootfs.ext4.zst', + '60ff78b68c7021df378e4fc2d66d3b016484d1acc7e07fb8920c1d8e30f4571f') + ASSET_ARMV5_DTB = Asset( + 'https://storage.tuxboot.com/buildroot/20241119/armv5/versatile-pb.dtb', + '50988e69ef3f3b08bfb9146e8fe414129990029e8dfbed444953b7e14809530a') + + def test_armv5(self): + self.set_machine('versatilepb') + self.cpu='arm926' + self.console='ttyAMA0' + self.wait_for_shutdown=False + self.common_tuxrun(kernel_asset=self.ASSET_ARMV5_KERNEL, + rootfs_asset=self.ASSET_ARMV5_ROOTFS, + dtb_asset=self.ASSET_ARMV5_DTB, + drive="virtio-blk-pci") + + ASSET_ARMV7_KERNEL = Asset( + 'https://storage.tuxboot.com/buildroot/20241119/armv7/zImage', + '1377bc3d90de5ce57ab17cd67429fe8b15c2e9964248c775c682b67e6299b991') + ASSET_ARMV7_ROOTFS = Asset( + 'https://storage.tuxboot.com/buildroot/20241119/armv7/rootfs.ext4.zst', + 'ed2cbc69bd6b3fbd5cafb5ee961393c7cfbe726446f14301c67d6b1f28bfdb51') + + def test_armv7(self): + self.set_machine('virt') + self.cpu='cortex-a15' + self.console='ttyAMA0' + self.wait_for_shutdown=False + self.common_tuxrun(kernel_asset=self.ASSET_ARMV7_KERNEL, + rootfs_asset=self.ASSET_ARMV7_ROOTFS) + + ASSET_ARMV7BE_KERNEL = Asset( + 'https://storage.tuxboot.com/buildroot/20241119/armv7be/zImage', + 'a244e6da99f1bbd254827ec7681bd4aac9eb1aa05aaebc6b15e5d289ebb683f3') + ASSET_ARMV7BE_ROOTFS = Asset( + 'https://storage.tuxboot.com/buildroot/20241119/armv7be/rootfs.ext4.zst', + 'd4f9c57860a512163f30ecc69b2174d1a1bdeb853a43dc49a09cfcfe84e428ea') + + def test_armv7be(self): + self.set_machine('virt') + self.cpu='cortex-a15' + self.console='ttyAMA0' + self.wait_for_shutdown=False + self.common_tuxrun(kernel_asset=self.ASSET_ARMV7BE_KERNEL, + rootfs_asset=self.ASSET_ARMV7BE_ROOTFS) + +if __name__ == '__main__': + TuxRunBaselineTest.main() diff --git a/tests/functional/test_arm_vexpress.py b/tests/functional/test_arm_vexpress.py new file mode 100755 index 0000000..6b11552 --- /dev/null +++ b/tests/functional/test_arm_vexpress.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python3 +# +# Functional test that boots a Linux kernel on an versatile express machine +# and checks the console +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import LinuxKernelTest, Asset + + +class VExpressTest(LinuxKernelTest): + + ASSET_DAY16 = Asset( + 'https://qemu-advcal.gitlab.io/qac-best-of-multiarch/download/day16.tar.xz', + '63311adb2d4c4e7a73214a86d29988add87266a909719c56acfadd026b4110a7') + + def test_arm_vexpressa9(self): + self.set_machine('vexpress-a9') + self.archive_extract(self.ASSET_DAY16) + self.launch_kernel(self.scratch_file('day16', 'winter.zImage'), + dtb=self.scratch_file('day16', + 'vexpress-v2p-ca9.dtb'), + wait_for='QEMU advent calendar') + +if __name__ == '__main__': + LinuxKernelTest.main() diff --git a/tests/functional/test_arm_virt.py b/tests/functional/test_arm_virt.py new file mode 100755 index 0000000..7b65491 --- /dev/null +++ b/tests/functional/test_arm_virt.py @@ -0,0 +1,30 @@ +#!/usr/bin/env python3 +# +# Functional test that boots a Linux kernel and checks the console +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import LinuxKernelTest, Asset + +class ArmVirtMachine(LinuxKernelTest): + + ASSET_KERNEL = Asset( + ('https://archives.fedoraproject.org/pub/archive/fedora/linux/' + 'releases/29/Everything/armhfp/os/images/pxeboot/vmlinuz'), + '18dd5f1a9a28bd539f9d047f7c0677211bae528e8712b40ca5a229a4ad8e2591') + + def test_arm_virt(self): + self.set_machine('virt') + kernel_path = self.ASSET_KERNEL.fetch() + + self.vm.set_console() + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'console=ttyAMA0') + self.vm.add_args('-kernel', kernel_path, + '-append', kernel_command_line) + self.vm.launch() + console_pattern = 'Kernel command line: %s' % kernel_command_line + self.wait_for_console_pattern(console_pattern) + +if __name__ == '__main__': + LinuxKernelTest.main() diff --git a/tests/functional/test_avr_mega2560.py b/tests/functional/test_avr_mega2560.py index 8e47b42..6359b72 100755 --- a/tests/functional/test_avr_mega2560.py +++ b/tests/functional/test_avr_mega2560.py @@ -18,12 +18,10 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. # -import time +from qemu_test import QemuSystemTest, Asset, wait_for_console_pattern -from qemu_test import QemuSystemTest, Asset class AVR6Machine(QemuSystemTest): - timeout = 5 ASSET_ROM = Asset(('https://github.com/seharris/qemu-avr-tests' '/raw/36c3e67b8755dcf/free-rtos/Demo' @@ -40,13 +38,12 @@ class AVR6Machine(QemuSystemTest): self.set_machine('arduino-mega-2560-v3') self.vm.add_args('-bios', rom_path) self.vm.add_args('-nographic') + self.vm.set_console() self.vm.launch() - time.sleep(2) - self.vm.shutdown() + wait_for_console_pattern(self, + 'XABCDEFGHIJKLMNOPQRSTUVWXABCDEFGHIJKLMNOPQRSTUVWXA') - self.assertIn('ABCDEFGHIJKLMNOPQRSTUVWXABCDEFGHIJKLMNOPQRSTUVWX', - self.vm.get_log()) if __name__ == '__main__': QemuSystemTest.main() diff --git a/tests/functional/test_avr_uno.py b/tests/functional/test_avr_uno.py new file mode 100755 index 0000000..adb3b73 --- /dev/null +++ b/tests/functional/test_avr_uno.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python3 +# +# QEMU AVR Arduino UNO functional test +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import QemuSystemTest, Asset, wait_for_console_pattern + + +class UnoMachine(QemuSystemTest): + + ASSET_UNO = Asset( + ('https://github.com/RahulRNandan/LED_Blink_AVR/raw/' + 'c6d602cbb974a193/build/main.elf'), + '3009a4e2cf5c5b65142f538abdf66d4dc6bc6beab7e552fff9ae314583761b72') + + def test_uno(self): + """ + The binary constantly prints out 'LED Blink' + """ + self.set_machine('arduino-uno') + rom_path = self.ASSET_UNO.fetch() + + self.vm.add_args('-bios', rom_path) + self.vm.set_console() + self.vm.launch() + + wait_for_console_pattern(self, 'LED Blink') + + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/functional/test_hppa_seabios.py b/tests/functional/test_hppa_seabios.py new file mode 100755 index 0000000..661b246 --- /dev/null +++ b/tests/functional/test_hppa_seabios.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python3 +# +# SeaBIOS boot test for HPPA machines +# +# Copyright (c) 2024 Linaro, Ltd +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import QemuSystemTest +from qemu_test import wait_for_console_pattern + +class HppaSeabios(QemuSystemTest): + + timeout = 5 + MACH_BITS = {'B160L': 32, 'C3700': 64} + + def boot_seabios(self): + mach = self.machine + bits = self.MACH_BITS[mach] + self.vm.add_args('-no-shutdown') + self.vm.set_console() + self.vm.launch() + wait_for_console_pattern(self, f'SeaBIOS PA-RISC {bits}-bit Firmware') + wait_for_console_pattern(self, f'Emulated machine: HP {mach} ({bits}-bit') + + def test_hppa_32(self): + self.set_machine('B160L') + self.boot_seabios() + + def test_hppa_64(self): + self.set_machine('C3700') + self.boot_seabios() + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/functional/test_i386_replay.py b/tests/functional/test_i386_replay.py new file mode 100755 index 0000000..7c4c260 --- /dev/null +++ b/tests/functional/test_i386_replay.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python3 +# +# Replay test that boots a Linux kernel on a i386 machine +# and checks the console +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import Asset +from replay_kernel import ReplayKernelBase + + +class I386Replay(ReplayKernelBase): + + ASSET_KERNEL = Asset( + 'https://storage.tuxboot.com/20230331/i386/bzImage', + 'a3e5b32a354729e65910f5a1ffcda7c14a6c12a55e8213fb86e277f1b76ed956') + + def test_pc(self): + self.set_machine('pc') + kernel_url = () + kernel_path = self.ASSET_KERNEL.fetch() + kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'console=ttyS0' + console_pattern = 'VFS: Cannot open root device' + self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=5) + + +if __name__ == '__main__': + ReplayKernelBase.main() diff --git a/tests/functional/test_i386_tuxrun.py b/tests/functional/test_i386_tuxrun.py new file mode 100755 index 0000000..f3ccf11 --- /dev/null +++ b/tests/functional/test_i386_tuxrun.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python3 +# +# Functional test that boots known good tuxboot images the same way +# that tuxrun (www.tuxrun.org) does. This tool is used by things like +# the LKFT project to run regression tests on kernels. +# +# Copyright (c) 2023 Linaro Ltd. +# +# Author: +# Alex Bennée <alex.bennee@linaro.org> +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import Asset +from qemu_test.tuxruntest import TuxRunBaselineTest + +class TuxRunI386Test(TuxRunBaselineTest): + + ASSET_I386_KERNEL = Asset( + 'https://storage.tuxboot.com/buildroot/20241119/i386/bzImage', + '47fb44e38e34101eb0f71a2a01742b959d40ed5fd67cefb5608a39be11d3b74e') + ASSET_I386_ROOTFS = Asset( + 'https://storage.tuxboot.com/buildroot/20241119/i386/rootfs.ext4.zst', + 'a1a3b3b4c9dccd6475b58db95c107b468b736b700f6620985a8ed050a73d51c8') + + def test_i386(self): + self.set_machine('q35') + self.cpu="coreduo" + self.wait_for_shutdown=False + self.common_tuxrun(kernel_asset=self.ASSET_I386_KERNEL, + rootfs_asset=self.ASSET_I386_ROOTFS, + drive="virtio-blk-pci") + +if __name__ == '__main__': + TuxRunBaselineTest.main() diff --git a/tests/functional/test_info_usernet.py b/tests/functional/test_info_usernet.py index cd37524..e8cbc37 100755 --- a/tests/functional/test_info_usernet.py +++ b/tests/functional/test_info_usernet.py @@ -11,8 +11,7 @@ # later. See the COPYING file in the top-level directory. from qemu_test import QemuSystemTest - -from qemu.utils import get_info_usernet_hostfwd_port +from qemu_test.utils import get_usernet_hostfwd_port class InfoUsernet(QemuSystemTest): @@ -22,9 +21,8 @@ class InfoUsernet(QemuSystemTest): self.set_machine('none') self.vm.add_args('-netdev', 'user,id=vnet,hostfwd=:127.0.0.1:0-:22') self.vm.launch() - res = self.vm.cmd('human-monitor-command', - command_line='info usernet') - port = get_info_usernet_hostfwd_port(res) + + port = get_usernet_hostfwd_port(self.vm) self.assertIsNotNone(port, ('"info usernet" output content does not seem to ' 'contain the redirected port')) diff --git a/tests/functional/test_intel_iommu.py b/tests/functional/test_intel_iommu.py new file mode 100755 index 0000000..62268d6 --- /dev/null +++ b/tests/functional/test_intel_iommu.py @@ -0,0 +1,155 @@ +#!/usr/bin/env python3 +# +# INTEL_IOMMU Functional tests +# +# Copyright (c) 2021 Red Hat, Inc. +# +# Author: +# Eric Auger <eric.auger@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. + +from qemu_test import LinuxKernelTest, Asset, exec_command_and_wait_for_pattern + + +class IntelIOMMU(LinuxKernelTest): + + ASSET_KERNEL = Asset( + ('https://archives.fedoraproject.org/pub/archive/fedora/linux/releases' + '/31/Server/x86_64/os/images/pxeboot/vmlinuz'), + 'd4738d03dbbe083ca610d0821d0a8f1488bebbdccef54ce33e3adb35fda00129') + + ASSET_INITRD = Asset( + ('https://archives.fedoraproject.org/pub/archive/fedora/linux/releases' + '/31/Server/x86_64/os/images/pxeboot/initrd.img'), + '277cd6c7adf77c7e63d73bbb2cded8ef9e2d3a2f100000e92ff1f8396513cd8b') + + ASSET_DISKIMAGE = Asset( + ('https://archives.fedoraproject.org/pub/archive/fedora/linux/releases' + '/31/Cloud/x86_64/images/Fedora-Cloud-Base-31-1.9.x86_64.qcow2'), + 'e3c1b309d9203604922d6e255c2c5d098a309c2d46215d8fc026954f3c5c27a0') + + DEFAULT_KERNEL_PARAMS = ('root=/dev/vda1 console=ttyS0 net.ifnames=0 ' + 'quiet rd.rescue ') + GUEST_PORT = 8080 + IOMMU_ADDON = ',iommu_platform=on,disable-modern=off,disable-legacy=on' + kernel_path = None + initrd_path = None + kernel_params = None + + def add_common_args(self, path): + self.vm.add_args('-drive', f'file={path},if=none,id=drv0,snapshot=on') + self.vm.add_args('-device', 'virtio-blk-pci,bus=pcie.0,' + + 'drive=drv0,id=virtio-disk0,bootindex=1,' + 'werror=stop,rerror=stop' + self.IOMMU_ADDON) + self.vm.add_args('-device', 'virtio-gpu-pci' + self.IOMMU_ADDON) + + self.vm.add_args('-netdev', + 'user,id=n1,hostfwd=tcp:127.0.0.1:0-:%d' % + self.GUEST_PORT) + self.vm.add_args('-device', + 'virtio-net-pci,netdev=n1' + self.IOMMU_ADDON) + + self.vm.add_args('-device', 'virtio-rng-pci,rng=rng0') + self.vm.add_args('-object', + 'rng-random,id=rng0,filename=/dev/urandom') + self.vm.add_args("-m", "1G") + self.vm.add_args("-accel", "kvm") + + def common_vm_setup(self): + self.set_machine('q35') + self.require_accelerator("kvm") + self.require_netdev('user') + + self.kernel_path = self.ASSET_KERNEL.fetch() + self.initrd_path = self.ASSET_INITRD.fetch() + image_path = self.ASSET_DISKIMAGE.fetch() + self.add_common_args(image_path) + self.kernel_params = self.DEFAULT_KERNEL_PARAMS + + def run_and_check(self): + if self.kernel_path: + self.vm.add_args('-kernel', self.kernel_path, + '-append', self.kernel_params, + '-initrd', self.initrd_path) + self.vm.set_console() + self.vm.launch() + self.wait_for_console_pattern('Entering emergency mode.') + prompt = '# ' + self.wait_for_console_pattern(prompt) + + # Copy a file (checked later), umount afterwards to drop disk cache: + exec_command_and_wait_for_pattern(self, 'mount /dev/vda1 /sysroot', + prompt) + filename = '/boot/initramfs-5.3.7-301.fc31.x86_64.img' + exec_command_and_wait_for_pattern(self, (f'cp /sysroot{filename}' + ' /sysroot/root/data'), + prompt) + exec_command_and_wait_for_pattern(self, 'umount /sysroot', prompt) + + # Switch from initrd to the cloud image filesystem: + exec_command_and_wait_for_pattern(self, 'mount /dev/vda1 /sysroot', + prompt) + exec_command_and_wait_for_pattern(self, + ('for d in dev proc sys run ; do ' + 'mount -o bind /$d /sysroot/$d ; done'), prompt) + exec_command_and_wait_for_pattern(self, 'chroot /sysroot', prompt) + + # Checking for IOMMU enablement: + self.log.info("Checking whether IOMMU has been enabled...") + exec_command_and_wait_for_pattern(self, 'cat /proc/cmdline', + 'intel_iommu=on') + self.wait_for_console_pattern(prompt) + exec_command_and_wait_for_pattern(self, 'dmesg | grep DMAR:', + 'IOMMU enabled') + self.wait_for_console_pattern(prompt) + exec_command_and_wait_for_pattern(self, + 'find /sys/kernel/iommu_groups/ -type l', + 'devices/0000:00:') + self.wait_for_console_pattern(prompt) + + # Check hard disk device via sha256sum: + self.log.info("Checking hard disk...") + hashsum = '0dc7472f879be70b2f3daae279e3ae47175ffe249691e7d97f47222b65b8a720' + exec_command_and_wait_for_pattern(self, 'sha256sum ' + filename, + hashsum) + self.wait_for_console_pattern(prompt) + exec_command_and_wait_for_pattern(self, 'sha256sum /root/data', + hashsum) + self.wait_for_console_pattern(prompt) + + # Check virtio-net via HTTP: + exec_command_and_wait_for_pattern(self, 'dhclient eth0', prompt) + self.check_http_download(filename, hashsum, self.GUEST_PORT) + + def test_intel_iommu(self): + self.common_vm_setup() + self.vm.add_args('-device', 'intel-iommu,intremap=on') + self.vm.add_args('-machine', 'kernel_irqchip=split') + self.kernel_params += 'intel_iommu=on' + self.run_and_check() + + def test_intel_iommu_strict(self): + self.common_vm_setup() + self.vm.add_args('-device', 'intel-iommu,intremap=on') + self.vm.add_args('-machine', 'kernel_irqchip=split') + self.kernel_params += 'intel_iommu=on,strict' + self.run_and_check() + + def test_intel_iommu_strict_cm(self): + self.common_vm_setup() + self.vm.add_args('-device', 'intel-iommu,intremap=on,caching-mode=on') + self.vm.add_args('-machine', 'kernel_irqchip=split') + self.kernel_params += 'intel_iommu=on,strict' + self.run_and_check() + + def test_intel_iommu_pt(self): + self.common_vm_setup() + self.vm.add_args('-device', 'intel-iommu,intremap=on') + self.vm.add_args('-machine', 'kernel_irqchip=split') + self.kernel_params += 'intel_iommu=on iommu=pt' + self.run_and_check() + +if __name__ == '__main__': + LinuxKernelTest.main() diff --git a/tests/functional/test_linux_initrd.py b/tests/functional/test_linux_initrd.py index c71a59d..2207f83 100755 --- a/tests/functional/test_linux_initrd.py +++ b/tests/functional/test_linux_initrd.py @@ -10,12 +10,10 @@ # 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 tempfile -from qemu_test import QemuSystemTest, Asset -from unittest import skipUnless +from qemu_test import QemuSystemTest, Asset, skipFlakyTest class LinuxInitrd(QemuSystemTest): @@ -60,7 +58,8 @@ class LinuxInitrd(QemuSystemTest): max_size + 1) self.assertRegex(self.vm.get_log(), expected_msg) - @skipUnless(os.getenv('QEMU_TEST_FLAKY_TESTS'), 'Test is unstable on GitLab') + # XXX file tracking bug + @skipFlakyTest(bug_url=None) def test_with_2gib_file_should_work_with_linux_v4_16(self): """ QEMU has supported up to 4 GiB initrd for recent kernel diff --git a/tests/functional/test_loongarch64_virt.py b/tests/functional/test_loongarch64_virt.py index 2b8baa2..b7d9abf 100755 --- a/tests/functional/test_loongarch64_virt.py +++ b/tests/functional/test_loongarch64_virt.py @@ -18,16 +18,16 @@ class LoongArchMachine(QemuSystemTest): ASSET_KERNEL = Asset( ('https://github.com/yangxiaojuan-loongson/qemu-binary/' - 'releases/download/2024-05-30/vmlinuz.efi'), + 'releases/download/2024-11-26/vmlinuz.efi'), '08b88a45f48a5fd92260bae895be4e5175be2397481a6f7821b9f39b2965b79e') ASSET_INITRD = Asset( ('https://github.com/yangxiaojuan-loongson/qemu-binary/' - 'releases/download/2024-05-30/ramdisk'), + 'releases/download/2024-11-26/ramdisk'), '03d6fb6f8ee64ecac961120a0bdacf741f17b3bee2141f17fa01908c8baf176a') ASSET_BIOS = Asset( ('https://github.com/yangxiaojuan-loongson/qemu-binary/' - 'releases/download/2024-05-30/QEMU_EFI.fd'), - '937c1e7815e2340150c194a9f8f0474259038a3d7b8845ed62cc08163c46bea1') + 'releases/download/2024-11-26/QEMU_EFI.fd'), + 'f55fbf5d92e885844631ae9bfa8887f659bbb4f6ef2beea9e9ff8bc0603b6697') def wait_for_console_pattern(self, success_message, vm=None): wait_for_console_pattern(self, success_message, diff --git a/tests/functional/test_m68k_mcf5208evb.py b/tests/functional/test_m68k_mcf5208evb.py new file mode 100755 index 0000000..c7d1998 --- /dev/null +++ b/tests/functional/test_m68k_mcf5208evb.py @@ -0,0 +1,27 @@ +#!/usr/bin/env python3 +# +# Functional test that boots a Linux kernel on an MCF5208EVB machine +# and checks the console +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import LinuxKernelTest, Asset + + +class Mcf5208EvbTest(LinuxKernelTest): + + ASSET_DAY07 = Asset( + 'https://qemu-advcal.gitlab.io/qac-best-of-multiarch/download/day07.tar.xz', + '753c2f3837126b7c6ba92d0b1e0b156e8a2c5131d2d576bb0b9a763fae73c08a') + + def test_m68k_mcf5208evb(self): + self.set_machine('mcf5208evb') + self.archive_extract(self.ASSET_DAY07) + self.vm.set_console() + self.vm.add_args('-kernel', + self.scratch_file('day07', 'sanity-clause.elf')) + self.vm.launch() + self.wait_for_console_pattern('QEMU advent calendar') + +if __name__ == '__main__': + LinuxKernelTest.main() diff --git a/tests/functional/test_m68k_nextcube.py b/tests/functional/test_m68k_nextcube.py index 89385a1..13c72bd 100755 --- a/tests/functional/test_m68k_nextcube.py +++ b/tests/functional/test_m68k_nextcube.py @@ -7,19 +7,11 @@ # 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 time from qemu_test import QemuSystemTest, Asset -from unittest import skipUnless - -from qemu_test.tesseract import tesseract_available, tesseract_ocr - -PIL_AVAILABLE = True -try: - from PIL import Image -except ImportError: - PIL_AVAILABLE = False +from qemu_test import skipIfMissingImports, skipIfMissingCommands +from qemu_test.tesseract import tesseract_ocr class NextCubeMachine(QemuSystemTest): @@ -37,30 +29,29 @@ class NextCubeMachine(QemuSystemTest): self.vm.launch() self.log.info('VM launched, waiting for display') - # TODO: Use avocado.utils.wait.wait_for to catch the - # 'displaysurface_create 1120x832' trace-event. + # TODO: wait for the 'displaysurface_create 1120x832' trace-event. time.sleep(2) - self.vm.cmd('human-monitor-command', - command_line='screendump %s' % screenshot_path) + res = self.vm.cmd('human-monitor-command', + command_line='screendump %s' % screenshot_path) + if 'unknown command' in res: + self.skipTest('screendump not available') - @skipUnless(PIL_AVAILABLE, 'Python PIL not installed') + @skipIfMissingImports("PIL") def test_bootrom_framebuffer_size(self): self.set_machine('next-cube') - screenshot_path = os.path.join(self.workdir, "dump.ppm") + screenshot_path = self.scratch_file("dump.ppm") self.check_bootrom_framebuffer(screenshot_path) + from PIL import Image width, height = Image.open(screenshot_path).size self.assertEqual(width, 1120) self.assertEqual(height, 832) - # Tesseract 4 adds a new OCR engine based on LSTM neural networks. The - # new version is faster and more accurate than version 3. The drawback is - # that it is still alpha-level software. - @skipUnless(tesseract_available(4), 'tesseract OCR tool not available') + @skipIfMissingCommands('tesseract') def test_bootrom_framebuffer_ocr_with_tesseract(self): self.set_machine('next-cube') - screenshot_path = os.path.join(self.workdir, "dump.ppm") + screenshot_path = self.scratch_file("dump.ppm") self.check_bootrom_framebuffer(screenshot_path) lines = tesseract_ocr(screenshot_path) text = '\n'.join(lines) diff --git a/tests/functional/test_m68k_q800.py b/tests/functional/test_m68k_q800.py new file mode 100755 index 0000000..b3e6553 --- /dev/null +++ b/tests/functional/test_m68k_q800.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python3 +# +# Functional test for testing the q800 m68k machine +# +# 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 qemu_test import LinuxKernelTest, Asset + +class Q800MachineTest(LinuxKernelTest): + + ASSET_KERNEL = Asset( + ('https://snapshot.debian.org/' + 'archive/debian-ports/20191021T083923Z/pool-m68k/main/l/linux/' + 'kernel-image-5.3.0-1-m68k-di_5.3.7-1_m68k.udeb'), + '949e50d74d4b9bc15d26c06d402717b7a4c0e32ff8100014f5930d8024de7b73') + + def test_m68k_q800(self): + self.set_machine('q800') + + kernel_path = self.archive_extract(self.ASSET_KERNEL, + member='boot/vmlinux-5.3.0-1-m68k') + + self.vm.set_console() + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'console=ttyS0 vga=off') + self.vm.add_args('-kernel', kernel_path, + '-append', kernel_command_line, + '-audio', 'none') + self.vm.launch() + console_pattern = 'Kernel command line: %s' % kernel_command_line + self.wait_for_console_pattern(console_pattern) + console_pattern = 'No filesystem could mount root' + self.wait_for_console_pattern(console_pattern) + +if __name__ == '__main__': + LinuxKernelTest.main() diff --git a/tests/functional/test_m68k_replay.py b/tests/functional/test_m68k_replay.py new file mode 100755 index 0000000..213d6ae --- /dev/null +++ b/tests/functional/test_m68k_replay.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python3 +# +# Replay test that boots a Linux kernel on an m68k machine +# and checks the console +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import Asset +from replay_kernel import ReplayKernelBase + + +class M68kReplay(ReplayKernelBase): + + ASSET_Q800 = Asset( + ('https://snapshot.debian.org/' + 'archive/debian-ports/20191021T083923Z/pool-m68k/main/l/linux/' + 'kernel-image-5.3.0-1-m68k-di_5.3.7-1_m68k.udeb'), + '949e50d74d4b9bc15d26c06d402717b7a4c0e32ff8100014f5930d8024de7b73') + + def test_q800(self): + self.set_machine('q800') + kernel_path = self.archive_extract(self.ASSET_Q800, + member='boot/vmlinux-5.3.0-1-m68k') + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'console=ttyS0 vga=off') + console_pattern = 'No filesystem could mount root' + self.run_rr(kernel_path, kernel_command_line, console_pattern, + args=('-audio', 'none')) + + ASSET_MCF5208 = Asset( + 'https://qemu-advcal.gitlab.io/qac-best-of-multiarch/download/day07.tar.xz', + '753c2f3837126b7c6ba92d0b1e0b156e8a2c5131d2d576bb0b9a763fae73c08a') + + def test_mcf5208evb(self): + self.set_machine('mcf5208evb') + kernel_path = self.archive_extract(self.ASSET_MCF5208, + member='day07/sanity-clause.elf') + self.run_rr(kernel_path, self.KERNEL_COMMON_COMMAND_LINE, + 'QEMU advent calendar') + + +if __name__ == '__main__': + ReplayKernelBase.main() diff --git a/tests/functional/test_m68k_tuxrun.py b/tests/functional/test_m68k_tuxrun.py new file mode 100755 index 0000000..7eacba1 --- /dev/null +++ b/tests/functional/test_m68k_tuxrun.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python3 +# +# Functional test that boots known good tuxboot images the same way +# that tuxrun (www.tuxrun.org) does. This tool is used by things like +# the LKFT project to run regression tests on kernels. +# +# Copyright (c) 2024 Linaro Ltd. +# +# Author: +# Alex Bennée <alex.bennee@linaro.org> +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import Asset +from qemu_test.tuxruntest import TuxRunBaselineTest + +class TuxRunM68KTest(TuxRunBaselineTest): + + ASSET_M68K_KERNEL = Asset( + 'https://storage.tuxboot.com/buildroot/20241119/m68k/vmlinux', + '7754e1d5cec753ccf1dc6894729a7f54c1a4965631ebf56df8e4ce1163ad19d8') + ASSET_M68K_ROOTFS = Asset( + 'https://storage.tuxboot.com/buildroot/20241119/m68k/rootfs.ext4.zst', + '557962ffff265607912e82232cf21adbe0e4e5a88e1e1d411ce848c37f0213e9') + + def test_m68k(self): + self.set_machine('virt') + self.cpu="m68040" + self.common_tuxrun(kernel_asset=self.ASSET_M68K_KERNEL, + rootfs_asset=self.ASSET_M68K_ROOTFS, + drive="virtio-blk-device") + +if __name__ == '__main__': + TuxRunBaselineTest.main() diff --git a/tests/functional/test_mem_addr_space.py b/tests/functional/test_mem_addr_space.py index bb0cf06..61b4a19 100755 --- a/tests/functional/test_mem_addr_space.py +++ b/tests/functional/test_mem_addr_space.py @@ -20,6 +20,25 @@ class MemAddrCheck(QemuSystemTest): # this reason. DELAY_Q35_BOOT_SEQUENCE = 1 + # This helper can go away when the 32-bit host deprecation + # turns into full & final removal of support. + def ensure_64bit_binary(self): + with open(self.qemu_bin, "rb") as fh: + ident = fh.read(4) + + # "\x7fELF" + if ident != bytes([0x7f, 0x45, 0x4C, 0x46]): + # Non-ELF file implies macOS or Windows which + # we already assume to be 64-bit only + return + + # bits == 1 -> 32-bit; bits == 2 -> 64-bit + bits = int.from_bytes(fh.read(1), byteorder='little') + if bits != 2: + # 32-bit ELF builds won't be able to address sufficient + # RAM to run the tests + self.skipTest("64-bit build host is required") + # first, lets test some 32-bit processors. # for all 32-bit cases, pci64_hole_size is 0. def test_phybits_low_pse36(self): @@ -38,8 +57,9 @@ class MemAddrCheck(QemuSystemTest): If maxmem is set to 59.5G with all other QEMU parameters identical, QEMU should start fine. """ - self.vm.add_args('-S', '-machine', 'q35', '-m', - '512,slots=1,maxmem=59.6G', + self.ensure_64bit_binary() + self.set_machine('q35') + self.vm.add_args('-S', '-m', '512,slots=1,maxmem=59.6G', '-cpu', 'pentium,pse36=on', '-display', 'none', '-object', 'memory-backend-ram,id=mem1,size=1G', '-device', 'pc-dimm,id=vm0,memdev=mem1') @@ -55,8 +75,9 @@ class MemAddrCheck(QemuSystemTest): access up to a maximum of 64GiB of memory. Rest is the same as the case with pse36 above. """ - self.vm.add_args('-S', '-machine', 'q35', '-m', - '512,slots=1,maxmem=59.6G', + self.ensure_64bit_binary() + self.set_machine('q35') + self.vm.add_args('-S', '-m', '512,slots=1,maxmem=59.6G', '-cpu', 'pentium,pae=on', '-display', 'none', '-object', 'memory-backend-ram,id=mem1,size=1G', '-device', 'pc-dimm,id=vm0,memdev=mem1') @@ -71,8 +92,9 @@ class MemAddrCheck(QemuSystemTest): Setting maxmem to 59.5G and making sure that QEMU can start with the same options as the failing case above with pse36 cpu feature. """ - self.vm.add_args('-machine', 'q35', '-m', - '512,slots=1,maxmem=59.5G', + self.ensure_64bit_binary() + self.set_machine('q35') + self.vm.add_args('-m', '512,slots=1,maxmem=59.5G', '-cpu', 'pentium,pse36=on', '-display', 'none', '-object', 'memory-backend-ram,id=mem1,size=1G', '-device', 'pc-dimm,id=vm0,memdev=mem1') @@ -88,8 +110,9 @@ class MemAddrCheck(QemuSystemTest): Setting maxmem to 59.5G and making sure that QEMU can start fine with the same options as the case above. """ - self.vm.add_args('-machine', 'q35', '-m', - '512,slots=1,maxmem=59.5G', + self.ensure_64bit_binary() + self.set_machine('q35') + self.vm.add_args('-m', '512,slots=1,maxmem=59.5G', '-cpu', 'pentium,pae=on', '-display', 'none', '-object', 'memory-backend-ram,id=mem1,size=1G', '-device', 'pc-dimm,id=vm0,memdev=mem1') @@ -104,8 +127,9 @@ class MemAddrCheck(QemuSystemTest): Pentium2 has 36 bits of addressing, so its same as pentium with pse36 ON. """ - self.vm.add_args('-machine', 'q35', '-m', - '512,slots=1,maxmem=59.5G', + self.ensure_64bit_binary() + self.set_machine('q35') + self.vm.add_args('-m', '512,slots=1,maxmem=59.5G', '-cpu', 'pentium2', '-display', 'none', '-object', 'memory-backend-ram,id=mem1,size=1G', '-device', 'pc-dimm,id=vm0,memdev=mem1') @@ -123,8 +147,9 @@ class MemAddrCheck(QemuSystemTest): message because the region for memory hotplug is always placed above 4 GiB due to the PCI hole and simplicity. """ - self.vm.add_args('-S', '-machine', 'q35', '-m', - '512,slots=1,maxmem=4G', + self.ensure_64bit_binary() + self.set_machine('q35') + self.vm.add_args('-S', '-m', '512,slots=1,maxmem=4G', '-cpu', 'pentium', '-display', 'none', '-object', 'memory-backend-ram,id=mem1,size=1G', '-device', 'pc-dimm,id=vm0,memdev=mem1') @@ -150,8 +175,9 @@ class MemAddrCheck(QemuSystemTest): which is equal to 987.5 GiB. Setting the value to 988 GiB should make QEMU fail with the error message. """ - self.vm.add_args('-S', '-machine', 'pc-q35-7.0', '-m', - '512,slots=1,maxmem=988G', + self.ensure_64bit_binary() + self.set_machine('pc-q35-7.0') + self.vm.add_args('-S', '-m', '512,slots=1,maxmem=988G', '-display', 'none', '-object', 'memory-backend-ram,id=mem1,size=1G', '-device', 'pc-dimm,id=vm0,memdev=mem1') @@ -170,8 +196,9 @@ class MemAddrCheck(QemuSystemTest): Make sure QEMU fails when maxmem size is 976 GiB (12 GiB less than 988 GiB). """ - self.vm.add_args('-S', '-machine', 'pc-q35-7.1', '-m', - '512,slots=1,maxmem=976G', + self.ensure_64bit_binary() + self.set_machine('pc-q35-7.1') + self.vm.add_args('-S', '-m', '512,slots=1,maxmem=976G', '-display', 'none', '-object', 'memory-backend-ram,id=mem1,size=1G', '-device', 'pc-dimm,id=vm0,memdev=mem1') @@ -186,8 +213,9 @@ class MemAddrCheck(QemuSystemTest): Same as q35-7.0 AMD case except that here we check that QEMU can successfully start when maxmem is < 988G. """ - self.vm.add_args('-S', '-machine', 'pc-q35-7.0', '-m', - '512,slots=1,maxmem=987.5G', + self.ensure_64bit_binary() + self.set_machine('pc-q35-7.0') + self.vm.add_args('-S', '-m', '512,slots=1,maxmem=987.5G', '-display', 'none', '-object', 'memory-backend-ram,id=mem1,size=1G', '-device', 'pc-dimm,id=vm0,memdev=mem1') @@ -202,8 +230,9 @@ class MemAddrCheck(QemuSystemTest): Same as q35-7.1 AMD case except that here we check that QEMU can successfully start when maxmem is < 976G. """ - self.vm.add_args('-S', '-machine', 'pc-q35-7.1', '-m', - '512,slots=1,maxmem=975.5G', + self.ensure_64bit_binary() + self.set_machine('pc-q35-7.1') + self.vm.add_args('-S', '-m', '512,slots=1,maxmem=975.5G', '-display', 'none', '-object', 'memory-backend-ram,id=mem1,size=1G', '-device', 'pc-dimm,id=vm0,memdev=mem1') @@ -219,9 +248,10 @@ class MemAddrCheck(QemuSystemTest): Intel cpu instead. QEMU should start fine in this case as "above_4G" memory starts at 4G. """ + self.ensure_64bit_binary() + self.set_machine('pc-q35-7.1') self.vm.add_args('-S', '-cpu', 'Skylake-Server', - '-machine', 'pc-q35-7.1', '-m', - '512,slots=1,maxmem=976G', + '-m', '512,slots=1,maxmem=976G', '-display', 'none', '-object', 'memory-backend-ram,id=mem1,size=1G', '-device', 'pc-dimm,id=vm0,memdev=mem1') @@ -243,9 +273,10 @@ class MemAddrCheck(QemuSystemTest): memory for the VM (1024 - 32 - 1 + 0.5). With 992 GiB, QEMU should fail to start. """ + self.ensure_64bit_binary() + self.set_machine('pc-q35-7.1') self.vm.add_args('-S', '-cpu', 'EPYC-v4,phys-bits=41', - '-machine', 'pc-q35-7.1', '-m', - '512,slots=1,maxmem=992G', + '-m', '512,slots=1,maxmem=992G', '-display', 'none', '-object', 'memory-backend-ram,id=mem1,size=1G', '-device', 'pc-dimm,id=vm0,memdev=mem1') @@ -261,9 +292,10 @@ class MemAddrCheck(QemuSystemTest): Same as above but by setting maxram between 976 GiB and 992 Gib, QEMU should start fine. """ + self.ensure_64bit_binary() + self.set_machine('pc-q35-7.1') self.vm.add_args('-S', '-cpu', 'EPYC-v4,phys-bits=41', - '-machine', 'pc-q35-7.1', '-m', - '512,slots=1,maxmem=990G', + '-m', '512,slots=1,maxmem=990G', '-display', 'none', '-object', 'memory-backend-ram,id=mem1,size=1G', '-device', 'pc-dimm,id=vm0,memdev=mem1') @@ -281,12 +313,13 @@ class MemAddrCheck(QemuSystemTest): So maxmem here should be at most 986 GiB considering all memory boundary alignment constraints with 40 bits (1 TiB) of processor physical bits. """ + self.ensure_64bit_binary() + self.set_machine('q35') self.vm.add_args('-S', '-cpu', 'Skylake-Server,phys-bits=40', - '-machine', 'q35,cxl=on', '-m', - '512,slots=1,maxmem=987G', + '-m', '512,slots=1,maxmem=987G', '-display', 'none', '-device', 'pxb-cxl,bus_nr=12,bus=pcie.0,id=cxl.1', - '-M', 'cxl-fmw.0.targets.0=cxl.1,cxl-fmw.0.size=1G') + '-M', 'cxl=on,cxl-fmw.0.targets.0=cxl.1,cxl-fmw.0.size=1G') self.vm.set_qmp_monitor(enabled=False) self.vm.launch() self.vm.wait() @@ -299,9 +332,11 @@ class MemAddrCheck(QemuSystemTest): with the exact same parameters as above, QEMU should start fine even with cxl enabled. """ + self.ensure_64bit_binary() + self.set_machine('q35') self.vm.add_args('-S', '-cpu', 'Skylake-Server,phys-bits=40', - '-machine', 'q35,cxl=on', '-m', - '512,slots=1,maxmem=987G', + '-machine', 'cxl=on', + '-m', '512,slots=1,maxmem=987G', '-display', 'none', '-device', 'pxb-cxl,bus_nr=12,bus=pcie.0,id=cxl.1') self.vm.set_qmp_monitor(enabled=False) diff --git a/tests/functional/test_memlock.py b/tests/functional/test_memlock.py new file mode 100755 index 0000000..2b515ff --- /dev/null +++ b/tests/functional/test_memlock.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python3 +# +# Functional test that check overcommit memlock options +# +# Copyright (c) Yandex Technologies LLC, 2025 +# +# Author: +# Alexandr Moshkov <dtalexundeer@yandex-team.ru> +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import re + +from typing import Dict + +from qemu_test import QemuSystemTest +from qemu_test import skipLockedMemoryTest + + +STATUS_VALUE_PATTERN = re.compile(r'^(\w+):\s+(\d+) kB', re.MULTILINE) + + +@skipLockedMemoryTest(2_097_152) # 2GB +class MemlockTest(QemuSystemTest): + """ + Runs a guest with memlock options. + Then verify, that this options is working correctly + by checking the status file of the QEMU process. + """ + + def common_vm_setup_with_memlock(self, memlock): + self.vm.add_args('-overcommit', f'mem-lock={memlock}') + self.vm.launch() + + def test_memlock_off(self): + self.common_vm_setup_with_memlock('off') + + status = self.get_process_status_values(self.vm.get_pid()) + + self.assertTrue(status['VmLck'] == 0) + + def test_memlock_on(self): + self.common_vm_setup_with_memlock('on') + + status = self.get_process_status_values(self.vm.get_pid()) + + # VmLck > 0 kB and almost all memory is resident + self.assertTrue(status['VmLck'] > 0) + self.assertTrue(status['VmRSS'] >= status['VmSize'] * 0.70) + + def test_memlock_onfault(self): + self.common_vm_setup_with_memlock('on-fault') + + status = self.get_process_status_values(self.vm.get_pid()) + + # VmLck > 0 kB and only few memory is resident + self.assertTrue(status['VmLck'] > 0) + self.assertTrue(status['VmRSS'] <= status['VmSize'] * 0.30) + + def get_process_status_values(self, pid: int) -> Dict[str, int]: + result = {} + raw_status = self._get_raw_process_status(pid) + + for line in raw_status.split('\n'): + if m := STATUS_VALUE_PATTERN.match(line): + result[m.group(1)] = int(m.group(2)) + + return result + + def _get_raw_process_status(self, pid: int) -> str: + try: + with open(f'/proc/{pid}/status', 'r') as f: + return f.read() + except FileNotFoundError: + self.skipTest("Can't open status file of the process") + + +if __name__ == '__main__': + MemlockTest.main() diff --git a/tests/functional/test_microblaze_replay.py b/tests/functional/test_microblaze_replay.py new file mode 100755 index 0000000..7484c41 --- /dev/null +++ b/tests/functional/test_microblaze_replay.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python3 +# +# Replay test that boots a Linux kernel on an microblaze machine +# and checks the console +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import Asset +from replay_kernel import ReplayKernelBase + + +class MicroblazeReplay(ReplayKernelBase): + + ASSET_DAY17 = Asset( + ('https://qemu-advcal.gitlab.io/qac-best-of-multiarch/download/' + 'day17.tar.xz'), + '3ba7439dfbea7af4876662c97f8e1f0cdad9231fc166e4861d17042489270057') + + def test_microblaze_s3adsp1800(self): + self.set_machine('petalogix-s3adsp1800') + kernel_path = self.archive_extract(self.ASSET_DAY17, + member='day17/ballerina.bin') + self.run_rr(kernel_path, self.REPLAY_KERNEL_COMMAND_LINE, + 'QEMU advent calendar') + + +if __name__ == '__main__': + ReplayKernelBase.main() diff --git a/tests/functional/test_microblaze_s3adsp1800.py b/tests/functional/test_microblaze_s3adsp1800.py index 4f692ff..f093b16 100755 --- a/tests/functional/test_microblaze_s3adsp1800.py +++ b/tests/functional/test_microblaze_s3adsp1800.py @@ -7,27 +7,32 @@ # 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 time -from qemu_test import exec_command, exec_command_and_wait_for_pattern +from qemu_test import exec_command_and_wait_for_pattern from qemu_test import QemuSystemTest, Asset from qemu_test import wait_for_console_pattern -from qemu_test.utils import archive_extract + class MicroblazeMachine(QemuSystemTest): timeout = 90 - ASSET_IMAGE = Asset( + ASSET_IMAGE_BE = Asset( ('https://qemu-advcal.gitlab.io/qac-best-of-multiarch/download/' 'day17.tar.xz'), '3ba7439dfbea7af4876662c97f8e1f0cdad9231fc166e4861d17042489270057') - def test_microblaze_s3adsp1800(self): + ASSET_IMAGE_LE = Asset( + ('http://www.qemu-advent-calendar.org/2023/download/day13.tar.gz'), + 'b9b3d43c5dd79db88ada495cc6e0d1f591153fe41355e925d791fbf44de50c22') + + def do_ballerina_be_test(self, force_endianness=False): self.set_machine('petalogix-s3adsp1800') - file_path = self.ASSET_IMAGE.fetch() - archive_extract(file_path, self.workdir) + self.archive_extract(self.ASSET_IMAGE_BE) self.vm.set_console() - self.vm.add_args('-kernel', self.workdir + '/day17/ballerina.bin') + self.vm.add_args('-kernel', + self.scratch_file('day17', 'ballerina.bin')) + if force_endianness: + self.vm.add_args('-M', 'endianness=big') self.vm.launch() wait_for_console_pattern(self, 'This architecture does not have ' 'kernel memory protection') @@ -36,5 +41,36 @@ class MicroblazeMachine(QemuSystemTest): # message, that's why we don't test for a later string here. This # needs some investigation by a microblaze wizard one day... + def do_xmaton_le_test(self, force_endianness=False): + self.require_netdev('user') + self.set_machine('petalogix-s3adsp1800') + self.archive_extract(self.ASSET_IMAGE_LE) + self.vm.set_console() + self.vm.add_args('-kernel', self.scratch_file('day13', 'xmaton.bin')) + if force_endianness: + self.vm.add_args('-M', 'endianness=little') + tftproot = self.scratch_file('day13') + self.vm.add_args('-nic', f'user,tftp={tftproot}') + self.vm.launch() + wait_for_console_pattern(self, 'QEMU Advent Calendar 2023') + wait_for_console_pattern(self, 'buildroot login:') + exec_command_and_wait_for_pattern(self, 'root', '#') + exec_command_and_wait_for_pattern(self, + 'tftp -g -r xmaton.png 10.0.2.2 ; md5sum xmaton.png', + '821cd3cab8efd16ad6ee5acc3642a8ea') + + +class MicroblazeBigEndianMachine(MicroblazeMachine): + + ASSET_IMAGE_BE = MicroblazeMachine.ASSET_IMAGE_BE + ASSET_IMAGE_LE = MicroblazeMachine.ASSET_IMAGE_LE + + def test_microblaze_s3adsp1800_legacy_be(self): + self.do_ballerina_be_test() + + def test_microblaze_s3adsp1800_legacy_le(self): + self.do_xmaton_le_test(force_endianness=True) + + if __name__ == '__main__': QemuSystemTest.main() diff --git a/tests/functional/test_microblazeel_s3adsp1800.py b/tests/functional/test_microblazeel_s3adsp1800.py index faa3927..915902d 100755 --- a/tests/functional/test_microblazeel_s3adsp1800.py +++ b/tests/functional/test_microblazeel_s3adsp1800.py @@ -7,36 +7,20 @@ # 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 time -from qemu_test import exec_command, exec_command_and_wait_for_pattern -from qemu_test import QemuSystemTest, Asset -from qemu_test import wait_for_console_pattern -from qemu_test.utils import archive_extract - -class MicroblazeelMachine(QemuSystemTest): - - timeout = 90 - - ASSET_IMAGE = Asset( - ('http://www.qemu-advent-calendar.org/2023/download/day13.tar.gz'), - 'b9b3d43c5dd79db88ada495cc6e0d1f591153fe41355e925d791fbf44de50c22') - - def test_microblazeel_s3adsp1800(self): - self.require_netdev('user') - self.set_machine('petalogix-s3adsp1800') - file_path = self.ASSET_IMAGE.fetch() - archive_extract(file_path, self.workdir) - self.vm.set_console() - self.vm.add_args('-kernel', self.workdir + '/day13/xmaton.bin') - self.vm.add_args('-nic', 'user,tftp=' + self.workdir + '/day13/') - self.vm.launch() - wait_for_console_pattern(self, 'QEMU Advent Calendar 2023') - time.sleep(0.1) - exec_command(self, 'root') - time.sleep(0.1) - exec_command_and_wait_for_pattern(self, - 'tftp -g -r xmaton.png 10.0.2.2 ; md5sum xmaton.png', - '821cd3cab8efd16ad6ee5acc3642a8ea') +from test_microblaze_s3adsp1800 import MicroblazeMachine + + +class MicroblazeLittleEndianMachine(MicroblazeMachine): + + ASSET_IMAGE_LE = MicroblazeMachine.ASSET_IMAGE_LE + ASSET_IMAGE_BE = MicroblazeMachine.ASSET_IMAGE_BE + + def test_microblaze_s3adsp1800_legacy_le(self): + self.do_xmaton_le_test() + + def test_microblaze_s3adsp1800_legacy_be(self): + self.do_ballerina_be_test(force_endianness=True) + if __name__ == '__main__': - QemuSystemTest.main() + MicroblazeMachine.main() diff --git a/tests/functional/test_migration.py b/tests/functional/test_migration.py new file mode 100755 index 0000000..c4393c3 --- /dev/null +++ b/tests/functional/test_migration.py @@ -0,0 +1,98 @@ +#!/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 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('ncat') + def test_migration_with_exec(self): + with Ports() as ports: + free_port = self._get_free_port(ports) + dest_uri = 'exec:ncat -l localhost %u' % free_port + src_uri = 'exec:ncat localhost %u' % free_port + self.do_migrate(dest_uri, src_uri) + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/functional/test_mips64_malta.py b/tests/functional/test_mips64_malta.py new file mode 100755 index 0000000..53c3e0c --- /dev/null +++ b/tests/functional/test_mips64_malta.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python3 +# +# Functional tests for the big-endian 64-bit MIPS Malta board +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import LinuxKernelTest, Asset +from test_mips_malta import mips_check_wheezy + + +class MaltaMachineConsole(LinuxKernelTest): + + ASSET_WHEEZY_KERNEL = Asset( + ('https://people.debian.org/~aurel32/qemu/mips/' + 'vmlinux-3.2.0-4-5kc-malta'), + '3e4ec154db080b3f1839f04dde83120654a33e5e1716863de576c47cb94f68f6') + + ASSET_WHEEZY_DISK = Asset( + ('https://people.debian.org/~aurel32/qemu/mips/' + 'debian_wheezy_mips_standard.qcow2'), + 'de03599285b8382ad309309a6c4869f6c6c42a5cfc983342bab9ec0dfa7849a2') + + def test_wheezy(self): + kernel_path = self.ASSET_WHEEZY_KERNEL.fetch() + image_path = self.ASSET_WHEEZY_DISK.fetch() + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'console=ttyS0 root=/dev/sda1') + mips_check_wheezy(self, + kernel_path, image_path, kernel_command_line, cpuinfo='MIPS 20Kc', + dl_file='/boot/initrd.img-3.2.0-4-5kc-malta', + hsum='d98b953bb4a41c0fc0fd8d19bbc691c08989ac52568c1d3054d92dfd890d3f06') + + +if __name__ == '__main__': + LinuxKernelTest.main() diff --git a/tests/functional/test_mips64_tuxrun.py b/tests/functional/test_mips64_tuxrun.py new file mode 100755 index 0000000..0e4c659 --- /dev/null +++ b/tests/functional/test_mips64_tuxrun.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python3 +# +# Functional test that boots known good tuxboot images the same way +# that tuxrun (www.tuxrun.org) does. This tool is used by things like +# the LKFT project to run regression tests on kernels. +# +# Copyright (c) 2023 Linaro Ltd. +# +# Author: +# Alex Bennée <alex.bennee@linaro.org> +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import Asset +from qemu_test.tuxruntest import TuxRunBaselineTest + +class TuxRunMips64Test(TuxRunBaselineTest): + + ASSET_MIPS64_KERNEL = Asset( + 'https://storage.tuxboot.com/buildroot/20241119/mips64/vmlinux', + 'fe2882d216898ba2c56b49ba59f46ad392f36871f7fe325373cd926848b9dbdc') + ASSET_MIPS64_ROOTFS = Asset( + 'https://storage.tuxboot.com/buildroot/20241119/mips64/rootfs.ext4.zst', + 'b8c98400216b6d4fb3b3ff05e9929aa015948b596cf0b82234813c84a4f7f4d5') + + def test_mips64(self): + self.set_machine('malta') + self.root="sda" + self.wait_for_shutdown=False + self.common_tuxrun(kernel_asset=self.ASSET_MIPS64_KERNEL, + rootfs_asset=self.ASSET_MIPS64_ROOTFS, + drive="driver=ide-hd,bus=ide.0,unit=0") + +if __name__ == '__main__': + TuxRunBaselineTest.main() diff --git a/tests/functional/test_mips64el_fuloong2e.py b/tests/functional/test_mips64el_fuloong2e.py index 7688a32..35e500b 100755 --- a/tests/functional/test_mips64el_fuloong2e.py +++ b/tests/functional/test_mips64el_fuloong2e.py @@ -12,15 +12,34 @@ import os import subprocess -from qemu_test import QemuSystemTest -from qemu_test import wait_for_console_pattern +from qemu_test import LinuxKernelTest, Asset +from qemu_test import wait_for_console_pattern, skipUntrustedTest from unittest import skipUnless -class MipsFuloong2e(QemuSystemTest): +class MipsFuloong2e(LinuxKernelTest): timeout = 60 - @skipUnless(os.getenv('QEMU_TEST_ALLOW_UNTRUSTED_CODE'), 'untrusted code') + ASSET_KERNEL = Asset( + ('http://archive.debian.org/debian/pool/main/l/linux/' + 'linux-image-3.16.0-6-loongson-2e_3.16.56-1+deb8u1_mipsel.deb'), + '2a70f15b397f4ced632b0c15cb22660394190644146d804d60a4796eefbe1f50') + + def test_linux_kernel_3_16(self): + kernel_path = self.archive_extract( + self.ASSET_KERNEL, + member='boot/vmlinux-3.16.0-6-loongson-2e') + + self.set_machine('fuloong2e') + self.vm.set_console() + kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'console=ttyS0' + self.vm.add_args('-kernel', kernel_path, + '-append', kernel_command_line) + self.vm.launch() + console_pattern = 'Kernel command line: %s' % kernel_command_line + self.wait_for_console_pattern(console_pattern) + + @skipUntrustedTest() @skipUnless(os.getenv('RESCUE_YL_PATH'), 'RESCUE_YL_PATH not available') def test_linux_kernel_2_6_27_isa_serial(self): # Recovery system for the Yeeloong laptop @@ -42,4 +61,4 @@ class MipsFuloong2e(QemuSystemTest): if __name__ == '__main__': - QemuSystemTest.main() + LinuxKernelTest.main() diff --git a/tests/functional/test_mips64el_loongson3v.py b/tests/functional/test_mips64el_loongson3v.py index 55d6292..f85371e 100755 --- a/tests/functional/test_mips64el_loongson3v.py +++ b/tests/functional/test_mips64el_loongson3v.py @@ -9,12 +9,9 @@ # # SPDX-License-Identifier: GPL-2.0-or-later -import os -import time - -from unittest import skipUnless from qemu_test import QemuSystemTest, Asset -from qemu_test import wait_for_console_pattern +from qemu_test import wait_for_console_pattern, skipUntrustedTest + class MipsLoongson3v(QemuSystemTest): timeout = 60 @@ -24,7 +21,7 @@ class MipsLoongson3v(QemuSystemTest): 'releases/download/20210112/pmon-3avirt.bin'), 'fcdf6bb2cb7885a4a62f31fcb0d5e368bac7b6cea28f40c6dfa678af22fea20a') - @skipUnless(os.getenv('QEMU_TEST_ALLOW_UNTRUSTED_CODE'), 'untrusted code') + @skipUntrustedTest() def test_pmon_serial_console(self): self.set_machine('loongson3-virt') diff --git a/tests/functional/test_mips64el_malta.py b/tests/functional/test_mips64el_malta.py new file mode 100755 index 0000000..3cc79b7 --- /dev/null +++ b/tests/functional/test_mips64el_malta.py @@ -0,0 +1,197 @@ +#!/usr/bin/env python3 +# +# Functional tests for the little-endian 64-bit MIPS Malta board +# +# Copyright (c) Philippe Mathieu-Daudé <f4bug@amsat.org> +# +# This work is licensed under the terms of the GNU GPL, version 2 or later. +# See the COPYING file in the top-level directory. +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import os +import logging + +from qemu_test import LinuxKernelTest, Asset +from qemu_test import exec_command_and_wait_for_pattern +from qemu_test import skipIfMissingImports, skipFlakyTest, skipUntrustedTest + +from test_mips_malta import mips_check_wheezy + + +class MaltaMachineConsole(LinuxKernelTest): + + 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_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 + """ + kernel_path = self.archive_extract( + self.ASSET_KERNEL_2_63_2, + member='boot/vmlinux-2.6.32-5-5kc-malta') + + self.set_machine('malta') + self.vm.set_console() + kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'console=ttyS0' + self.vm.add_args('-kernel', kernel_path, + '-append', kernel_command_line) + self.vm.launch() + console_pattern = 'Kernel command line: %s' % kernel_command_line + self.wait_for_console_pattern(console_pattern) + + 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_mips64el_malta_5KEc_cpio(self): + kernel_path = self.ASSET_KERNEL_3_19_3.fetch() + initrd_path = self.uncompress(self.ASSET_CPIO_R1) + + self.set_machine('malta') + self.vm.set_console() + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'console=ttyS0 console=tty ' + + 'rdinit=/sbin/init noreboot') + self.vm.add_args('-cpu', '5KEc', + '-kernel', kernel_path, + '-initrd', initrd_path, + '-append', kernel_command_line, + '-no-reboot') + self.vm.launch() + self.wait_for_console_pattern('Boot successful.') + + exec_command_and_wait_for_pattern(self, 'cat /proc/cpuinfo', + 'MIPS 5KE') + exec_command_and_wait_for_pattern(self, 'uname -a', + '3.19.3.mtoman.20150408') + exec_command_and_wait_for_pattern(self, 'reboot', + 'reboot: Restarting system') + # Wait for VM to shut down gracefully + self.vm.wait() + + ASSET_WHEEZY_KERNEL = Asset( + ('https://people.debian.org/~aurel32/qemu/mipsel/' + 'vmlinux-3.2.0-4-5kc-malta'), + '5e8b725244c59745bb8b64f5d8f49f25fecfa549f3395fb6d19a3b9e5065b85b') + + ASSET_WHEEZY_DISK = Asset( + ('https://people.debian.org/~aurel32/qemu/mipsel/' + 'debian_wheezy_mipsel_standard.qcow2'), + '454f09ae39f7e6461c84727b927100d2c7813841f2a0a5dce328114887ecf914') + + def test_wheezy(self): + kernel_path = self.ASSET_WHEEZY_KERNEL.fetch() + image_path = self.ASSET_WHEEZY_DISK.fetch() + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'console=ttyS0 root=/dev/sda1') + mips_check_wheezy(self, + kernel_path, image_path, kernel_command_line, cpuinfo='MIPS 20Kc', + dl_file='/boot/initrd.img-3.2.0-4-5kc-malta', + hsum='7579f8b56c1187c7c04d0dc3c0c56c7a6314c5ddd3a9bf8803ecc7cf8a3be9f8') + + +@skipIfMissingImports('numpy', 'cv2') +class MaltaMachineFramebuffer(LinuxKernelTest): + + timeout = 30 + + ASSET_KERNEL_4_7_0 = Asset( + ('https://github.com/philmd/qemu-testing-blob/raw/a5966ca4b5/' + 'mips/malta/mips64el/vmlinux-4.7.0-rc1.I6400.gz'), + '1f64efc59968a3c328672e6b10213fe574bb2308d9d2ed44e75e40be59e9fbc2') + + ASSET_TUXLOGO = Asset( + ('https://github.com/torvalds/linux/raw/v2.6.12/' + 'drivers/video/logo/logo_linux_vga16.ppm'), + 'b762f0d91ec018887ad1b334543c2fdf9be9fdfc87672b409211efaa3ea0ef79') + + def do_test_i6400_framebuffer_logo(self, cpu_cores_count): + """ + Boot Linux kernel and check Tux logo is displayed on the framebuffer. + """ + + import numpy as np + import cv2 + + screendump_path = self.scratch_file('screendump.pbm') + + kernel_path = self.uncompress(self.ASSET_KERNEL_4_7_0) + + tuxlogo_path = self.ASSET_TUXLOGO.fetch() + + self.set_machine('malta') + self.vm.set_console() + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'clocksource=GIC console=tty0 console=ttyS0') + self.vm.add_args('-kernel', kernel_path, + '-cpu', 'I6400', + '-smp', '%u' % cpu_cores_count, + '-vga', 'std', + '-append', kernel_command_line) + self.vm.launch() + framebuffer_ready = 'Console: switching to colour frame buffer device' + self.wait_for_console_pattern(framebuffer_ready) + self.vm.cmd('human-monitor-command', command_line='stop') + res = self.vm.cmd('human-monitor-command', + command_line='screendump %s' % screendump_path) + if 'unknown command' in res: + self.skipTest('screendump not available') + logger = logging.getLogger('framebuffer') + + match_threshold = 0.95 + screendump_bgr = cv2.imread(screendump_path, cv2.IMREAD_COLOR) + tuxlogo_bgr = cv2.imread(tuxlogo_path, cv2.IMREAD_COLOR) + result = cv2.matchTemplate(screendump_bgr, tuxlogo_bgr, + cv2.TM_CCOEFF_NORMED) + loc = np.where(result >= match_threshold) + tuxlogo_count = 0 + h, w = tuxlogo_bgr.shape[:2] + debug_png = os.getenv('QEMU_TEST_CV2_SCREENDUMP_PNG_PATH') + for tuxlogo_count, pt in enumerate(zip(*loc[::-1]), start=1): + logger.debug('found Tux at position (x, y) = %s', pt) + cv2.rectangle(screendump_bgr, pt, + (pt[0] + w, pt[1] + h), (0, 0, 255), 2) + if debug_png: + cv2.imwrite(debug_png, screendump_bgr) + self.assertGreaterEqual(tuxlogo_count, cpu_cores_count) + + def test_mips_malta_i6400_framebuffer_logo_1core(self): + self.do_test_i6400_framebuffer_logo(1) + + # XXX file tracking bug + @skipFlakyTest(bug_url=None) + def test_mips_malta_i6400_framebuffer_logo_7cores(self): + self.do_test_i6400_framebuffer_logo(7) + + @skipFlakyTest(bug_url=None) + def test_mips_malta_i6400_framebuffer_logo_8cores(self): + self.do_test_i6400_framebuffer_logo(8) + + +from test_mipsel_malta import MaltaMachineYAMON + +if __name__ == '__main__': + LinuxKernelTest.main() diff --git a/tests/functional/test_mips64el_replay.py b/tests/functional/test_mips64el_replay.py new file mode 100755 index 0000000..26a6ccf --- /dev/null +++ b/tests/functional/test_mips64el_replay.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python3 +# +# Replay tests for the little-endian 64-bit MIPS Malta board +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import Asset, 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_mips64el_tuxrun.py b/tests/functional/test_mips64el_tuxrun.py new file mode 100755 index 0000000..0a24757 --- /dev/null +++ b/tests/functional/test_mips64el_tuxrun.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python3 +# +# Functional test that boots known good tuxboot images the same way +# that tuxrun (www.tuxrun.org) does. This tool is used by things like +# the LKFT project to run regression tests on kernels. +# +# Copyright (c) 2023 Linaro Ltd. +# +# Author: +# Alex Bennée <alex.bennee@linaro.org> +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import Asset +from qemu_test.tuxruntest import TuxRunBaselineTest + +class TuxRunMips64ELTest(TuxRunBaselineTest): + + ASSET_MIPS64EL_KERNEL = Asset( + 'https://storage.tuxboot.com/buildroot/20241119/mips64el/vmlinux', + '0d2829a96f005229839c4cd586d4d8a136ea4b488d29821611c8e97f2266bfa9') + ASSET_MIPS64EL_ROOTFS = Asset( + 'https://storage.tuxboot.com/buildroot/20241119/mips64el/rootfs.ext4.zst', + '69c8b69a4f1582ce4c6f01a994968f5d73bffb2fc99cbeeeb26c8b5a28eaeb84') + + def test_mips64el(self): + self.set_machine('malta') + self.root="sda" + self.wait_for_shutdown=False + self.common_tuxrun(kernel_asset=self.ASSET_MIPS64EL_KERNEL, + rootfs_asset=self.ASSET_MIPS64EL_ROOTFS, + drive="driver=ide-hd,bus=ide.0,unit=0") + +if __name__ == '__main__': + TuxRunBaselineTest.main() diff --git a/tests/functional/test_mips_malta.py b/tests/functional/test_mips_malta.py new file mode 100755 index 0000000..30279f0 --- /dev/null +++ b/tests/functional/test_mips_malta.py @@ -0,0 +1,196 @@ +#!/usr/bin/env python3 +# +# Functional tests for the big-endian 32-bit MIPS Malta board +# +# Copyright (c) Philippe Mathieu-Daudé <f4bug@amsat.org> +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import os + +from qemu_test import LinuxKernelTest, Asset, wait_for_console_pattern +from qemu_test import exec_command_and_wait_for_pattern + + +def mips_run_common_commands(test, prompt='#'): + exec_command_and_wait_for_pattern(test, + 'uname -m', + 'mips') + exec_command_and_wait_for_pattern(test, + 'grep XT-PIC /proc/interrupts', + 'timer') + wait_for_console_pattern(test, prompt) + exec_command_and_wait_for_pattern(test, + 'grep XT-PIC /proc/interrupts', + 'serial') + wait_for_console_pattern(test, prompt) + exec_command_and_wait_for_pattern(test, + 'grep XT-PIC /proc/interrupts', + 'ata_piix') + wait_for_console_pattern(test, prompt) + exec_command_and_wait_for_pattern(test, + 'grep XT-PIC /proc/interrupts', + 'rtc') + wait_for_console_pattern(test, prompt) + exec_command_and_wait_for_pattern(test, + 'cat /proc/devices', + 'input') + wait_for_console_pattern(test, prompt) + exec_command_and_wait_for_pattern(test, + 'cat /proc/devices', + 'fb') + wait_for_console_pattern(test, prompt) + exec_command_and_wait_for_pattern(test, + 'cat /proc/ioports', + ' : serial') + wait_for_console_pattern(test, prompt) + exec_command_and_wait_for_pattern(test, + 'cat /proc/ioports', + ' : ata_piix') + wait_for_console_pattern(test, prompt) + +def mips_check_wheezy(test, kernel_path, image_path, kernel_command_line, + dl_file, hsum, nic='pcnet', cpuinfo='MIPS 24Kc'): + test.require_netdev('user') + test.require_device(nic) + test.set_machine('malta') + + port=8080 + test.vm.add_args('-kernel', kernel_path, + '-append', kernel_command_line, + '-drive', 'file=%s,snapshot=on' % image_path, + '-netdev', 'user,id=n1' + + ',tftp=' + os.path.basename(kernel_path) + + ',hostfwd=tcp:127.0.0.1:0-:%d' % port, + '-device', f'{nic},netdev=n1', + '-no-reboot') + test.vm.set_console() + test.vm.launch() + + wait_for_console_pattern(test, 'login: ', 'Oops') + exec_command_and_wait_for_pattern(test, 'root', 'Password:') + exec_command_and_wait_for_pattern(test, 'root', ':~# ') + mips_run_common_commands(test) + + exec_command_and_wait_for_pattern(test, 'cd /', '# ') + test.check_http_download(dl_file, hsum, port, + pythoncmd='python -m SimpleHTTPServer') + + exec_command_and_wait_for_pattern(test, 'cat /proc/cpuinfo', cpuinfo) + exec_command_and_wait_for_pattern(test, 'cat /proc/devices', 'usb') + exec_command_and_wait_for_pattern(test, 'cat /proc/ioports', + ' : piix4_smbus') + exec_command_and_wait_for_pattern(test, 'lspci -d 11ab:4620', + 'GT-64120') + exec_command_and_wait_for_pattern(test, + 'cat /sys/bus/i2c/devices/i2c-0/name', + 'SMBus PIIX4 adapter') + exec_command_and_wait_for_pattern(test, 'cat /proc/mtd', 'YAMON') + # Empty 'Board Config' (64KB) + exec_command_and_wait_for_pattern(test, 'md5sum /dev/mtd2ro', + '0dfbe8aa4c20b52e1b8bf3cb6cbdf193') + + +class MaltaMachineConsole(LinuxKernelTest): + + 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_mips_malta(self): + kernel_path = self.archive_extract( + self.ASSET_KERNEL_2_63_2, + member='boot/vmlinux-2.6.32-5-4kc-malta') + + self.set_machine('malta') + self.vm.set_console() + kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'console=ttyS0' + self.vm.add_args('-kernel', kernel_path, + '-append', kernel_command_line) + self.vm.launch() + console_pattern = 'Kernel command line: %s' % kernel_command_line + self.wait_for_console_pattern(console_pattern) + + 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') + + def test_mips_malta_cpio(self): + self.require_netdev('user') + self.set_machine('malta') + self.require_device('pcnet') + + 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) + + self.vm.set_console() + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'console=ttyS0 console=tty ' + + 'rdinit=/sbin/init noreboot') + self.vm.add_args('-kernel', kernel_path, + '-initrd', initrd_path, + '-append', kernel_command_line, + '-netdev', 'user,id=n1,tftp=' + self.scratch_file('boot'), + '-device', 'pcnet,netdev=n1', + '-no-reboot') + self.vm.launch() + self.wait_for_console_pattern('Boot successful.') + + exec_command_and_wait_for_pattern(self, 'cat /proc/cpuinfo', + 'BogoMIPS') + exec_command_and_wait_for_pattern(self, 'uname -a', + '4.5.0-2-4kc-malta #1 Debian') + mips_run_common_commands(self) + + exec_command_and_wait_for_pattern(self, 'ip link set eth0 up', + 'eth0: link up') + exec_command_and_wait_for_pattern(self, + 'ip addr add 10.0.2.15 dev eth0', + '#') + exec_command_and_wait_for_pattern(self, 'route add default eth0', '#') + exec_command_and_wait_for_pattern(self, + 'tftp -g -r vmlinux-4.5.0-2-4kc-malta 10.0.2.2', '#') + exec_command_and_wait_for_pattern(self, + 'md5sum vmlinux-4.5.0-2-4kc-malta', + 'a98218a7efbdefb2dfdf9ecd08c98318') + + exec_command_and_wait_for_pattern(self, 'reboot', + 'reboot: Restarting system') + # Wait for VM to shut down gracefully + self.vm.wait() + + ASSET_WHEEZY_KERNEL = Asset( + ('https://people.debian.org/~aurel32/qemu/mips/' + 'vmlinux-3.2.0-4-4kc-malta'), + '0377fcda31299213c10b8e5babe7260ef99188b3ae1aca6f56594abb71e7f67e') + + ASSET_WHEEZY_DISK = Asset( + ('https://people.debian.org/~aurel32/qemu/mips/' + 'debian_wheezy_mips_standard.qcow2'), + 'de03599285b8382ad309309a6c4869f6c6c42a5cfc983342bab9ec0dfa7849a2') + + def test_wheezy(self): + kernel_path = self.ASSET_WHEEZY_KERNEL.fetch() + image_path = self.ASSET_WHEEZY_DISK.fetch() + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'console=ttyS0 root=/dev/sda1') + mips_check_wheezy(self, + kernel_path, image_path, kernel_command_line, nic='e1000', + dl_file='/boot/initrd.img-3.2.0-4-4kc-malta', + hsum='ff0c0369143d9bbb9a6e6bc79322a2be535619df639e84103237f406e87493dc') + + +if __name__ == '__main__': + LinuxKernelTest.main() diff --git a/tests/functional/test_mips_replay.py b/tests/functional/test_mips_replay.py new file mode 100755 index 0000000..4327481 --- /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 +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_mips_tuxrun.py b/tests/functional/test_mips_tuxrun.py new file mode 100755 index 0000000..6771dbd --- /dev/null +++ b/tests/functional/test_mips_tuxrun.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python3 +# +# Functional test that boots known good tuxboot images the same way +# that tuxrun (www.tuxrun.org) does. This tool is used by things like +# the LKFT project to run regression tests on kernels. +# +# Copyright (c) 2023 Linaro Ltd. +# +# Author: +# Alex Bennée <alex.bennee@linaro.org> +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import Asset +from qemu_test.tuxruntest import TuxRunBaselineTest + +class TuxRunMipsTest(TuxRunBaselineTest): + + ASSET_MIPS_KERNEL = Asset( + 'https://storage.tuxboot.com/buildroot/20241119/mips32/vmlinux', + 'b6f97fc698ae8c96456ad8c996c7454228074df0d7520dedd0a15e2913700a19') + ASSET_MIPS_ROOTFS = Asset( + 'https://storage.tuxboot.com/buildroot/20241119/mips32/rootfs.ext4.zst', + '87055cf3cbde3fd134e5039e7b87feb03231d8c4b21ee712b8ba3308dfa72f50') + + def test_mips32(self): + self.set_machine('malta') + self.cpu="mips32r6-generic" + self.root="sda" + self.wait_for_shutdown=False + self.common_tuxrun(kernel_asset=self.ASSET_MIPS_KERNEL, + rootfs_asset=self.ASSET_MIPS_ROOTFS, + drive="driver=ide-hd,bus=ide.0,unit=0") + +if __name__ == '__main__': + TuxRunBaselineTest.main() diff --git a/tests/functional/test_mipsel_malta.py b/tests/functional/test_mipsel_malta.py new file mode 100755 index 0000000..9ee2884 --- /dev/null +++ b/tests/functional/test_mipsel_malta.py @@ -0,0 +1,108 @@ +#!/usr/bin/env python3 +# +# Functional tests for the little-endian 32-bit MIPS Malta board +# +# Copyright (c) Philippe Mathieu-Daudé <f4bug@amsat.org> +# +# This work is licensed under the terms of the GNU GPL, version 2 or later. +# See the COPYING file in the top-level directory. +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import QemuSystemTest, LinuxKernelTest, Asset +from qemu_test import interrupt_interactive_console_until_pattern +from qemu_test import wait_for_console_pattern + +from test_mips_malta import mips_check_wheezy + + +class MaltaMachineConsole(LinuxKernelTest): + + 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_mips_malta32el_nanomips(self, kernel): + kernel_path = self.uncompress(kernel) + + self.set_machine('malta') + self.vm.set_console() + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'mem=256m@@0x0 ' + + 'console=ttyS0') + self.vm.add_args('-cpu', 'I7200', + '-no-reboot', + '-kernel', kernel_path, + '-append', kernel_command_line) + self.vm.launch() + console_pattern = 'Kernel command line: %s' % kernel_command_line + self.wait_for_console_pattern(console_pattern) + + def test_mips_malta32el_nanomips_4k(self): + self.do_test_mips_malta32el_nanomips(self.ASSET_KERNEL_4K) + + def test_mips_malta32el_nanomips_16k_up(self): + self.do_test_mips_malta32el_nanomips(self.ASSET_KERNEL_16K) + + def test_mips_malta32el_nanomips_64k_dbg(self): + self.do_test_mips_malta32el_nanomips(self.ASSET_KERNEL_64K) + + ASSET_WHEEZY_KERNEL = Asset( + ('https://people.debian.org/~aurel32/qemu/mipsel/' + 'vmlinux-3.2.0-4-4kc-malta'), + 'dc8a3648305b0201ca7a5cd135fe2890067a65d93c38728022bb0e656ad2bf9a') + + ASSET_WHEEZY_DISK = Asset( + ('https://people.debian.org/~aurel32/qemu/mipsel/' + 'debian_wheezy_mipsel_standard.qcow2'), + '454f09ae39f7e6461c84727b927100d2c7813841f2a0a5dce328114887ecf914') + + def test_wheezy(self): + kernel_path = self.ASSET_WHEEZY_KERNEL.fetch() + image_path = self.ASSET_WHEEZY_DISK.fetch() + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'console=ttyS0 root=/dev/sda1') + mips_check_wheezy(self, + kernel_path, image_path, kernel_command_line, + dl_file='/boot/initrd.img-3.2.0-4-4kc-malta', + hsum='9fc9f250ed56a74e35e704ddfd5a1c5a5625adefc5c9da91f649288d3ca000f0') + + +class MaltaMachineYAMON(QemuSystemTest): + + ASSET_YAMON_ROM = Asset( + ('https://s3-eu-west-1.amazonaws.com/downloads-mips/mips-downloads/' + 'YAMON/yamon-bin-02.22.zip'), + 'eef86f0eed0ef554f041dcd47b87eebea0e6f9f1184ed31f7e9e8b4a803860ab') + + def test_mipsel_malta_yamon(self): + yamon_bin = 'yamon-02.22.bin' + self.archive_extract(self.ASSET_YAMON_ROM) + yamon_path = self.scratch_file(yamon_bin) + + self.set_machine('malta') + self.vm.set_console() + self.vm.add_args('-bios', yamon_path) + self.vm.launch() + + prompt = 'YAMON>' + pattern = 'YAMON ROM Monitor' + interrupt_interactive_console_until_pattern(self, pattern, prompt) + wait_for_console_pattern(self, prompt) + self.vm.shutdown() + + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/functional/test_mipsel_replay.py b/tests/functional/test_mipsel_replay.py new file mode 100644 index 0000000..5f4796c --- /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, 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_mipsel_tuxrun.py b/tests/functional/test_mipsel_tuxrun.py new file mode 100755 index 0000000..d4b39ba --- /dev/null +++ b/tests/functional/test_mipsel_tuxrun.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python3 +# +# Functional test that boots known good tuxboot images the same way +# that tuxrun (www.tuxrun.org) does. This tool is used by things like +# the LKFT project to run regression tests on kernels. +# +# Copyright (c) 2023 Linaro Ltd. +# +# Author: +# Alex Bennée <alex.bennee@linaro.org> +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import Asset +from qemu_test.tuxruntest import TuxRunBaselineTest + +class TuxRunMipsELTest(TuxRunBaselineTest): + + ASSET_MIPSEL_KERNEL = Asset( + 'https://storage.tuxboot.com/buildroot/20241119/mips32el/vmlinux', + '660dd8c7a6ca7a32d37b4e6348865532ab0edb66802e8cc07869338444cf4929') + ASSET_MIPSEL_ROOTFS = Asset( + 'https://storage.tuxboot.com/buildroot/20241119/mips32el/rootfs.ext4.zst', + 'c5d69542bcaed54a4f34671671eb4be5c608ee02671d4d0436544367816a73b1') + + def test_mips32el(self): + self.set_machine('malta') + self.cpu="mips32r6-generic" + self.root="sda" + self.wait_for_shutdown=False + self.common_tuxrun(kernel_asset=self.ASSET_MIPSEL_KERNEL, + rootfs_asset=self.ASSET_MIPSEL_ROOTFS, + drive="driver=ide-hd,bus=ide.0,unit=0") + +if __name__ == '__main__': + TuxRunBaselineTest.main() diff --git a/tests/functional/test_multiprocess.py b/tests/functional/test_multiprocess.py new file mode 100755 index 0000000..751cf10 --- /dev/null +++ b/tests/functional/test_multiprocess.py @@ -0,0 +1,100 @@ +#!/usr/bin/env python3 +# +# Test for multiprocess qemu +# +# 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 socket + +from qemu_test import QemuSystemTest, Asset, wait_for_console_pattern +from qemu_test import exec_command, exec_command_and_wait_for_pattern + +class Multiprocess(QemuSystemTest): + + KERNEL_COMMON_COMMAND_LINE = 'printk.time=0 ' + + ASSET_KERNEL_X86 = Asset( + ('https://archives.fedoraproject.org/pub/archive/fedora/linux' + '/releases/31/Everything/x86_64/os/images/pxeboot/vmlinuz'), + 'd4738d03dbbe083ca610d0821d0a8f1488bebbdccef54ce33e3adb35fda00129') + + ASSET_INITRD_X86 = Asset( + ('https://archives.fedoraproject.org/pub/archive/fedora/linux' + '/releases/31/Everything/x86_64/os/images/pxeboot/initrd.img'), + '3b6cb5c91a14c42e2f61520f1689264d865e772a1f0069e660a800d31dd61fb9') + + ASSET_KERNEL_AARCH64 = Asset( + ('https://archives.fedoraproject.org/pub/archive/fedora/linux' + '/releases/31/Everything/aarch64/os/images/pxeboot/vmlinuz'), + '3ae07fcafbfc8e4abeb693035a74fe10698faae15e9ccd48882a9167800c1527') + + ASSET_INITRD_AARCH64 = Asset( + ('https://archives.fedoraproject.org/pub/archive/fedora/linux' + '/releases/31/Everything/aarch64/os/images/pxeboot/initrd.img'), + '9fd230cab10b1dafea41cf00150e6669d37051fad133bd618d2130284e16d526') + + def do_test(self, kernel_asset, initrd_asset, + kernel_command_line, machine_type): + """Main test method""" + self.require_accelerator('kvm') + self.require_device('x-pci-proxy-dev') + + # Create socketpair to connect proxy and remote processes + proxy_sock, remote_sock = socket.socketpair(socket.AF_UNIX, + socket.SOCK_STREAM) + os.set_inheritable(proxy_sock.fileno(), True) + os.set_inheritable(remote_sock.fileno(), True) + + kernel_path = kernel_asset.fetch() + initrd_path = initrd_asset.fetch() + + # Create remote process + remote_vm = self.get_vm() + remote_vm.add_args('-machine', 'x-remote') + remote_vm.add_args('-nodefaults') + remote_vm.add_args('-device', 'lsi53c895a,id=lsi1') + remote_vm.add_args('-object', 'x-remote-object,id=robj1,' + 'devid=lsi1,fd='+str(remote_sock.fileno())) + remote_vm.launch() + + # Create proxy process + self.vm.set_console() + self.vm.add_args('-machine', machine_type) + self.vm.add_args('-accel', 'kvm') + self.vm.add_args('-cpu', 'host') + self.vm.add_args('-object', + 'memory-backend-memfd,id=sysmem-file,size=2G') + self.vm.add_args('--numa', 'node,memdev=sysmem-file') + self.vm.add_args('-m', '2048') + self.vm.add_args('-kernel', kernel_path, + '-initrd', initrd_path, + '-append', kernel_command_line) + self.vm.add_args('-device', + 'x-pci-proxy-dev,' + 'id=lsi1,fd='+str(proxy_sock.fileno())) + self.vm.launch() + wait_for_console_pattern(self, 'as init process', + 'Kernel panic - not syncing') + exec_command(self, 'mount -t sysfs sysfs /sys') + exec_command_and_wait_for_pattern(self, + 'cat /sys/bus/pci/devices/*/uevent', + 'PCI_ID=1000:0012') + + def test_multiprocess(self): + kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + if self.arch == 'x86_64': + kernel_command_line += 'console=ttyS0 rdinit=/bin/bash' + self.do_test(self.ASSET_KERNEL_X86, self.ASSET_INITRD_X86, + kernel_command_line, 'pc') + elif self.arch == 'aarch64': + kernel_command_line += 'rdinit=/bin/bash console=ttyAMA0' + self.do_test(self.ASSET_KERNEL_AARCH64, self.ASSET_INITRD_AARCH64, + kernel_command_line, 'virt,gic-version=3') + else: + assert False + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/functional/test_netdev_ethtool.py b/tests/functional/test_netdev_ethtool.py index d5b911c..ee1a397 100755 --- a/tests/functional/test_netdev_ethtool.py +++ b/tests/functional/test_netdev_ethtool.py @@ -5,7 +5,7 @@ # This test leverages ethtool's --test sequence to validate network # device behaviour. # -# SPDX-License-Identifier: GPL-2.0-or-late +# SPDX-License-Identifier: GPL-2.0-or-later from unittest import skip from qemu_test import QemuSystemTest, Asset diff --git a/tests/functional/test_or1k_replay.py b/tests/functional/test_or1k_replay.py new file mode 100755 index 0000000..2b60a93 --- /dev/null +++ b/tests/functional/test_or1k_replay.py @@ -0,0 +1,27 @@ +#!/usr/bin/env python3 +# +# Replay test that boots a Linux kernel on an OpenRISC-1000 SIM machine +# and checks the console +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import Asset +from replay_kernel import ReplayKernelBase + + +class Or1kReplay(ReplayKernelBase): + + ASSET_DAY20 = Asset( + 'https://qemu-advcal.gitlab.io/qac-best-of-multiarch/download/day20.tar.xz', + 'ff9d7dd7c6bdba325bd85ee85c02db61ff653e129558aeffe6aff55bffb6763a') + + def test_sim(self): + self.set_machine('or1k-sim') + kernel_path = self.archive_extract(self.ASSET_DAY20, + member='day20/vmlinux') + self.run_rr(kernel_path, self.REPLAY_KERNEL_COMMAND_LINE, + 'QEMU advent calendar') + + +if __name__ == '__main__': + ReplayKernelBase.main() diff --git a/tests/functional/test_or1k_sim.py b/tests/functional/test_or1k_sim.py new file mode 100755 index 0000000..f9f0b69 --- /dev/null +++ b/tests/functional/test_or1k_sim.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python3 +# +# Functional test that boots a Linux kernel on an OpenRISC-1000 SIM machine +# and checks the console +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import LinuxKernelTest, Asset + + +class OpenRISC1kSimTest(LinuxKernelTest): + + ASSET_DAY20 = Asset( + 'https://qemu-advcal.gitlab.io/qac-best-of-multiarch/download/day20.tar.xz', + 'ff9d7dd7c6bdba325bd85ee85c02db61ff653e129558aeffe6aff55bffb6763a') + + def test_or1k_sim(self): + self.set_machine('or1k-sim') + self.archive_extract(self.ASSET_DAY20) + self.vm.set_console() + self.vm.add_args('-kernel', self.scratch_file('day20', 'vmlinux')) + self.vm.launch() + self.wait_for_console_pattern('QEMU advent calendar') + +if __name__ == '__main__': + LinuxKernelTest.main() diff --git a/tests/functional/test_pc_cpu_hotplug_props.py b/tests/functional/test_pc_cpu_hotplug_props.py index 9d5a37c..2bed8ad 100755 --- a/tests/functional/test_pc_cpu_hotplug_props.py +++ b/tests/functional/test_pc_cpu_hotplug_props.py @@ -26,6 +26,7 @@ from qemu_test import QemuSystemTest class OmittedCPUProps(QemuSystemTest): def test_no_die_id(self): + self.set_machine('pc') self.vm.add_args('-nodefaults', '-S') self.vm.add_args('-smp', '1,sockets=2,cores=2,threads=2,maxcpus=8') self.vm.add_args('-device', 'qemu64-x86_64-cpu,socket-id=1,core-id=0,thread-id=0') diff --git a/tests/functional/test_ppc64_e500.py b/tests/functional/test_ppc64_e500.py new file mode 100755 index 0000000..f5fcad9 --- /dev/null +++ b/tests/functional/test_ppc64_e500.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python3 +# +# Boot a Linux kernel on a e500 ppc64 machine and check the console +# +# 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 E500Test(LinuxKernelTest): + + ASSET_BR2_E5500_UIMAGE = Asset( + 'https://github.com/legoater/qemu-ppc-boot/raw/refs/heads/main/buildroot/qemu_ppc64_e5500-2023.11-8-gdcd9f0f6eb-20240104/uImage', + '2478187c455d6cca3984e9dfde9c635d824ea16236b85fd6b4809f744706deda') + + ASSET_BR2_E5500_ROOTFS = Asset( + 'https://github.com/legoater/qemu-ppc-boot/raw/refs/heads/main//buildroot/qemu_ppc64_e5500-2023.11-8-gdcd9f0f6eb-20240104/rootfs.ext2', + '9035ef97237c84c7522baaff17d25cdfca4bb7a053d5e296e902919473423d76') + + def test_ppc64_e500_buildroot(self): + self.set_machine('ppce500') + self.require_netdev('user') + self.cpu = 'e5500' + + uimage_path = self.ASSET_BR2_E5500_UIMAGE.fetch() + rootfs_path = self.ASSET_BR2_E5500_ROOTFS.fetch() + + self.vm.set_console() + self.vm.add_args('-kernel', uimage_path, + '-append', 'root=/dev/vda', + '-drive', f'file={rootfs_path},if=virtio,format=raw', + '-snapshot', '-no-shutdown') + self.vm.launch() + + self.wait_for_console_pattern('Linux version') + self.wait_for_console_pattern('/init as init process') + self.wait_for_console_pattern('lease of 10.0.2.15') + 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_ppc64_hv.py b/tests/functional/test_ppc64_hv.py index 1a6e4b6..d87f440 100755 --- a/tests/functional/test_ppc64_hv.py +++ b/tests/functional/test_ppc64_hv.py @@ -9,34 +9,14 @@ # 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 unittest import skipIf, skipUnless -from qemu_test import QemuSystemTest, Asset -from qemu_test import wait_for_console_pattern, exec_command import os -import time import subprocess -from datetime import datetime -deps = ["xorriso"] # dependent tools needed in the test setup/box. - -def which(tool): - """ looks up the full path for @tool, returns None if not found - or if @tool does not have executable permissions. - """ - paths=os.getenv('PATH') - for p in paths.split(os.path.pathsep): - p = os.path.join(p, tool) - if os.path.exists(p) and os.access(p, os.X_OK): - return p - return None - -def missing_deps(): - """ returns True if any of the test dependent tools are absent. - """ - for dep in deps: - if which(dep) is None: - return True - return False +from datetime import datetime +from qemu_test import QemuSystemTest, Asset +from qemu_test import wait_for_console_pattern, exec_command +from qemu_test import skipIfMissingCommands, skipBigDataTest +from qemu_test import exec_command_and_wait_for_pattern # Alpine is a light weight distro that supports QEMU. These tests boot # that on the machine then run a QEMU guest inside it in KVM mode, @@ -45,8 +25,8 @@ def missing_deps(): # large download, but it may be more polite to create qcow2 image with # QEMU already installed and use that. # XXX: The order of these tests seems to matter, see git blame. -@skipIf(missing_deps(), 'dependencies (%s) not installed' % ','.join(deps)) -@skipUnless(os.getenv('QEMU_TEST_ALLOW_LARGE_STORAGE'), 'storage limited') +@skipIfMissingCommands("xorriso") +@skipBigDataTest() class HypervisorTest(QemuSystemTest): timeout = 1000 @@ -55,9 +35,9 @@ class HypervisorTest(QemuSystemTest): good_message = 'VFS: Cannot open root device' ASSET_ISO = Asset( - ('https://dl-cdn.alpinelinux.org/alpine/v3.18/' - 'releases/ppc64le/alpine-standard-3.18.4-ppc64le.iso'), - 'c26b8d3e17c2f3f0fed02b4b1296589c2390e6d5548610099af75300edd7b3ff') + ('https://dl-cdn.alpinelinux.org/alpine/v3.21/' + 'releases/ppc64le/alpine-standard-3.21.0-ppc64le.iso'), + '7651ab4e3027604535c0b36e86c901b4695bf8fe97b908f5b48590f6baae8f30') def extract_from_iso(self, iso, path): """ @@ -67,24 +47,15 @@ class HypervisorTest(QemuSystemTest): :param path: path within the iso file of the file to be extracted :returns: path of the extracted file """ - filename = os.path.basename(path) + filename = self.scratch_file(os.path.basename(path)) - cwd = os.getcwd() - os.chdir(self.workdir) - - with open(filename, "w") as outfile: - cmd = "xorriso -osirrox on -indev %s -cpx %s %s" % (iso, path, filename) - subprocess.run(cmd.split(), - stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + cmd = "xorriso -osirrox on -indev %s -cpx %s %s" % (iso, path, filename) + subprocess.run(cmd.split(), + stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) os.chmod(filename, 0o600) - os.chdir(cwd) - # Return complete path to extracted file. Because callers to - # extract_from_iso() specify 'path' with a leading slash, it is - # necessary to use os.path.relpath() as otherwise os.path.join() - # interprets it as an absolute path and drops the self.workdir part. - return os.path.normpath(os.path.join(self.workdir, filename)) + return filename def setUp(self): super().setUp() @@ -99,34 +70,33 @@ class HypervisorTest(QemuSystemTest): self.vm.add_args("-kernel", self.vmlinuz) self.vm.add_args("-initrd", self.initramfs) self.vm.add_args("-smp", "4", "-m", "2g") - self.vm.add_args("-drive", f"file={self.iso_path},format=raw,if=none,id=drive0") + self.vm.add_args("-drive", f"file={self.iso_path},format=raw,if=none," + "id=drive0,read-only=true") self.vm.launch() - wait_for_console_pattern(self, 'Welcome to Alpine Linux 3.18') - exec_command(self, 'root') + ps1='localhost:~#' wait_for_console_pattern(self, 'localhost login:') - wait_for_console_pattern(self, 'You may change this message by editing /etc/motd.') + exec_command_and_wait_for_pattern(self, 'root', ps1) # If the time is wrong, SSL certificates can fail. - exec_command(self, 'date -s "' + datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S' + '"')) - exec_command(self, 'setup-alpine -qe') - wait_for_console_pattern(self, 'Updating repository indexes... done.') + exec_command_and_wait_for_pattern(self, 'date -s "' + datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S' + '"'), ps1) + ps1='alpine:~#' + exec_command_and_wait_for_pattern(self, 'setup-alpine -qe', ps1) + exec_command_and_wait_for_pattern(self, 'setup-apkrepos -c1', ps1) + exec_command_and_wait_for_pattern(self, 'apk update', ps1) + # Could upgrade here but it usually should not be necessary + # exec_command_and_wait_for_pattern(self, 'apk upgrade --available', ps1) def do_stop_alpine(self): - exec_command(self, 'poweroff') + exec_command(self, 'echo "TEST ME"') wait_for_console_pattern(self, 'alpine:~#') + exec_command(self, 'poweroff') + wait_for_console_pattern(self, 'reboot: Power down') self.vm.wait() def do_setup_kvm(self): - exec_command(self, 'echo http://dl-cdn.alpinelinux.org/alpine/v3.18/main > /etc/apk/repositories') - wait_for_console_pattern(self, 'alpine:~#') - exec_command(self, 'echo http://dl-cdn.alpinelinux.org/alpine/v3.18/community >> /etc/apk/repositories') - wait_for_console_pattern(self, 'alpine:~#') - exec_command(self, 'apk update') - wait_for_console_pattern(self, 'alpine:~#') - exec_command(self, 'apk add qemu-system-ppc64') - wait_for_console_pattern(self, 'alpine:~#') - exec_command(self, 'modprobe kvm-hv') - wait_for_console_pattern(self, 'alpine:~#') + ps1='alpine:~#' + exec_command_and_wait_for_pattern(self, 'apk add qemu-system-ppc64', ps1) + exec_command_and_wait_for_pattern(self, 'modprobe kvm-hv', ps1) # This uses the host's block device as the source file for guest block # device for install media. This is a bit hacky but allows reuse of the @@ -144,20 +114,18 @@ class HypervisorTest(QemuSystemTest): '-initrd /media/nvme0n1/boot/initramfs-lts ' '-kernel /media/nvme0n1/boot/vmlinuz-lts ' '-append \'usbcore.nousb ' + append + '\'') - # Alpine 3.18 kernel seems to crash in XHCI USB driver. - wait_for_console_pattern(self, 'Welcome to Alpine Linux 3.18') - exec_command(self, 'root') + # Alpine 3.21 kernel seems to crash in XHCI USB driver. + ps1='localhost:~#' wait_for_console_pattern(self, 'localhost login:') - wait_for_console_pattern(self, 'You may change this message by editing /etc/motd.') - exec_command(self, 'poweroff >& /dev/null') - wait_for_console_pattern(self, 'localhost:~#') + exec_command_and_wait_for_pattern(self, 'root', ps1) + exec_command(self, 'poweroff') wait_for_console_pattern(self, 'reboot: Power down') - time.sleep(1) - exec_command(self, '') + # Now wait for the host's prompt to come back wait_for_console_pattern(self, 'alpine:~#') def test_hv_pseries(self): self.require_accelerator("tcg") + self.require_netdev('user') self.set_machine('pseries') self.vm.add_args("-accel", "tcg,thread=multi") self.vm.add_args('-device', 'nvme,serial=1234,drive=drive0') @@ -169,6 +137,7 @@ class HypervisorTest(QemuSystemTest): def test_hv_pseries_kvm(self): self.require_accelerator("kvm") + self.require_netdev('user') self.set_machine('pseries') self.vm.add_args("-accel", "kvm") self.vm.add_args('-device', 'nvme,serial=1234,drive=drive0') @@ -180,6 +149,7 @@ class HypervisorTest(QemuSystemTest): def test_hv_powernv(self): self.require_accelerator("tcg") + self.require_netdev('user') self.set_machine('powernv') self.vm.add_args("-accel", "tcg,thread=multi") self.vm.add_args('-device', 'nvme,bus=pcie.2,addr=0x0,serial=1234,drive=drive0', diff --git a/tests/functional/test_ppc64_mac99.py b/tests/functional/test_ppc64_mac99.py new file mode 100755 index 0000000..dfd9c01 --- /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_ppc64_powernv.py b/tests/functional/test_ppc64_powernv.py index 67497d6..685e217 100755 --- a/tests/functional/test_ppc64_powernv.py +++ b/tests/functional/test_ppc64_powernv.py @@ -7,10 +7,10 @@ # 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 qemu_test import QemuSystemTest, Asset +from qemu_test import LinuxKernelTest, Asset from qemu_test import wait_for_console_pattern -class powernvMachine(QemuSystemTest): +class powernvMachine(LinuxKernelTest): timeout = 90 KERNEL_COMMON_COMMAND_LINE = 'printk.time=0 console=hvc0 ' @@ -78,5 +78,41 @@ class powernvMachine(QemuSystemTest): wait_for_console_pattern(self, console_pattern, self.panic_message) wait_for_console_pattern(self, self.good_message, self.panic_message) + + ASSET_EPAPR_KERNEL = Asset( + ('https://github.com/open-power/op-build/releases/download/v2.7/' + 'zImage.epapr'), + '0ab237df661727e5392cee97460e8674057a883c5f74381a128fa772588d45cd') + + def do_test_ppc64_powernv(self, proc): + self.require_accelerator("tcg") + kernel_path = self.ASSET_EPAPR_KERNEL.fetch() + self.vm.set_console() + self.vm.add_args('-kernel', kernel_path, + '-append', 'console=tty0 console=hvc0', + '-device', 'pcie-pci-bridge,id=bridge1,bus=pcie.1,addr=0x0', + '-device', 'nvme,bus=pcie.2,addr=0x0,serial=1234', + '-device', 'e1000e,bus=bridge1,addr=0x3', + '-device', 'nec-usb-xhci,bus=bridge1,addr=0x2') + self.vm.launch() + + self.wait_for_console_pattern("CPU: " + proc + " generation processor") + self.wait_for_console_pattern("zImage starting: loaded") + self.wait_for_console_pattern("Run /init as init process") + # Device detection output driven by udev probing is sometimes cut off + # from console output, suspect S14silence-console init script. + + def test_powernv8(self): + self.set_machine('powernv8') + self.do_test_ppc64_powernv('P8') + + def test_powernv9(self): + self.set_machine('powernv9') + self.do_test_ppc64_powernv('P9') + + def test_powernv10(self): + self.set_machine('powernv10') + self.do_test_ppc64_powernv('P10') + if __name__ == '__main__': - QemuSystemTest.main() + LinuxKernelTest.main() diff --git a/tests/functional/test_ppc64_pseries.py b/tests/functional/test_ppc64_pseries.py index fdc404e..6705793 100755 --- a/tests/functional/test_ppc64_pseries.py +++ b/tests/functional/test_ppc64_pseries.py @@ -63,6 +63,7 @@ class pseriesMachine(QemuSystemTest): wait_for_console_pattern(self, self.good_message, self.panic_message) def test_ppc64_linux_smt_boot(self): + self.set_machine('pseries') self.vm.add_args('-smp', '4,threads=4') self.do_test_ppc64_linux_boot() console_pattern = 'CPU maps initialized for 4 threads per core' diff --git a/tests/functional/test_ppc64_replay.py b/tests/functional/test_ppc64_replay.py new file mode 100755 index 0000000..e8c9c4b --- /dev/null +++ b/tests/functional/test_ppc64_replay.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python3 +# +# Replay test that boots a Linux kernel on ppc64 machines +# and checks the console +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import Asset, skipFlakyTest +from replay_kernel import ReplayKernelBase + + +class Ppc64Replay(ReplayKernelBase): + + ASSET_DAY19 = Asset( + ('https://qemu-advcal.gitlab.io/qac-best-of-multiarch/download/' + 'day19.tar.xz'), + '20b1bb5a8488c664defbb5d283addc91a05335a936c63b3f5ff7eee74b725755') + + @skipFlakyTest('https://gitlab.com/qemu-project/qemu/-/issues/2523') + def test_ppc64_e500(self): + self.set_machine('ppce500') + self.cpu = 'e5500' + kernel_path = self.archive_extract(self.ASSET_DAY19, + member='day19/uImage') + self.run_rr(kernel_path, self.REPLAY_KERNEL_COMMAND_LINE, + 'QEMU advent calendar') + + ASSET_KERNEL = Asset( + ('https://archives.fedoraproject.org/pub/archive/fedora-secondary/' + 'releases/29/Everything/ppc64le/os/ppc/ppc64/vmlinuz'), + '383c2f5c23bc0d9d32680c3924d3fd7ee25cc5ef97091ac1aa5e1d853422fc5f') + + def test_ppc64_pseries(self): + self.set_machine('pseries') + kernel_path = self.ASSET_KERNEL.fetch() + kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'console=hvc0' + console_pattern = 'VFS: Cannot open root device' + self.run_rr(kernel_path, kernel_command_line, console_pattern) + + def test_ppc64_powernv(self): + self.set_machine('powernv') + kernel_path = self.ASSET_KERNEL.fetch() + kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + \ + 'console=tty0 console=hvc0' + console_pattern = 'VFS: Cannot open root device' + self.run_rr(kernel_path, kernel_command_line, console_pattern) + + +if __name__ == '__main__': + ReplayKernelBase.main() diff --git a/tests/functional/test_ppc64_reverse_debug.py b/tests/functional/test_ppc64_reverse_debug.py new file mode 100755 index 0000000..5931ade --- /dev/null +++ b/tests/functional/test_ppc64_reverse_debug.py @@ -0,0 +1,41 @@ +#!/usr/bin/env python3 +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Reverse debugging test +# +# Copyright (c) 2020 ISP RAS +# +# Author: +# Pavel Dovgalyuk <Pavel.Dovgalyuk@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. + +from qemu_test import skipIfMissingImports, skipFlakyTest +from reverse_debugging import ReverseDebugging + + +@skipIfMissingImports('avocado.utils') +class ReverseDebugging_ppc64(ReverseDebugging): + + REG_PC = 0x40 + + @skipFlakyTest("https://gitlab.com/qemu-project/qemu/-/issues/1992") + def test_ppc64_pseries(self): + self.set_machine('pseries') + # SLOF branches back to its entry point, which causes this test + # to take the 'hit a breakpoint again' path. That's not a problem, + # just slightly different than the other machines. + self.endian_is_le = False + self.reverse_debugging() + + @skipFlakyTest("https://gitlab.com/qemu-project/qemu/-/issues/1992") + def test_ppc64_powernv(self): + self.set_machine('powernv') + self.endian_is_le = False + self.reverse_debugging() + + +if __name__ == '__main__': + ReverseDebugging.main() diff --git a/tests/functional/test_ppc64_tuxrun.py b/tests/functional/test_ppc64_tuxrun.py new file mode 100755 index 0000000..e8f79c6 --- /dev/null +++ b/tests/functional/test_ppc64_tuxrun.py @@ -0,0 +1,113 @@ +#!/usr/bin/env python3 +# +# Functional test that boots known good tuxboot images the same way +# that tuxrun (www.tuxrun.org) does. This tool is used by things like +# the LKFT project to run regression tests on kernels. +# +# Copyright (c) 2023 Linaro Ltd. +# +# Author: +# Alex Bennée <alex.bennee@linaro.org> +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from subprocess import check_call, DEVNULL +import tempfile + +from qemu_test import Asset +from qemu_test.tuxruntest import TuxRunBaselineTest + +class TuxRunPPC64Test(TuxRunBaselineTest): + + def ppc64_common_tuxrun(self, kernel_asset, rootfs_asset, prefix): + self.set_machine('pseries') + self.cpu='POWER10' + self.console='hvc0' + self.root='sda' + self.extradev='spapr-vscsi' + # add device args to command line. + self.require_netdev('user') + self.vm.add_args('-netdev', 'user,id=vnet,hostfwd=:127.0.0.1:0-:22', + '-device', 'virtio-net,netdev=vnet') + self.vm.add_args('-netdev', '{"type":"user","id":"hostnet0"}', + '-device', '{"driver":"virtio-net-pci","netdev":' + '"hostnet0","id":"net0","mac":"52:54:00:4c:e3:86",' + '"bus":"pci.0","addr":"0x9"}') + self.vm.add_args('-device', '{"driver":"qemu-xhci","p2":15,"p3":15,' + '"id":"usb","bus":"pci.0","addr":"0x2"}') + self.vm.add_args('-device', '{"driver":"virtio-scsi-pci","id":"scsi0"' + ',"bus":"pci.0","addr":"0x3"}') + self.vm.add_args('-device', '{"driver":"virtio-serial-pci","id":' + '"virtio-serial0","bus":"pci.0","addr":"0x4"}') + self.vm.add_args('-device', '{"driver":"scsi-cd","bus":"scsi0.0"' + ',"channel":0,"scsi-id":0,"lun":0,"device_id":' + '"drive-scsi0-0-0-0","id":"scsi0-0-0-0"}') + self.vm.add_args('-device', '{"driver":"virtio-balloon-pci",' + '"id":"balloon0","bus":"pci.0","addr":"0x6"}') + self.vm.add_args('-audiodev', '{"id":"audio1","driver":"none"}') + self.vm.add_args('-device', '{"driver":"usb-tablet","id":"input0"' + ',"bus":"usb.0","port":"1"}') + self.vm.add_args('-device', '{"driver":"usb-kbd","id":"input1"' + ',"bus":"usb.0","port":"2"}') + self.vm.add_args('-device', '{"driver":"VGA","id":"video0",' + '"vgamem_mb":16,"bus":"pci.0","addr":"0x7"}') + self.vm.add_args('-object', '{"qom-type":"rng-random","id":"objrng0"' + ',"filename":"/dev/urandom"}', + '-device', '{"driver":"virtio-rng-pci","rng":"objrng0"' + ',"id":"rng0","bus":"pci.0","addr":"0x8"}') + self.vm.add_args('-object', '{"qom-type":"cryptodev-backend-builtin",' + '"id":"objcrypto0","queues":1}', + '-device', '{"driver":"virtio-crypto-pci",' + '"cryptodev":"objcrypto0","id":"crypto0","bus"' + ':"pci.0","addr":"0xa"}') + self.vm.add_args('-device', '{"driver":"spapr-pci-host-bridge"' + ',"index":1,"id":"pci.1"}') + self.vm.add_args('-device', '{"driver":"spapr-vscsi","id":"scsi1"' + ',"reg":12288}') + self.vm.add_args('-m', '1G,slots=32,maxmem=2G', + '-object', 'memory-backend-ram,id=ram1,size=1G', + '-device', 'pc-dimm,id=dimm1,memdev=ram1') + + # Create a temporary qcow2 and launch the test-case + with tempfile.NamedTemporaryFile(prefix=prefix, + suffix='.qcow2') as qcow2: + check_call([self.qemu_img, 'create', '-f', 'qcow2', + qcow2.name, ' 1G'], + stdout=DEVNULL, stderr=DEVNULL) + + self.vm.add_args('-drive', 'file=' + qcow2.name + + ',format=qcow2,if=none,id=' + 'drive-virtio-disk1', + '-device', 'virtio-blk-pci,bus=pci.0,' + 'addr=0xb,drive=drive-virtio-disk1,id=virtio-disk1' + ',bootindex=2') + self.common_tuxrun(kernel_asset, rootfs_asset=rootfs_asset, + drive="scsi-hd") + + ASSET_PPC64_KERNEL = Asset( + 'https://storage.tuxboot.com/buildroot/20241119/ppc64/vmlinux', + '8219d5cb26e7654ad7826fe8aee6290f7c01eef44f2cd6d26c15fe8f99e1c17c') + ASSET_PPC64_ROOTFS = Asset( + 'https://storage.tuxboot.com/buildroot/20241119/ppc64/rootfs.ext4.zst', + 'b68e12314303c5dd0fef37ae98021299a206085ae591893e73557af99a02d373') + + def test_ppc64(self): + self.ppc64_common_tuxrun(kernel_asset=self.ASSET_PPC64_KERNEL, + rootfs_asset=self.ASSET_PPC64_ROOTFS, + prefix='tuxrun_ppc64_') + + ASSET_PPC64LE_KERNEL = Asset( + 'https://storage.tuxboot.com/buildroot/20241119/ppc64le/vmlinux', + '21aea1fbc18bf6fa7d8ca4ea48d4940b2c8363c077acd564eb47d769b7495279') + ASSET_PPC64LE_ROOTFS = Asset( + 'https://storage.tuxboot.com/buildroot/20241119/ppc64le/rootfs.ext4.zst', + '67d36a3f9597b738e8b7359bdf04500f4d9bb82fc35eaa65aa439d888b2392f4') + + def test_ppc64le(self): + self.ppc64_common_tuxrun(kernel_asset=self.ASSET_PPC64LE_KERNEL, + rootfs_asset=self.ASSET_PPC64LE_ROOTFS, + prefix='tuxrun_ppc64le_') + + +if __name__ == '__main__': + TuxRunBaselineTest.main() diff --git a/tests/functional/test_ppc_405.py b/tests/functional/test_ppc_405.py deleted file mode 100755 index 9851c03..0000000 --- a/tests/functional/test_ppc_405.py +++ /dev/null @@ -1,37 +0,0 @@ -#!/usr/bin/env python3 -# -# Test that the U-Boot firmware boots on ppc 405 machines and check the console -# -# Copyright (c) 2021 Red Hat, Inc. -# -# 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 qemu_test import QemuSystemTest, Asset -from qemu_test import wait_for_console_pattern -from qemu_test import exec_command_and_wait_for_pattern - -class Ppc405Machine(QemuSystemTest): - - timeout = 90 - - ASSET_UBOOT = Asset( - ('https://gitlab.com/huth/u-boot/-/raw/taihu-2021-10-09/' - 'u-boot-taihu.bin'), - 'a076bb6cdeaafa406330e51e074b66d8878d9036d67d4caa0137be03ee4c112c') - - def do_test_ppc405(self): - file_path = self.ASSET_UBOOT.fetch() - self.vm.set_console(console_index=1) - self.vm.add_args('-bios', file_path) - self.vm.launch() - wait_for_console_pattern(self, 'AMCC PPC405EP Evaluation Board') - exec_command_and_wait_for_pattern(self, 'reset', 'AMCC PowerPC 405EP') - - def test_ppc_ref405ep(self): - self.require_accelerator("tcg") - self.set_machine('ref405ep') - self.do_test_ppc405() - -if __name__ == '__main__': - QemuSystemTest.main() diff --git a/tests/functional/test_ppc_40p.py b/tests/functional/test_ppc_40p.py index c64e876..614972a 100755 --- a/tests/functional/test_ppc_40p.py +++ b/tests/functional/test_ppc_40p.py @@ -7,11 +7,9 @@ # 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 - -from unittest import skipUnless from qemu_test import QemuSystemTest, Asset -from qemu_test import wait_for_console_pattern +from qemu_test import wait_for_console_pattern, skipUntrustedTest +from qemu_test import exec_command_and_wait_for_pattern class IbmPrep40pMachine(QemuSystemTest): @@ -37,7 +35,7 @@ class IbmPrep40pMachine(QemuSystemTest): # All rights reserved. # U.S. Government Users Restricted Rights - Use, duplication or disclosure # restricted by GSA ADP Schedule Contract with IBM Corp. - @skipUnless(os.getenv('QEMU_TEST_ALLOW_UNTRUSTED_CODE'), 'untrusted code') + @skipUntrustedTest() def test_factory_firmware_and_netbsd(self): self.set_machine('40p') self.require_accelerator("tcg") @@ -46,7 +44,8 @@ class IbmPrep40pMachine(QemuSystemTest): self.vm.set_console() self.vm.add_args('-bios', bios_path, - '-fda', drive_path) + '-drive', + f"file={drive_path},format=raw,if=floppy,read-only=true") self.vm.launch() os_banner = 'NetBSD 4.0 (GENERIC) #0: Sun Dec 16 00:49:40 PST 2007' wait_for_console_pattern(self, os_banner) @@ -74,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/functional/test_ppc_amiga.py b/tests/functional/test_ppc_amiga.py index b793b5c..8600e2e 100755 --- a/tests/functional/test_ppc_amiga.py +++ b/tests/functional/test_ppc_amiga.py @@ -10,8 +10,8 @@ import subprocess from qemu_test import QemuSystemTest, Asset -from qemu_test import wait_for_console_pattern, run_cmd -from zipfile import ZipFile +from qemu_test import wait_for_console_pattern + class AmigaOneMachine(QemuSystemTest): @@ -26,16 +26,16 @@ class AmigaOneMachine(QemuSystemTest): self.require_accelerator("tcg") self.set_machine('amigaone') tar_name = 'A1Firmware_Floppy_05-Mar-2005.zip' - zip_file = self.ASSET_IMAGE.fetch() - with ZipFile(zip_file, 'r') as zf: - zf.extractall(path=self.workdir) - bios_fh = open(self.workdir + "/u-boot-amigaone.bin", "wb") - subprocess.run(['tail', '-c', '524288', - self.workdir + "/floppy_edition/updater.image"], - stdout=bios_fh) + self.archive_extract(self.ASSET_IMAGE, format="zip") + bios = self.scratch_file("u-boot-amigaone.bin") + with open(bios, "wb") as bios_fh: + subprocess.run(['tail', '-c', '524288', + self.scratch_file("floppy_edition", + "updater.image")], + stdout=bios_fh) self.vm.set_console() - self.vm.add_args('-bios', self.workdir + '/u-boot-amigaone.bin') + self.vm.add_args('-bios', bios) self.vm.launch() wait_for_console_pattern(self, 'FLASH:') diff --git a/tests/functional/test_ppc_bamboo.py b/tests/functional/test_ppc_bamboo.py index e72cbde..fddcc24 100755 --- a/tests/functional/test_ppc_bamboo.py +++ b/tests/functional/test_ppc_bamboo.py @@ -7,11 +7,11 @@ # 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 qemu_test.utils import archive_extract from qemu_test import QemuSystemTest, Asset from qemu_test import wait_for_console_pattern from qemu_test import exec_command_and_wait_for_pattern + class BambooMachine(QemuSystemTest): timeout = 90 @@ -25,13 +25,14 @@ class BambooMachine(QemuSystemTest): self.set_machine('bamboo') self.require_accelerator("tcg") self.require_netdev('user') - file_path = self.ASSET_IMAGE.fetch() - archive_extract(file_path, self.workdir) + self.archive_extract(self.ASSET_IMAGE) self.vm.set_console() - self.vm.add_args('-kernel', self.workdir + - '/system-image-powerpc-440fp/linux', - '-initrd', self.workdir + - '/system-image-powerpc-440fp/rootfs.cpio.gz', + self.vm.add_args('-kernel', + self.scratch_file('system-image-powerpc-440fp', + 'linux'), + '-initrd', + self.scratch_file('system-image-powerpc-440fp', + 'rootfs.cpio.gz'), '-nic', 'user,model=rtl8139,restrict=on') self.vm.launch() wait_for_console_pattern(self, 'Type exit when done') diff --git a/tests/functional/test_ppc_mac.py b/tests/functional/test_ppc_mac.py new file mode 100755 index 0000000..9e4bc1a --- /dev/null +++ b/tests/functional/test_ppc_mac.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python3 +# +# Boot Linux kernel on a mac99 and g3beige ppc machine and check the console +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import LinuxKernelTest, Asset + + +class MacTest(LinuxKernelTest): + + ASSET_DAY15 = Asset( + 'https://qemu-advcal.gitlab.io/qac-best-of-multiarch/download/day15.tar.xz', + '03e0757c131d2959decf293a3572d3b96c5a53587165bf05ce41b2818a2bccd5') + + def do_day15_test(self): + # mac99 also works with kvm_pr but we don't have a reliable way at + # the moment (e.g. by looking at /proc/modules) to detect whether + # we're running kvm_hv or kvm_pr. For now let's disable this test + # if we don't have TCG support. + self.require_accelerator("tcg") + self.archive_extract(self.ASSET_DAY15) + self.vm.add_args('-M', 'graphics=off') + self.launch_kernel(self.scratch_file('day15', 'invaders.elf'), + wait_for='QEMU advent calendar') + + def test_ppc_g3beige(self): + self.set_machine('g3beige') + self.do_day15_test() + + def test_ppc_mac99(self): + self.set_machine('mac99') + self.do_day15_test() + +if __name__ == '__main__': + LinuxKernelTest.main() diff --git a/tests/functional/test_ppc_mpc8544ds.py b/tests/functional/test_ppc_mpc8544ds.py index 2b3f089..0715410 100755 --- a/tests/functional/test_ppc_mpc8544ds.py +++ b/tests/functional/test_ppc_mpc8544ds.py @@ -7,10 +7,10 @@ # 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 qemu_test.utils import archive_extract from qemu_test import QemuSystemTest, Asset from qemu_test import wait_for_console_pattern + class Mpc8544dsMachine(QemuSystemTest): timeout = 90 @@ -25,10 +25,10 @@ class Mpc8544dsMachine(QemuSystemTest): def test_ppc_mpc8544ds(self): self.require_accelerator("tcg") self.set_machine('mpc8544ds') - file_path = self.ASSET_IMAGE.fetch() - archive_extract(file_path, self.workdir, member='creek/creek.bin') + kernel_file = self.archive_extract(self.ASSET_IMAGE, + member='creek/creek.bin') self.vm.set_console() - self.vm.add_args('-kernel', self.workdir + '/creek/creek.bin') + self.vm.add_args('-kernel', kernel_file) self.vm.launch() wait_for_console_pattern(self, 'QEMU advent calendar 2020', self.panic_message) diff --git a/tests/functional/test_ppc_replay.py b/tests/functional/test_ppc_replay.py new file mode 100755 index 0000000..8382070 --- /dev/null +++ b/tests/functional/test_ppc_replay.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python3 +# +# Replay tests for ppc machines +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import Asset +from replay_kernel import ReplayKernelBase + + +class PpcReplay(ReplayKernelBase): + + ASSET_DAY15 = Asset( + 'https://qemu-advcal.gitlab.io/qac-best-of-multiarch/download/day15.tar.xz', + '03e0757c131d2959decf293a3572d3b96c5a53587165bf05ce41b2818a2bccd5') + + def do_day15_test(self): + self.require_accelerator("tcg") + kernel_path = self.archive_extract(self.ASSET_DAY15, + member='day15/invaders.elf') + self.run_rr(kernel_path, self.REPLAY_KERNEL_COMMAND_LINE, + 'QEMU advent calendar', args=('-M', 'graphics=off')) + + def test_g3beige(self): + self.set_machine('g3beige') + self.do_day15_test() + + def test_mac99(self): + self.set_machine('mac99') + self.do_day15_test() + + +if __name__ == '__main__': + ReplayKernelBase.main() diff --git a/tests/functional/test_ppc_sam460ex.py b/tests/functional/test_ppc_sam460ex.py new file mode 100644 index 0000000..31cf9dd --- /dev/null +++ b/tests/functional/test_ppc_sam460ex.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python3 +# +# Functional test that boots a sam460ex machine with a PPC 460EX 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 sam460exTest(LinuxKernelTest): + + ASSET_BR2_SAM460EX_LINUX = Asset( + 'https://github.com/legoater/qemu-ppc-boot/raw/refs/heads/main/buildroot/qemu_ppc_sam460ex-2023.11-8-gdcd9f0f6eb-20240105/vmlinux', + '6f46346f3e20e8b5fc050ff363f350f8b9d76a051b9e0bd7ea470cc680c14df2') + + def test_ppc_sam460ex_buildroot(self): + self.set_machine('sam460ex') + self.require_netdev('user') + + linux_path = self.ASSET_BR2_SAM460EX_LINUX.fetch() + + self.vm.set_console() + self.vm.add_args('-kernel', linux_path, + '-device', 'virtio-net-pci,netdev=net0', + '-netdev', 'user,id=net0') + self.vm.launch() + + self.wait_for_console_pattern('Linux version') + self.wait_for_console_pattern('Hardware name: amcc,canyonlands 460EX') + self.wait_for_console_pattern('/init as init process') + self.wait_for_console_pattern('lease of 10.0.2.15 obtained') + self.wait_for_console_pattern('buildroot login:') + exec_command_and_wait_for_pattern(self, 'root', '#') + exec_command_and_wait_for_pattern(self, 'poweroff', 'System Halted') + +if __name__ == '__main__': + LinuxKernelTest.main() diff --git a/tests/functional/test_ppc_tuxrun.py b/tests/functional/test_ppc_tuxrun.py new file mode 100755 index 0000000..5458a7f --- /dev/null +++ b/tests/functional/test_ppc_tuxrun.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python3 +# +# Functional test that boots known good tuxboot images the same way +# that tuxrun (www.tuxrun.org) does. This tool is used by things like +# the LKFT project to run regression tests on kernels. +# +# Copyright (c) 2023 Linaro Ltd. +# +# Author: +# Alex Bennée <alex.bennee@linaro.org> +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import Asset +from qemu_test.tuxruntest import TuxRunBaselineTest + +class TuxRunPPC32Test(TuxRunBaselineTest): + + ASSET_PPC32_KERNEL = Asset( + 'https://storage.tuxboot.com/buildroot/20241119/ppc32/uImage', + 'aa5d81deabdb255a318c4bc5ffd6fdd2b5da1ef39f1955dcc35b671d258b68e9') + ASSET_PPC32_ROOTFS = Asset( + 'https://storage.tuxboot.com/buildroot/20241119/ppc32/rootfs.ext4.zst', + '67554f830269d6bf53b67c7dd206bcc821e463993d526b1644066fea8117019b') + + def test_ppc32(self): + self.set_machine('ppce500') + self.cpu='e500mc' + self.wait_for_shutdown=False + self.common_tuxrun(kernel_asset=self.ASSET_PPC32_KERNEL, + rootfs_asset=self.ASSET_PPC32_ROOTFS, + drive="virtio-blk-pci") + +if __name__ == '__main__': + TuxRunBaselineTest.main() diff --git a/tests/functional/test_ppc_virtex_ml507.py b/tests/functional/test_ppc_virtex_ml507.py index ffa9a06..8fe4354 100755 --- a/tests/functional/test_ppc_virtex_ml507.py +++ b/tests/functional/test_ppc_virtex_ml507.py @@ -7,10 +7,10 @@ # 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 qemu_test.utils import archive_extract from qemu_test import QemuSystemTest, Asset from qemu_test import wait_for_console_pattern + class VirtexMl507Machine(QemuSystemTest): timeout = 90 @@ -25,11 +25,11 @@ class VirtexMl507Machine(QemuSystemTest): def test_ppc_virtex_ml507(self): self.require_accelerator("tcg") self.set_machine('virtex-ml507') - file_path = self.ASSET_IMAGE.fetch() - archive_extract(file_path, self.workdir) + self.archive_extract(self.ASSET_IMAGE) self.vm.set_console() - self.vm.add_args('-kernel', self.workdir + '/hippo/hippo.linux', - '-dtb', self.workdir + '/hippo/virtex440-ml507.dtb', + self.vm.add_args('-kernel', self.scratch_file('hippo', 'hippo.linux'), + '-dtb', self.scratch_file('hippo', + 'virtex440-ml507.dtb'), '-m', '512') self.vm.launch() wait_for_console_pattern(self, 'QEMU advent calendar 2020', diff --git a/tests/functional/test_riscv32_tuxrun.py b/tests/functional/test_riscv32_tuxrun.py new file mode 100755 index 0000000..3c57020 --- /dev/null +++ b/tests/functional/test_riscv32_tuxrun.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python3 +# +# Functional test that boots known good tuxboot images the same way +# that tuxrun (www.tuxrun.org) does. This tool is used by things like +# the LKFT project to run regression tests on kernels. +# +# Copyright (c) 2023 Linaro Ltd. +# +# Author: +# Alex Bennée <alex.bennee@linaro.org> +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import Asset +from qemu_test.tuxruntest import TuxRunBaselineTest + +class TuxRunRiscV32Test(TuxRunBaselineTest): + + ASSET_RISCV32_KERNEL = Asset( + 'https://storage.tuxboot.com/buildroot/20241119/riscv32/Image', + '872bc8f8e0d4661825d5f47f7bec64988e9d0a8bd5db8917d57e16f66d83b329') + ASSET_RISCV32_ROOTFS = Asset( + 'https://storage.tuxboot.com/buildroot/20241119/riscv32/rootfs.ext4.zst', + '511ad34e63222db08d6c1da16fad224970de36517a784110956ba6a24a0ee5f6') + + def test_riscv32(self): + self.set_machine('virt') + self.common_tuxrun(kernel_asset=self.ASSET_RISCV32_KERNEL, + rootfs_asset=self.ASSET_RISCV32_ROOTFS) + + def test_riscv32_maxcpu(self): + self.set_machine('virt') + self.cpu='max' + self.common_tuxrun(kernel_asset=self.ASSET_RISCV32_KERNEL, + rootfs_asset=self.ASSET_RISCV32_ROOTFS) + +if __name__ == '__main__': + TuxRunBaselineTest.main() diff --git a/tests/functional/test_riscv64_tuxrun.py b/tests/functional/test_riscv64_tuxrun.py new file mode 100755 index 0000000..0d8de36 --- /dev/null +++ b/tests/functional/test_riscv64_tuxrun.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python3 +# +# Functional test that boots known good tuxboot images the same way +# that tuxrun (www.tuxrun.org) does. This tool is used by things like +# the LKFT project to run regression tests on kernels. +# +# Copyright (c) 2023 Linaro Ltd. +# +# Author: +# Alex Bennée <alex.bennee@linaro.org> +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import Asset +from qemu_test.tuxruntest import TuxRunBaselineTest + +class TuxRunRiscV64Test(TuxRunBaselineTest): + + ASSET_RISCV64_KERNEL = Asset( + 'https://storage.tuxboot.com/buildroot/20241119/riscv64/Image', + '2bd8132a3bf21570290042324fff48c987f42f2a00c08de979f43f0662ebadba') + ASSET_RISCV64_ROOTFS = Asset( + 'https://storage.tuxboot.com/buildroot/20241119/riscv64/rootfs.ext4.zst', + 'aa4736a9872651dfc0d95e709465eedf1134fd19d42b8cb305bfd776f9801004') + + ASSET_RISCV32_KERNEL = Asset( + 'https://storage.tuxboot.com/buildroot/20241119/riscv32/Image', + '872bc8f8e0d4661825d5f47f7bec64988e9d0a8bd5db8917d57e16f66d83b329') + ASSET_RISCV32_ROOTFS = Asset( + 'https://storage.tuxboot.com/buildroot/20241119/riscv32/rootfs.ext4.zst', + '511ad34e63222db08d6c1da16fad224970de36517a784110956ba6a24a0ee5f6') + + def test_riscv64(self): + self.set_machine('virt') + self.common_tuxrun(kernel_asset=self.ASSET_RISCV64_KERNEL, + rootfs_asset=self.ASSET_RISCV64_ROOTFS) + + def test_riscv64_maxcpu(self): + self.set_machine('virt') + self.cpu='max' + self.common_tuxrun(kernel_asset=self.ASSET_RISCV64_KERNEL, + rootfs_asset=self.ASSET_RISCV64_ROOTFS) + + def test_riscv64_rv32(self): + self.set_machine('virt') + self.cpu='rv32' + self.common_tuxrun(kernel_asset=self.ASSET_RISCV32_KERNEL, + rootfs_asset=self.ASSET_RISCV32_ROOTFS) + +if __name__ == '__main__': + TuxRunBaselineTest.main() diff --git a/tests/functional/test_riscv_opensbi.py b/tests/functional/test_riscv_opensbi.py new file mode 100755 index 0000000..d077e40 --- /dev/null +++ b/tests/functional/test_riscv_opensbi.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python3 +# +# OpenSBI boot test for RISC-V machines +# +# Copyright (c) 2022, Ventana Micro +# +# 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 qemu_test import QemuSystemTest +from qemu_test import wait_for_console_pattern + +class RiscvOpenSBI(QemuSystemTest): + + timeout = 5 + + def boot_opensbi(self): + self.vm.set_console() + self.vm.launch() + wait_for_console_pattern(self, 'Platform Name') + wait_for_console_pattern(self, 'Boot HART MEDELEG') + + def test_riscv_spike(self): + self.set_machine('spike') + self.boot_opensbi() + + def test_riscv_sifive_u(self): + self.set_machine('sifive_u') + self.boot_opensbi() + + def test_riscv_virt(self): + self.set_machine('virt') + self.boot_opensbi() + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/functional/test_rx_gdbsim.py b/tests/functional/test_rx_gdbsim.py index 5687f75..4924579 100755 --- a/tests/functional/test_rx_gdbsim.py +++ b/tests/functional/test_rx_gdbsim.py @@ -10,13 +10,9 @@ # 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 - -from unittest import skipUnless from qemu_test import QemuSystemTest, Asset from qemu_test import exec_command_and_wait_for_pattern -from qemu_test import wait_for_console_pattern -from qemu_test.utils import gzip_uncompress +from qemu_test import wait_for_console_pattern, skipFlakyTest class RxGdbSimMachine(QemuSystemTest): @@ -25,13 +21,16 @@ class RxGdbSimMachine(QemuSystemTest): KERNEL_COMMON_COMMAND_LINE = 'printk.time=0 ' ASSET_UBOOT = Asset( - 'https://acc.dl.osdn.jp/users/23/23888/u-boot.bin.gz', - '7146567d669e91dbac166384b29aeba1715beb844c8551e904b86831bfd9d046') + ('https://github.com/philmd/qemu-testing-blob/raw/rx-gdbsim/rx/gdbsim/' + 'u-boot.bin'), + 'dd7dd4220cccf7aeb32227b26233bf39600db05c3f8e26005bcc2bf6c927207d') ASSET_DTB = Asset( - 'https://acc.dl.osdn.jp/users/23/23887/rx-virt.dtb', + ('https://github.com/philmd/qemu-testing-blob/raw/rx-gdbsim/rx/gdbsim/' + 'rx-gdbsim.dtb'), 'aa278d9c1907a4501741d7ee57e7f65c02dd1b3e0323b33c6d4247f1b32cf29a') ASSET_KERNEL = Asset( - 'http://acc.dl.osdn.jp/users/23/23845/zImage', + ('https://github.com/philmd/qemu-testing-blob/raw/rx-gdbsim/rx/gdbsim/' + 'zImage'), 'baa43205e74a7220ed8482188c5e9ce497226712abb7f4e7e4f825ce19ff9656') def test_uboot(self): @@ -40,9 +39,7 @@ class RxGdbSimMachine(QemuSystemTest): """ self.set_machine('gdbsim-r5f562n8') - uboot_path_gz = self.ASSET_UBOOT.fetch() - uboot_path = os.path.join(self.workdir, 'u-boot.bin') - gzip_uncompress(uboot_path_gz, uboot_path) + uboot_path = self.ASSET_UBOOT.fetch() self.vm.set_console() self.vm.add_args('-bios', uboot_path, @@ -52,9 +49,10 @@ class RxGdbSimMachine(QemuSystemTest): wait_for_console_pattern(self, uboot_version) gcc_version = 'rx-unknown-linux-gcc (GCC) 9.0.0 20181105 (experimental)' # FIXME limit baudrate on chardev, else we type too fast + # https://gitlab.com/qemu-project/qemu/-/issues/2691 #exec_command_and_wait_for_pattern(self, 'version', gcc_version) - @skipUnless(os.getenv('QEMU_TEST_FLAKY_TESTS'), 'Test is unstable on GitLab') + @skipFlakyTest(bug_url="https://gitlab.com/qemu-project/qemu/-/issues/2691") def test_linux_sash(self): """ Boots a Linux kernel and checks that the console is operational. diff --git a/tests/functional/test_s390x_ccw_virtio.py b/tests/functional/test_s390x_ccw_virtio.py index f7acd90..453711a 100755 --- a/tests/functional/test_s390x_ccw_virtio.py +++ b/tests/functional/test_s390x_ccw_virtio.py @@ -17,7 +17,7 @@ import tempfile from qemu_test import QemuSystemTest, Asset from qemu_test import exec_command_and_wait_for_pattern from qemu_test import wait_for_console_pattern -from qemu_test.utils import lzma_uncompress + class S390CCWVirtioMachine(QemuSystemTest): KERNEL_COMMON_COMMAND_LINE = 'printk.time=0 ' @@ -174,9 +174,7 @@ class S390CCWVirtioMachine(QemuSystemTest): kernel_path = self.ASSET_F31_KERNEL.fetch() - initrd_path_xz = self.ASSET_F31_INITRD.fetch() - initrd_path = os.path.join(self.workdir, 'initrd-raw.img') - lzma_uncompress(initrd_path_xz, initrd_path) + initrd_path = self.uncompress(self.ASSET_F31_INITRD, format="xz") self.vm.set_console() kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + ' audit=0 ' diff --git a/tests/functional/test_s390x_replay.py b/tests/functional/test_s390x_replay.py new file mode 100755 index 0000000..33b5843 --- /dev/null +++ b/tests/functional/test_s390x_replay.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python3 +# +# Replay test that boots a Linux kernel on an s390x machine +# and checks the console +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import Asset +from replay_kernel import ReplayKernelBase + + +class S390xReplay(ReplayKernelBase): + + ASSET_KERNEL = Asset( + ('https://archives.fedoraproject.org/pub/archive/fedora-secondary/' + 'releases/29/Everything/s390x/os/images/kernel.img'), + 'dace03b8ae0c9f670ebb9b8d6ce5eb24b62987f346de8f1300a439bb00bb99e7') + + def test_s390_ccw_virtio(self): + self.set_machine('s390-ccw-virtio') + kernel_path = self.ASSET_KERNEL.fetch() + kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'console=sclp0' + console_pattern = 'Kernel command line: %s' % kernel_command_line + self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=9) + + +if __name__ == '__main__': + ReplayKernelBase.main() diff --git a/tests/functional/test_s390x_topology.py b/tests/functional/test_s390x_topology.py index 20727f6..1b5dc65 100755 --- a/tests/functional/test_s390x_topology.py +++ b/tests/functional/test_s390x_topology.py @@ -10,14 +10,10 @@ # 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 time - from qemu_test import QemuSystemTest, Asset from qemu_test import exec_command from qemu_test import exec_command_and_wait_for_pattern from qemu_test import wait_for_console_pattern -from qemu_test.utils import lzma_uncompress class S390CPUTopology(QemuSystemTest): @@ -89,9 +85,7 @@ class S390CPUTopology(QemuSystemTest): """ self.require_accelerator("kvm") kernel_path = self.ASSET_F35_KERNEL.fetch() - initrd_path_xz = self.ASSET_F35_INITRD.fetch() - initrd_path = os.path.join(self.workdir, 'initrd-raw.img') - lzma_uncompress(initrd_path_xz, initrd_path) + initrd_path = self.uncompress(self.ASSET_F35_INITRD, format="xz") self.vm.set_console() kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE @@ -223,12 +217,12 @@ class S390CPUTopology(QemuSystemTest): self.assertEqual(res['return']['polarization'], 'horizontal') self.check_topology(0, 0, 0, 0, 'medium', False) - self.guest_set_dispatching('1'); + self.guest_set_dispatching('1') res = self.vm.qmp('query-s390x-cpu-polarization') self.assertEqual(res['return']['polarization'], 'vertical') self.check_topology(0, 0, 0, 0, 'medium', False) - self.guest_set_dispatching('0'); + self.guest_set_dispatching('0') res = self.vm.qmp('query-s390x-cpu-polarization') self.assertEqual(res['return']['polarization'], 'horizontal') self.check_topology(0, 0, 0, 0, 'medium', False) @@ -289,7 +283,7 @@ class S390CPUTopology(QemuSystemTest): self.check_polarization('vertical:high') self.check_topology(0, 0, 0, 0, 'high', False) - self.guest_set_dispatching('0'); + self.guest_set_dispatching('0') self.check_polarization("horizontal") self.check_topology(0, 0, 0, 0, 'high', False) @@ -316,11 +310,11 @@ class S390CPUTopology(QemuSystemTest): self.check_topology(0, 0, 0, 0, 'high', True) self.check_polarization("horizontal") - self.guest_set_dispatching('1'); + self.guest_set_dispatching('1') self.check_topology(0, 0, 0, 0, 'high', True) self.check_polarization("vertical:high") - self.guest_set_dispatching('0'); + self.guest_set_dispatching('0') self.check_topology(0, 0, 0, 0, 'high', True) self.check_polarization("horizontal") @@ -366,7 +360,7 @@ class S390CPUTopology(QemuSystemTest): self.check_topology(0, 0, 0, 0, 'high', True) - self.guest_set_dispatching('1'); + self.guest_set_dispatching('1') self.check_topology(0, 0, 0, 0, 'high', True) diff --git a/tests/functional/test_s390x_tuxrun.py b/tests/functional/test_s390x_tuxrun.py new file mode 100755 index 0000000..8df3c68 --- /dev/null +++ b/tests/functional/test_s390x_tuxrun.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python3 +# +# Functional test that boots known good tuxboot images the same way +# that tuxrun (www.tuxrun.org) does. This tool is used by things like +# the LKFT project to run regression tests on kernels. +# +# Copyright (c) 2023 Linaro Ltd. +# +# Author: +# Alex Bennée <alex.bennee@linaro.org> +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import Asset +from qemu_test.tuxruntest import TuxRunBaselineTest + +class TuxRunS390xTest(TuxRunBaselineTest): + + ASSET_S390X_KERNEL = Asset( + 'https://storage.tuxboot.com/buildroot/20241119/s390/bzImage', + 'ee67e91db52a2aed104a7c72b2a08987c678f8179c029626789c35d6dd0fedf1') + ASSET_S390X_ROOTFS = Asset( + 'https://storage.tuxboot.com/buildroot/20241119/s390/rootfs.ext4.zst', + 'bff7971fc2fef56372d98afe4557b82fd0a785a241e44c29b058e577ad1bbb44') + + def test_s390(self): + self.set_machine('s390-ccw-virtio') + self.wait_for_shutdown=False + self.common_tuxrun(kernel_asset=self.ASSET_S390X_KERNEL, + rootfs_asset=self.ASSET_S390X_ROOTFS, + drive="virtio-blk-ccw", + haltmsg="Requesting system halt") + +if __name__ == '__main__': + TuxRunBaselineTest.main() diff --git a/tests/functional/test_sh4_r2d.py b/tests/functional/test_sh4_r2d.py new file mode 100755 index 0000000..03a64837 --- /dev/null +++ b/tests/functional/test_sh4_r2d.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python3 +# +# Boot a Linux kernel on a r2d sh4 machine and check the console +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import LinuxKernelTest, Asset, skipFlakyTest + + +class R2dTest(LinuxKernelTest): + + ASSET_DAY09 = Asset( + 'https://qemu-advcal.gitlab.io/qac-best-of-multiarch/download/day09.tar.xz', + 'a61b44d2630a739d1380cc4ff4b80981d47ccfd5992f1484ccf48322c35f09ac') + + # This test has a 6-10% failure rate on various hosts that look + # like issues with a buggy kernel. + # XXX file tracking bug + @skipFlakyTest(bug_url=None) + def test_r2d(self): + self.set_machine('r2d') + self.archive_extract(self.ASSET_DAY09) + self.vm.add_args('-append', 'console=ttySC1') + self.launch_kernel(self.scratch_file('day09', 'zImage'), + console_index=1, + wait_for='QEMU advent calendar') + +if __name__ == '__main__': + LinuxKernelTest.main() diff --git a/tests/functional/test_sh4_tuxrun.py b/tests/functional/test_sh4_tuxrun.py new file mode 100755 index 0000000..1748f8c --- /dev/null +++ b/tests/functional/test_sh4_tuxrun.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python3 +# +# Functional test that boots known good tuxboot images the same way +# that tuxrun (www.tuxrun.org) does. This tool is used by things like +# the LKFT project to run regression tests on kernels. +# +# Copyright (c) 2023 Linaro Ltd. +# +# Author: +# Alex Bennée <alex.bennee@linaro.org> +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import Asset, exec_command_and_wait_for_pattern +from qemu_test.tuxruntest import TuxRunBaselineTest + +class TuxRunSh4Test(TuxRunBaselineTest): + + ASSET_SH4_KERNEL = Asset( + 'https://storage.tuxboot.com/20230331/sh4/zImage', + '29d9b2aba604a0f53a5dc3b5d0f2b8e35d497de1129f8ee5139eb6fdf0db692f') + ASSET_SH4_ROOTFS = Asset( + 'https://storage.tuxboot.com/20230331/sh4/rootfs.ext4.zst', + '3592a7a3d5a641e8b9821449e77bc43c9904a56c30d45da0694349cfd86743fd') + + def test_sh4(self): + self.set_machine('r2d') + self.cpu='sh7785' + self.root='sda' + self.console='ttySC1' + + # The test is currently too unstable to do much in userspace + # so we skip common_tuxrun and do a minimal boot and shutdown. + (kernel, disk, dtb) = self.fetch_tuxrun_assets(self.ASSET_SH4_KERNEL, + self.ASSET_SH4_ROOTFS) + + # the console comes on the second serial port + self.prepare_run(kernel, disk, + "driver=ide-hd,bus=ide.0,unit=0", + console_index=1) + self.vm.launch() + + self.wait_for_console_pattern("tuxtest login:") + exec_command_and_wait_for_pattern(self, 'root', 'root@tuxtest:~#') + exec_command_and_wait_for_pattern(self, 'halt', + "reboot: System halted") + +if __name__ == '__main__': + TuxRunBaselineTest.main() diff --git a/tests/functional/test_sh4eb_r2d.py b/tests/functional/test_sh4eb_r2d.py new file mode 100755 index 0000000..473093b --- /dev/null +++ b/tests/functional/test_sh4eb_r2d.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python3 +# +# Boot a Linux kernel on a r2d sh4eb machine and check the console +# +# 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 R2dEBTest(LinuxKernelTest): + + ASSET_TGZ = Asset( + 'https://landley.net/bin/mkroot/0.8.11/sh4eb.tgz', + 'be8c6cb5aef8406899dc5aa5e22b6aa45840eb886cdd3ced51555c10577ada2c') + + def test_sh4eb_r2d(self): + self.set_machine('r2d') + self.archive_extract(self.ASSET_TGZ) + self.vm.add_args('-append', 'console=ttySC1 noiotrap') + self.launch_kernel(self.scratch_file('sh4eb', 'linux-kernel'), + initrd=self.scratch_file('sh4eb', + 'initramfs.cpio.gz'), + console_index=1, wait_for='Type exit when done') + exec_command_and_wait_for_pattern(self, 'exit', 'Restarting system') + +if __name__ == '__main__': + LinuxKernelTest.main() diff --git a/tests/functional/test_sparc64_sun4u.py b/tests/functional/test_sparc64_sun4u.py index 32e245f..27ac289 100755 --- a/tests/functional/test_sparc64_sun4u.py +++ b/tests/functional/test_sparc64_sun4u.py @@ -10,11 +10,9 @@ # 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 - from qemu_test import QemuSystemTest, Asset from qemu_test import wait_for_console_pattern -from qemu_test.utils import archive_extract + class Sun4uMachine(QemuSystemTest): """Boots the Linux kernel and checks that the console is operational""" @@ -28,11 +26,10 @@ class Sun4uMachine(QemuSystemTest): def test_sparc64_sun4u(self): self.set_machine('sun4u') - file_path = self.ASSET_IMAGE.fetch() - kernel_name = 'day23/vmlinux' - archive_extract(file_path, self.workdir, kernel_name) + kernel_file = self.archive_extract(self.ASSET_IMAGE, + member='day23/vmlinux') self.vm.set_console() - self.vm.add_args('-kernel', os.path.join(self.workdir, kernel_name), + self.vm.add_args('-kernel', kernel_file, '-append', 'printk.time=0') self.vm.launch() wait_for_console_pattern(self, 'Starting logging: OK') diff --git a/tests/functional/test_sparc64_tuxrun.py b/tests/functional/test_sparc64_tuxrun.py new file mode 100755 index 0000000..0d7b43d --- /dev/null +++ b/tests/functional/test_sparc64_tuxrun.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python3 +# +# Functional test that boots known good tuxboot images the same way +# that tuxrun (www.tuxrun.org) does. This tool is used by things like +# the LKFT project to run regression tests on kernels. +# +# Copyright (c) 2023 Linaro Ltd. +# +# Author: +# Alex Bennée <alex.bennee@linaro.org> +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import Asset +from qemu_test.tuxruntest import TuxRunBaselineTest + +class TuxRunSparc64Test(TuxRunBaselineTest): + + ASSET_SPARC64_KERNEL = Asset( + 'https://storage.tuxboot.com/buildroot/20241119/sparc64/vmlinux', + 'a04cfb2e70a264051d161fdd93aabf4b2a9472f2e435c14ed18c5848c5fed261') + ASSET_SPARC64_ROOTFS = Asset( + 'https://storage.tuxboot.com/buildroot/20241119/sparc64/rootfs.ext4.zst', + '479c3dc104c82b68be55e2c0c5c38cd473d0b37ad4badccde4775bb88ce34611') + + def test_sparc64(self): + self.set_machine('sun4u') + self.root='sda' + self.wait_for_shutdown=False + self.common_tuxrun(kernel_asset=self.ASSET_SPARC64_KERNEL, + rootfs_asset=self.ASSET_SPARC64_ROOTFS, + drive="driver=ide-hd,bus=ide.0,unit=0") + +if __name__ == '__main__': + TuxRunBaselineTest.main() diff --git a/tests/functional/test_sparc_replay.py b/tests/functional/test_sparc_replay.py new file mode 100755 index 0000000..865d648 --- /dev/null +++ b/tests/functional/test_sparc_replay.py @@ -0,0 +1,27 @@ +#!/usr/bin/env python3 +# +# Replay test that boots a Linux kernel on a sparc sun4m machine +# and checks the console +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import Asset +from replay_kernel import ReplayKernelBase + + +class SparcReplay(ReplayKernelBase): + + ASSET_DAY11 = Asset( + 'https://qemu-advcal.gitlab.io/qac-best-of-multiarch/download/day11.tar.xz', + 'c776533ba756bf4dd3f1fc4c024fb50ef0d853e05c5f5ddf0900a32d1eaa49e0') + + def test_replay(self): + self.set_machine('SS-10') + kernel_path = self.archive_extract(self.ASSET_DAY11, + member="day11/zImage.elf") + self.run_rr(kernel_path, self.REPLAY_KERNEL_COMMAND_LINE, + 'QEMU advent calendar') + + +if __name__ == '__main__': + ReplayKernelBase.main() diff --git a/tests/functional/test_sparc_sun4m.py b/tests/functional/test_sparc_sun4m.py new file mode 100755 index 0000000..7cd28eb --- /dev/null +++ b/tests/functional/test_sparc_sun4m.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python3 +# +# Functional test that boots a Linux kernel on a sparc sun4m machine +# and checks the console +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import LinuxKernelTest, Asset + + +class Sun4mTest(LinuxKernelTest): + + ASSET_DAY11 = Asset( + 'https://qemu-advcal.gitlab.io/qac-best-of-multiarch/download/day11.tar.xz', + 'c776533ba756bf4dd3f1fc4c024fb50ef0d853e05c5f5ddf0900a32d1eaa49e0') + + def test_sparc_ss20(self): + self.set_machine('SS-20') + self.archive_extract(self.ASSET_DAY11) + self.launch_kernel(self.scratch_file('day11', 'zImage.elf'), + wait_for='QEMU advent calendar') + +if __name__ == '__main__': + LinuxKernelTest.main() diff --git a/tests/functional/test_virtio_balloon.py b/tests/functional/test_virtio_balloon.py new file mode 100755 index 0000000..5877b6c --- /dev/null +++ b/tests/functional/test_virtio_balloon.py @@ -0,0 +1,178 @@ +#!/usr/bin/env python3 +# +# virtio-balloon tests +# +# 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 time + +from qemu_test import QemuSystemTest, Asset +from qemu_test import wait_for_console_pattern +from qemu_test import exec_command_and_wait_for_pattern + +UNSET_STATS_VALUE = 18446744073709551615 + + +class VirtioBalloonx86(QemuSystemTest): + + ASSET_KERNEL = Asset( + ('https://archives.fedoraproject.org/pub/archive/fedora/linux/releases' + '/31/Server/x86_64/os/images/pxeboot/vmlinuz'), + 'd4738d03dbbe083ca610d0821d0a8f1488bebbdccef54ce33e3adb35fda00129') + + ASSET_INITRD = Asset( + ('https://archives.fedoraproject.org/pub/archive/fedora/linux/releases' + '/31/Server/x86_64/os/images/pxeboot/initrd.img'), + '277cd6c7adf77c7e63d73bbb2cded8ef9e2d3a2f100000e92ff1f8396513cd8b') + + ASSET_DISKIMAGE = Asset( + ('https://archives.fedoraproject.org/pub/archive/fedora/linux/releases' + '/31/Cloud/x86_64/images/Fedora-Cloud-Base-31-1.9.x86_64.qcow2'), + 'e3c1b309d9203604922d6e255c2c5d098a309c2d46215d8fc026954f3c5c27a0') + + DEFAULT_KERNEL_PARAMS = ('root=/dev/vda1 console=ttyS0 net.ifnames=0 ' + 'rd.rescue quiet') + + def wait_for_console_pattern(self, success_message, vm=None): + wait_for_console_pattern( + self, + success_message, + failure_message="Kernel panic - not syncing", + vm=vm, + ) + + def mount_root(self): + self.wait_for_console_pattern('Entering emergency mode.') + prompt = '# ' + self.wait_for_console_pattern(prompt) + + # Synchronize on virtio-block driver creating the root device + exec_command_and_wait_for_pattern(self, + "while ! (dmesg -c | grep vda:) ; do sleep 1 ; done", + "vda1") + + exec_command_and_wait_for_pattern(self, 'mount /dev/vda1 /sysroot', + prompt) + exec_command_and_wait_for_pattern(self, 'chroot /sysroot', + prompt) + exec_command_and_wait_for_pattern(self, "modprobe virtio-balloon", + prompt) + + def assert_initial_stats(self): + ret = self.vm.qmp('qom-get', + {'path': '/machine/peripheral/balloon', + 'property': 'guest-stats'})['return'] + when = ret.get('last-update') + assert when == 0 + stats = ret.get('stats') + for name, val in stats.items(): + assert val == UNSET_STATS_VALUE + + def assert_running_stats(self, then): + # We told the QEMU to refresh stats every 100ms, but + # there can be a delay between virtio-ballon driver + # being modprobed and seeing the first stats refresh + # Retry a few times for robustness under heavy load + retries = 10 + when = 0 + while when == 0 and retries: + ret = self.vm.qmp('qom-get', + {'path': '/machine/peripheral/balloon', + 'property': 'guest-stats'})['return'] + when = ret.get('last-update') + if when == 0: + retries = retries - 1 + time.sleep(0.5) + + now = time.time() + + assert when > then and when < now + stats = ret.get('stats') + # Stat we expect this particular Kernel to have set + expectData = [ + "stat-available-memory", + "stat-disk-caches", + "stat-free-memory", + "stat-htlb-pgalloc", + "stat-htlb-pgfail", + "stat-major-faults", + "stat-minor-faults", + "stat-swap-in", + "stat-swap-out", + "stat-total-memory", + ] + for name, val in stats.items(): + if name in expectData: + assert val != UNSET_STATS_VALUE + else: + assert val == UNSET_STATS_VALUE + + def test_virtio_balloon_stats(self): + self.set_machine('q35') + self.require_accelerator("kvm") + kernel_path = self.ASSET_KERNEL.fetch() + initrd_path = self.ASSET_INITRD.fetch() + diskimage_path = self.ASSET_DISKIMAGE.fetch() + + self.vm.set_console() + self.vm.add_args("-S") + self.vm.add_args("-cpu", "max") + self.vm.add_args("-m", "2G") + # Slow down BIOS phase with boot menu, so that after a system + # reset, we can reliably catch the clean stats again in BIOS + # phase before the guest OS launches + self.vm.add_args("-boot", "menu=on") + self.vm.add_args("-accel", "kvm") + self.vm.add_args("-device", "virtio-balloon,id=balloon") + self.vm.add_args('-drive', + f'file={diskimage_path},if=none,id=drv0,snapshot=on') + self.vm.add_args('-device', 'virtio-blk-pci,bus=pcie.0,' + + 'drive=drv0,id=virtio-disk0,bootindex=1') + + self.vm.add_args( + "-kernel", + kernel_path, + "-initrd", + initrd_path, + "-append", + self.DEFAULT_KERNEL_PARAMS + ) + self.vm.launch() + + # Poll stats at 100ms + self.vm.qmp('qom-set', + {'path': '/machine/peripheral/balloon', + 'property': 'guest-stats-polling-interval', + 'value': 100 }) + + # We've not run any guest code yet, neither BIOS or guest, + # so stats should be all default values + self.assert_initial_stats() + + self.vm.qmp('cont') + + then = time.time() + self.mount_root() + self.assert_running_stats(then) + + # Race window between these two commands, where we + # rely on '-boot menu=on' to (hopefully) ensure we're + # still executing the BIOS when QEMU processes the + # 'stop', and thus have not loaded the virtio-balloon + # driver in the guest + self.vm.qmp('system_reset') + self.vm.qmp('stop') + + # If the above assumption held, we're in BIOS now and + # stats should be all back at their default values + self.assert_initial_stats() + self.vm.qmp('cont') + + then = time.time() + self.mount_root() + self.assert_running_stats(then) + + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/functional/test_virtio_gpu.py b/tests/functional/test_virtio_gpu.py index 441cbdc..81c9156 100755 --- a/tests/functional/test_virtio_gpu.py +++ b/tests/functional/test_virtio_gpu.py @@ -6,25 +6,19 @@ # later. See the COPYING file in the top-level directory. -from qemu_test import BUILD_DIR from qemu_test import QemuSystemTest, Asset from qemu_test import wait_for_console_pattern from qemu_test import exec_command_and_wait_for_pattern from qemu_test import is_readable_executable_file -from qemu.utils import kvm_available import os import socket import subprocess -def pick_default_vug_bin(): - relative_path = "./contrib/vhost-user-gpu/vhost-user-gpu" - if is_readable_executable_file(relative_path): - return relative_path - - bld_dir_path = os.path.join(BUILD_DIR, relative_path) +def pick_default_vug_bin(test): + bld_dir_path = test.build_file("contrib", "vhost-user-gpu", "vhost-user-gpu") if is_readable_executable_file(bld_dir_path): return bld_dir_path @@ -80,15 +74,14 @@ class VirtioGPUx86(QemuSystemTest): self.wait_for_console_pattern("as init process") exec_command_and_wait_for_pattern( - self, "/usr/sbin/modprobe virtio_gpu", "" + self, "/usr/sbin/modprobe virtio_gpu", "features: +virgl +edid" ) - self.wait_for_console_pattern("features: +virgl +edid") def test_vhost_user_vga_virgl(self): # FIXME: should check presence of vhost-user-gpu, virgl, memfd etc self.require_accelerator('kvm') - vug = pick_default_vug_bin() + vug = pick_default_vug_bin(self) if not vug: self.skipTest("Could not find vhost-user-gpu") @@ -102,9 +95,7 @@ class VirtioGPUx86(QemuSystemTest): os.set_inheritable(qemu_sock.fileno(), True) os.set_inheritable(vug_sock.fileno(), True) - self._vug_log_path = os.path.join( - self.logdir, "vhost-user-gpu.log" - ) + self._vug_log_path = self.log_file("vhost-user-gpu.log") self._vug_log_file = open(self._vug_log_path, "wb") self.log.info('Complete vhost-user-gpu.log file can be ' 'found at %s', self._vug_log_path) diff --git a/tests/functional/test_virtio_version.py b/tests/functional/test_virtio_version.py index eb23060..a5ea732 100755 --- a/tests/functional/test_virtio_version.py +++ b/tests/functional/test_virtio_version.py @@ -9,8 +9,6 @@ Check compatibility of virtio device types # # 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 sys -import os from qemu.machine import QEMUMachine from qemu_test import QemuSystemTest @@ -141,6 +139,7 @@ class VirtioVersionCheck(QemuSystemTest): def test_conventional_devs(self): + self.set_machine('pc') self.check_all_variants('virtio-net-pci', VIRTIO_NET) # virtio-blk requires 'driver' parameter #self.check_all_variants('virtio-blk-pci', VIRTIO_BLOCK) @@ -167,6 +166,7 @@ class VirtioVersionCheck(QemuSystemTest): self.assertIn('pci-express-device', ifaces) def test_modern_only_devs(self): + self.set_machine('pc') self.check_modern_only('virtio-vga', VIRTIO_GPU) self.check_modern_only('virtio-gpu-pci', VIRTIO_GPU) self.check_modern_only('virtio-mouse-pci', VIRTIO_INPUT) diff --git a/tests/avocado/vnc.py b/tests/functional/test_vnc.py index 862c899..f1dd159 100644..100755 --- a/tests/avocado/vnc.py +++ b/tests/functional/test_vnc.py @@ -1,3 +1,5 @@ +#!/usr/bin/env python3 +# # Simple functional tests for VNC functionality # # Copyright (c) 2018 Red Hat, Inc. @@ -9,25 +11,13 @@ # later. See the COPYING file in the top-level directory. import socket -from typing import List -from avocado_qemu import QemuSystemTest +from qemu.machine.machine import VMLaunchFailure +from qemu_test import QemuSystemTest +from qemu_test.ports import Ports VNC_ADDR = '127.0.0.1' -VNC_PORT_START = 32768 -VNC_PORT_END = VNC_PORT_START + 1024 - - -def check_bind(port: int) -> bool: - with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock: - try: - sock.bind((VNC_ADDR, port)) - except OSError: - return False - - return True - def check_connect(port: int) -> bool: with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock: @@ -38,32 +28,20 @@ def check_connect(port: int) -> bool: return True - -def find_free_ports(count: int) -> List[int]: - result = [] - for port in range(VNC_PORT_START, VNC_PORT_END): - if check_bind(port): - result.append(port) - if len(result) >= count: - break - assert len(result) == count - return result - - class Vnc(QemuSystemTest): - """ - :avocado: tags=vnc,quick - :avocado: tags=machine:none - """ - def test_no_vnc(self): - self.vm.add_args('-nodefaults', '-S') - self.vm.launch() - self.assertFalse(self.vm.qmp('query-vnc')['return']['enabled']) def test_no_vnc_change_password(self): + self.set_machine('none') self.vm.add_args('-nodefaults', '-S') self.vm.launch() - self.assertFalse(self.vm.qmp('query-vnc')['return']['enabled']) + + query_vnc_response = self.vm.qmp('query-vnc') + if 'error' in query_vnc_response: + self.assertEqual(query_vnc_response['error']['class'], + 'CommandNotFound') + self.skipTest('VNC support not available') + self.assertFalse(query_vnc_response['return']['enabled']) + set_password_response = self.vm.qmp('change-vnc-password', password='new_password') self.assertIn('error', set_password_response) @@ -72,9 +50,22 @@ class Vnc(QemuSystemTest): self.assertEqual(set_password_response['error']['desc'], 'Could not set password') + def launch_guarded(self): + try: + self.vm.launch() + except VMLaunchFailure as excp: + if "-vnc: invalid option" in excp.output: + self.skipTest("VNC support not available") + elif "Cipher backend does not support DES algorithm" in excp.output: + self.skipTest("No cryptographic backend available") + else: + self.log.info("unhandled launch failure: %s", excp.output) + raise excp + def test_change_password_requires_a_password(self): - self.vm.add_args('-nodefaults', '-S', '-vnc', ':0') - self.vm.launch() + self.set_machine('none') + self.vm.add_args('-nodefaults', '-S', '-vnc', ':1,to=999') + self.launch_guarded() self.assertTrue(self.vm.qmp('query-vnc')['return']['enabled']) set_password_response = self.vm.qmp('change-vnc-password', password='new_password') @@ -85,20 +76,20 @@ class Vnc(QemuSystemTest): 'Could not set password') def test_change_password(self): - self.vm.add_args('-nodefaults', '-S', '-vnc', ':0,password=on') - self.vm.launch() + self.set_machine('none') + self.vm.add_args('-nodefaults', '-S', '-vnc', ':1,to=999,password=on') + self.launch_guarded() self.assertTrue(self.vm.qmp('query-vnc')['return']['enabled']) self.vm.cmd('change-vnc-password', password='new_password') - def test_change_listen(self): - a, b, c = find_free_ports(3) + def do_test_change_listen(self, a, b, c): self.assertFalse(check_connect(a)) self.assertFalse(check_connect(b)) self.assertFalse(check_connect(c)) self.vm.add_args('-nodefaults', '-S', '-vnc', f'{VNC_ADDR}:{a - 5900}') - self.vm.launch() + self.launch_guarded() self.assertEqual(self.vm.qmp('query-vnc')['return']['service'], str(a)) self.assertTrue(check_connect(a)) self.assertFalse(check_connect(b)) @@ -113,3 +104,13 @@ class Vnc(QemuSystemTest): self.assertFalse(check_connect(a)) self.assertTrue(check_connect(b)) self.assertTrue(check_connect(c)) + + def test_change_listen(self): + self.set_machine('none') + with Ports() as ports: + a, b, c = ports.find_free_ports(3) + self.do_test_change_listen(a, b, c) + + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/functional/test_x86_64_hotplug_blk.py b/tests/functional/test_x86_64_hotplug_blk.py new file mode 100755 index 0000000..7ddbfef --- /dev/null +++ b/tests/functional/test_x86_64_hotplug_blk.py @@ -0,0 +1,85 @@ +#!/usr/bin/env python3 +# +# Functional test that hotplugs a virtio blk disk and checks it on a Linux +# guest +# +# Copyright (c) 2021 Red Hat, Inc. +# Copyright (c) Yandex +# +# 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 qemu_test import LinuxKernelTest, Asset, exec_command_and_wait_for_pattern + + +class HotPlugBlk(LinuxKernelTest): + + ASSET_KERNEL = Asset( + ('https://archives.fedoraproject.org/pub/archive/fedora/linux/releases' + '/31/Server/x86_64/os/images/pxeboot/vmlinuz'), + 'd4738d03dbbe083ca610d0821d0a8f1488bebbdccef54ce33e3adb35fda00129') + + ASSET_INITRD = Asset( + ('https://archives.fedoraproject.org/pub/archive/fedora/linux/releases' + '/31/Server/x86_64/os/images/pxeboot/initrd.img'), + '277cd6c7adf77c7e63d73bbb2cded8ef9e2d3a2f100000e92ff1f8396513cd8b') + + def blockdev_add(self) -> None: + self.vm.cmd('blockdev-add', **{ + 'driver': 'null-co', + 'size': 1073741824, + 'node-name': 'disk' + }) + + def assert_vda(self) -> None: + exec_command_and_wait_for_pattern(self, 'while ! test -e /sys/block/vda ;' + ' do sleep 0.2 ; done', '# ') + + def assert_no_vda(self) -> None: + exec_command_and_wait_for_pattern(self, 'while test -e /sys/block/vda ;' + ' do sleep 0.2 ; done', '# ') + + def plug(self) -> None: + args = { + 'driver': 'virtio-blk-pci', + 'drive': 'disk', + 'id': 'virtio-disk0', + 'bus': 'pci.1', + 'addr': '1', + } + + self.assert_no_vda() + self.vm.cmd('device_add', args) + self.wait_for_console_pattern('virtio_blk virtio0: [vda]') + self.assert_vda() + + def unplug(self) -> None: + self.vm.cmd('device_del', id='virtio-disk0') + + self.vm.event_wait('DEVICE_DELETED', 1.0, + match={'data': {'device': 'virtio-disk0'}}) + + self.assert_no_vda() + + def test(self) -> None: + self.require_accelerator('kvm') + self.set_machine('q35') + + self.vm.add_args('-accel', 'kvm') + self.vm.add_args('-device', 'pcie-pci-bridge,id=pci.1,bus=pcie.0') + 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.') + self.wait_for_console_pattern('# ') + + self.blockdev_add() + + self.plug() + self.unplug() + + +if __name__ == '__main__': + LinuxKernelTest.main() diff --git a/tests/functional/test_x86_64_hotplug_cpu.py b/tests/functional/test_x86_64_hotplug_cpu.py new file mode 100755 index 0000000..7b9200a --- /dev/null +++ b/tests/functional/test_x86_64_hotplug_cpu.py @@ -0,0 +1,71 @@ +#!/usr/bin/env python3 +# +# Functional test that hotplugs a CPU and checks it on a Linux guest +# +# Copyright (c) 2021 Red Hat, Inc. +# +# Author: +# Cleber Rosa <crosa@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. + +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') + + 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): + + 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.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) + + 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.wait_for_console_pattern('CPU1 has been hot-added') + + exec_command_and_wait_for_pattern(self, + 'cd /sys/devices/system/cpu/cpu1', + 'cpu1#') + + exec_command_and_wait_for_pattern(self, 'cd ..', prompt) + self.vm.cmd('device_del', id='c1') + + exec_command_and_wait_for_pattern(self, + 'while cd /sys/devices/system/cpu/cpu1 ;' + ' do sleep 0.2 ; done', + 'No such file or directory') + +if __name__ == '__main__': + LinuxKernelTest.main() diff --git a/tests/avocado/kvm_xen_guest.py b/tests/functional/test_x86_64_kvm_xen.py index f8cb458..a5d4450 100644..100755 --- a/tests/avocado/kvm_xen_guest.py +++ b/tests/functional/test_x86_64_kvm_xen.py @@ -1,3 +1,5 @@ +#!/usr/bin/env python3 +# # KVM Xen guest functional tests # # Copyright © 2021 Red Hat, Inc. @@ -9,23 +11,14 @@ # # SPDX-License-Identifier: GPL-2.0-or-later -import os - from qemu.machine import machine -from avocado_qemu import LinuxSSHMixIn -from avocado_qemu import QemuSystemTest -from avocado_qemu import wait_for_console_pattern +from qemu_test import QemuSystemTest, Asset, exec_command_and_wait_for_pattern +from qemu_test import wait_for_console_pattern -class KVMXenGuest(QemuSystemTest, LinuxSSHMixIn): - """ - :avocado: tags=arch:x86_64 - :avocado: tags=machine:q35 - :avocado: tags=accel:kvm - :avocado: tags=kvm_xen_guest - """ +class KVMXenGuest(QemuSystemTest): - KERNEL_DEFAULT = 'printk.time=0 root=/dev/xvda console=ttyS0' + KERNEL_DEFAULT = 'printk.time=0 root=/dev/xvda console=ttyS0 quiet' kernel_path = None kernel_params = None @@ -33,28 +26,28 @@ class KVMXenGuest(QemuSystemTest, LinuxSSHMixIn): # Fetch assets from the kvm-xen-guest subdir of my shared test # images directory on fileserver.linaro.org where you can find # build instructions for how they where assembled. - def get_asset(self, name, sha1): - base_url = ('https://fileserver.linaro.org/s/' - 'kE4nCFLdQcoBF9t/download?' - 'path=%2Fkvm-xen-guest&files=' ) - url = base_url + name - # use explicit name rather than failing to neatly parse the - # URL into a unique one - return self.fetch_asset(name=name, locations=(url), asset_hash=sha1) + ASSET_KERNEL = Asset( + ('https://fileserver.linaro.org/s/kE4nCFLdQcoBF9t/download?' + 'path=%2Fkvm-xen-guest&files=bzImage'), + 'ec0ad7bb8c33c5982baee0a75505fe7dbf29d3ff5d44258204d6307c6fe0132a') + + ASSET_ROOTFS = Asset( + ('https://fileserver.linaro.org/s/kE4nCFLdQcoBF9t/download?' + 'path=%2Fkvm-xen-guest&files=rootfs.ext4'), + 'b11045d649006c649c184e93339aaa41a8fe20a1a86620af70323252eb29e40b') def common_vm_setup(self): # We also catch lack of KVM_XEN support if we fail to launch self.require_accelerator("kvm") + self.require_netdev('user') self.vm.set_console() self.vm.add_args("-accel", "kvm,xen-version=0x4000a,kernel-irqchip=split") self.vm.add_args("-smp", "2") - self.kernel_path = self.get_asset("bzImage", - "367962983d0d32109998a70b45dcee4672d0b045") - self.rootfs = self.get_asset("rootfs.ext4", - "f1478401ea4b3fa2ea196396be44315bab2bb5e4") + self.kernel_path = self.ASSET_KERNEL.fetch() + self.rootfs = self.ASSET_ROOTFS.fetch() def run_and_check(self): self.vm.add_args('-kernel', self.kernel_path, @@ -68,10 +61,10 @@ class KVMXenGuest(QemuSystemTest, LinuxSSHMixIn): self.vm.launch() except machine.VMLaunchFailure as e: if "Xen HVM guest support not present" in e.output: - self.cancel("KVM Xen support is not present " - "(need v5.12+ kernel with CONFIG_KVM_XEN)") + self.skipTest("KVM Xen support is not present " + "(need v5.12+ kernel with CONFIG_KVM_XEN)") elif "Property 'kvm-accel.xen-version' not found" in e.output: - self.cancel("QEMU not built with CONFIG_XEN_EMU support") + self.skipTest("QEMU not built with CONFIG_XEN_EMU support") else: raise e @@ -79,93 +72,86 @@ class KVMXenGuest(QemuSystemTest, LinuxSSHMixIn): console_pattern = 'Starting dropbear sshd: OK' wait_for_console_pattern(self, console_pattern, 'Oops') self.log.info('sshd ready') - self.ssh_connect('root', '', False) - self.ssh_command('cat /proc/cmdline') - self.ssh_command('dmesg | grep -e "Grant table initialized"') + exec_command_and_wait_for_pattern(self, 'cat /proc/cmdline', 'xen') + exec_command_and_wait_for_pattern(self, 'dmesg | grep "Grant table"', + 'Grant table initialized') + wait_for_console_pattern(self, '#', 'Oops') def test_kvm_xen_guest(self): - """ - :avocado: tags=kvm_xen_guest - """ - self.common_vm_setup() self.kernel_params = (self.KERNEL_DEFAULT + ' xen_emul_unplug=ide-disks') self.run_and_check() - self.ssh_command('grep xen-pirq.*msi /proc/interrupts') + exec_command_and_wait_for_pattern(self, + 'grep xen-pirq.*msi /proc/interrupts', + 'virtio0-output') def test_kvm_xen_guest_nomsi(self): - """ - :avocado: tags=kvm_xen_guest_nomsi - """ - self.common_vm_setup() self.kernel_params = (self.KERNEL_DEFAULT + ' xen_emul_unplug=ide-disks pci=nomsi') self.run_and_check() - self.ssh_command('grep xen-pirq.* /proc/interrupts') + exec_command_and_wait_for_pattern(self, + 'grep xen-pirq.* /proc/interrupts', + 'virtio0') def test_kvm_xen_guest_noapic_nomsi(self): - """ - :avocado: tags=kvm_xen_guest_noapic_nomsi - """ - self.common_vm_setup() self.kernel_params = (self.KERNEL_DEFAULT + ' xen_emul_unplug=ide-disks noapic pci=nomsi') self.run_and_check() - self.ssh_command('grep xen-pirq /proc/interrupts') + exec_command_and_wait_for_pattern(self, + 'grep xen-pirq /proc/interrupts', + 'virtio0') def test_kvm_xen_guest_vapic(self): - """ - :avocado: tags=kvm_xen_guest_vapic - """ - self.common_vm_setup() self.vm.add_args('-cpu', 'host,+xen-vapic') self.kernel_params = (self.KERNEL_DEFAULT + ' xen_emul_unplug=ide-disks') self.run_and_check() - self.ssh_command('grep xen-pirq /proc/interrupts') - self.ssh_command('grep PCI-MSI /proc/interrupts') + exec_command_and_wait_for_pattern(self, + 'grep xen-pirq /proc/interrupts', + 'acpi') + wait_for_console_pattern(self, '#') + exec_command_and_wait_for_pattern(self, + 'grep PCI-MSI /proc/interrupts', + 'virtio0-output') def test_kvm_xen_guest_novector(self): - """ - :avocado: tags=kvm_xen_guest_novector - """ - self.common_vm_setup() self.kernel_params = (self.KERNEL_DEFAULT + ' xen_emul_unplug=ide-disks' + ' xen_no_vector_callback') self.run_and_check() - self.ssh_command('grep xen-platform-pci /proc/interrupts') + exec_command_and_wait_for_pattern(self, + 'grep xen-platform-pci /proc/interrupts', + 'fasteoi') def test_kvm_xen_guest_novector_nomsi(self): - """ - :avocado: tags=kvm_xen_guest_novector_nomsi - """ - self.common_vm_setup() self.kernel_params = (self.KERNEL_DEFAULT + ' xen_emul_unplug=ide-disks pci=nomsi' + ' xen_no_vector_callback') self.run_and_check() - self.ssh_command('grep xen-platform-pci /proc/interrupts') + exec_command_and_wait_for_pattern(self, + 'grep xen-platform-pci /proc/interrupts', + 'IO-APIC') def test_kvm_xen_guest_novector_noapic(self): - """ - :avocado: tags=kvm_xen_guest_novector_noapic - """ - self.common_vm_setup() self.kernel_params = (self.KERNEL_DEFAULT + ' xen_emul_unplug=ide-disks' + ' xen_no_vector_callback noapic') self.run_and_check() - self.ssh_command('grep xen-platform-pci /proc/interrupts') + exec_command_and_wait_for_pattern(self, + 'grep xen-platform-pci /proc/interrupts', + 'XT-PIC') + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/functional/test_x86_64_replay.py b/tests/functional/test_x86_64_replay.py new file mode 100755 index 0000000..27287d4 --- /dev/null +++ b/tests/functional/test_x86_64_replay.py @@ -0,0 +1,58 @@ +#!/usr/bin/env python3 +# +# Replay test that boots a Linux kernel on x86_64 machines +# and checks the console +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from subprocess import check_call, DEVNULL + +from qemu_test import Asset, skipFlakyTest, get_qemu_img +from replay_kernel import ReplayKernelBase + + +class X86Replay(ReplayKernelBase): + + ASSET_KERNEL = Asset( + 'https://storage.tuxboot.com/buildroot/20241119/x86_64/bzImage', + 'f57bfc6553bcd6e0a54aab86095bf642b33b5571d14e3af1731b18c87ed5aef8') + + ASSET_ROOTFS = Asset( + 'https://storage.tuxboot.com/buildroot/20241119/x86_64/rootfs.ext4.zst', + '4b8b2a99117519c5290e1202cb36eb6c7aaba92b357b5160f5970cf5fb78a751') + + def do_test_x86(self, machine, blkdevice, devroot): + self.require_netdev('user') + self.set_machine(machine) + self.cpu="Nehalem" + kernel_path = self.ASSET_KERNEL.fetch() + + raw_disk = self.uncompress(self.ASSET_ROOTFS) + disk = self.scratch_file('scratch.qcow2') + qemu_img = get_qemu_img(self) + check_call([qemu_img, 'create', '-f', 'qcow2', '-b', raw_disk, + '-F', 'raw', disk], stdout=DEVNULL, stderr=DEVNULL) + + args = ('-drive', 'file=%s,snapshot=on,id=hd0,if=none' % disk, + '-drive', 'driver=blkreplay,id=hd0-rr,if=none,image=hd0', + '-device', '%s,drive=hd0-rr' % blkdevice, + '-netdev', 'user,id=vnet,hostfwd=:127.0.0.1:0-:22', + '-device', 'virtio-net,netdev=vnet', + '-object', 'filter-replay,id=replay,netdev=vnet') + + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + f"console=ttyS0 root=/dev/{devroot}") + console_pattern = 'Welcome to TuxTest' + self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=5, + args=args) + + @skipFlakyTest('https://gitlab.com/qemu-project/qemu/-/issues/2094') + def test_pc(self): + self.do_test_x86('pc', 'virtio-blk', 'vda') + + def test_q35(self): + self.do_test_x86('q35', 'ide-hd', 'sda') + + +if __name__ == '__main__': + ReplayKernelBase.main() diff --git a/tests/functional/test_x86_64_reverse_debug.py b/tests/functional/test_x86_64_reverse_debug.py new file mode 100755 index 0000000..d713e91 --- /dev/null +++ b/tests/functional/test_x86_64_reverse_debug.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python3 +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Reverse debugging test +# +# Copyright (c) 2020 ISP RAS +# +# Author: +# Pavel Dovgalyuk <Pavel.Dovgalyuk@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. + +from qemu_test import skipIfMissingImports, skipFlakyTest +from reverse_debugging import ReverseDebugging + + +@skipIfMissingImports('avocado.utils') +class ReverseDebugging_X86_64(ReverseDebugging): + + REG_PC = 0x10 + REG_CS = 0x12 + def get_pc(self, g): + return self.get_reg_le(g, self.REG_PC) \ + + self.get_reg_le(g, self.REG_CS) * 0x10 + + @skipFlakyTest("https://gitlab.com/qemu-project/qemu/-/issues/2922") + def test_x86_64_pc(self): + self.set_machine('pc') + # start with BIOS only + self.reverse_debugging() + + +if __name__ == '__main__': + ReverseDebugging.main() diff --git a/tests/functional/test_x86_64_tuxrun.py b/tests/functional/test_x86_64_tuxrun.py new file mode 100755 index 0000000..fcbc62b --- /dev/null +++ b/tests/functional/test_x86_64_tuxrun.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python3 +# +# Functional test that boots known good tuxboot images the same way +# that tuxrun (www.tuxrun.org) does. This tool is used by things like +# the LKFT project to run regression tests on kernels. +# +# Copyright (c) 2023 Linaro Ltd. +# +# Author: +# Alex Bennée <alex.bennee@linaro.org> +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import Asset +from qemu_test.tuxruntest import TuxRunBaselineTest + +class TuxRunX86Test(TuxRunBaselineTest): + + ASSET_X86_64_KERNEL = Asset( + 'https://storage.tuxboot.com/buildroot/20241119/x86_64/bzImage', + 'f57bfc6553bcd6e0a54aab86095bf642b33b5571d14e3af1731b18c87ed5aef8') + ASSET_X86_64_ROOTFS = Asset( + 'https://storage.tuxboot.com/buildroot/20241119/x86_64/rootfs.ext4.zst', + '4b8b2a99117519c5290e1202cb36eb6c7aaba92b357b5160f5970cf5fb78a751') + + def test_x86_64(self): + self.set_machine('q35') + self.cpu="Nehalem" + self.root='sda' + self.wait_for_shutdown=False + self.common_tuxrun(kernel_asset=self.ASSET_X86_64_KERNEL, + rootfs_asset=self.ASSET_X86_64_ROOTFS, + drive="driver=ide-hd,bus=ide.0,unit=0") + +if __name__ == '__main__': + TuxRunBaselineTest.main() diff --git a/tests/functional/test_xtensa_lx60.py b/tests/functional/test_xtensa_lx60.py new file mode 100755 index 0000000..147c920 --- /dev/null +++ b/tests/functional/test_xtensa_lx60.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python3 +# +# Functional test that boots a Linux kernel on an xtensa lx650 machine +# and checks the console +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import LinuxKernelTest, Asset + + +class XTensaLX60Test(LinuxKernelTest): + + ASSET_DAY02 = Asset( + 'https://qemu-advcal.gitlab.io/qac-best-of-multiarch/download/day02.tar.xz', + '68ff07f9b3fd3df36d015eb46299ba44748e94bfbb2d5295fddc1a8d4a9fd324') + + def test_xtensa_lx60(self): + self.set_machine('lx60') + self.cpu = 'dc233c' + self.archive_extract(self.ASSET_DAY02) + self.launch_kernel(self.scratch_file('day02', + 'santas-sleigh-ride.elf'), + wait_for='QEMU advent calendar') + +if __name__ == '__main__': + LinuxKernelTest.main() diff --git a/tests/functional/test_xtensa_replay.py b/tests/functional/test_xtensa_replay.py new file mode 100755 index 0000000..eb00a3b --- /dev/null +++ b/tests/functional/test_xtensa_replay.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python3 +# +# Replay test that boots a Linux kernel on an xtensa lx650 machine +# and checks the console +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import Asset +from replay_kernel import ReplayKernelBase + + +class XTensaReplay(ReplayKernelBase): + + ASSET_DAY02 = Asset( + 'https://qemu-advcal.gitlab.io/qac-best-of-multiarch/download/day02.tar.xz', + '68ff07f9b3fd3df36d015eb46299ba44748e94bfbb2d5295fddc1a8d4a9fd324') + + def test_replay(self): + self.set_machine('lx60') + self.cpu = 'dc233c' + kernel_path = self.archive_extract(self.ASSET_DAY02, + member='day02/santas-sleigh-ride.elf') + self.run_rr(kernel_path, self.REPLAY_KERNEL_COMMAND_LINE, + 'QEMU advent calendar') + + +if __name__ == '__main__': + ReplayKernelBase.main() diff --git a/tests/guest-debug/run-test.py b/tests/guest-debug/run-test.py index 368ff8a..75e9c92 100755 --- a/tests/guest-debug/run-test.py +++ b/tests/guest-debug/run-test.py @@ -27,11 +27,17 @@ def get_args(): parser.add_argument("--binary", help="Binary to debug", required=True) parser.add_argument("--test", help="GDB test script") + parser.add_argument('test_args', nargs='*', + help="Additional args for GDB test script. " + "The args should be preceded by -- to avoid confusion " + "with flags for runner script") parser.add_argument("--gdb", help="The gdb binary to use", default=None) parser.add_argument("--gdb-args", help="Additional gdb arguments") parser.add_argument("--output", help="A file to redirect output to") parser.add_argument("--stderr", help="A file to redirect stderr to") + parser.add_argument("--no-suspend", action="store_true", + help="Ask the binary to not wait for GDB connection") return parser.parse_args() @@ -69,10 +75,19 @@ if __name__ == '__main__': # Launch QEMU with binary if "system" in args.qemu: + if args.no_suspend: + suspend = '' + else: + suspend = ' -S' cmd = f'{args.qemu} {args.qargs} {args.binary}' \ - f' -S -gdb unix:path={socket_name},server=on' + f'{suspend} -gdb unix:path={socket_name},server=on' else: - cmd = f'{args.qemu} {args.qargs} -g {socket_name} {args.binary}' + if args.no_suspend: + suspend = ',suspend=n' + else: + suspend = '' + cmd = f'{args.qemu} {args.qargs} -g {socket_name}{suspend}' \ + f' {args.binary}' log(output, "QEMU CMD: %s" % (cmd)) inferior = subprocess.Popen(shlex.split(cmd)) @@ -91,6 +106,8 @@ if __name__ == '__main__': gdb_cmd += " -ex 'target remote %s'" % (socket_name) # finally the test script itself if args.test: + if args.test_args: + gdb_cmd += f" -ex \"py sys.argv={args.test_args}\"" gdb_cmd += " -x %s" % (args.test) diff --git a/tests/guest-debug/test_gdbstub.py b/tests/guest-debug/test_gdbstub.py index 46fbf98..4f08089 100644 --- a/tests/guest-debug/test_gdbstub.py +++ b/tests/guest-debug/test_gdbstub.py @@ -2,6 +2,7 @@ """ from __future__ import print_function +import argparse import gdb import os import sys @@ -10,6 +11,16 @@ import traceback fail_count = 0 +def gdb_exit(status): + gdb.execute(f"exit {status}") + + +class arg_parser(argparse.ArgumentParser): + def exit(self, status=None, message=""): + print("Wrong GDB script test argument! " + message) + gdb_exit(1) + + def report(cond, msg): """Report success/fail of a test""" if cond: @@ -33,11 +44,11 @@ def main(test, expected_arch=None): "connected to {}".format(expected_arch)) except (gdb.error, AttributeError): print("SKIP: not connected") - exit(0) + gdb_exit(0) if gdb.parse_and_eval("$pc") == 0: print("SKIP: PC not set") - exit(0) + gdb_exit(0) try: test() @@ -57,4 +68,4 @@ def main(test, expected_arch=None): pass print("All tests complete: {} failures".format(fail_count)) - gdb.execute(f"exit {fail_count}") + gdb_exit(fail_count) diff --git a/tests/include/meson.build b/tests/include/meson.build index 9abba30..8e8d1ec 100644 --- a/tests/include/meson.build +++ b/tests/include/meson.build @@ -13,4 +13,4 @@ test_qapi_outputs_extra = [ test_qapi_files_extra = custom_target('QAPI test (include)', output: test_qapi_outputs_extra, input: test_qapi_files, - command: 'true') + command: [python, '-c', '']) diff --git a/tests/lcitool/libvirt-ci b/tests/lcitool/libvirt-ci -Subproject 789b4601bce4e01f43fdb6ad4ce5ab4e4667444 +Subproject 18c4bfe02c467e5639bf9a687139735ccd7a3ff diff --git a/tests/lcitool/mappings.yml b/tests/lcitool/mappings.yml index 03b974a..8f0e95e 100644 --- a/tests/lcitool/mappings.yml +++ b/tests/lcitool/mappings.yml @@ -1,9 +1,17 @@ mappings: + # Too old on Ubuntu 22.04; we install it from cargo instead + bindgen: + Ubuntu2204: + flake8: OpenSUSELeap15: meson: OpenSUSELeap15: + # Use Meson from PyPI wherever Rust is enabled + Debian: + Fedora: + Ubuntu: python3: OpenSUSELeap15: python311-base @@ -60,10 +68,15 @@ mappings: python3-wheel: OpenSUSELeap15: python311-pip + rust: + Debian12: rustc-web + Ubuntu2204: rustc-1.77 + Ubuntu2404: rustc-1.77 + pypi_mappings: # Request more recent version meson: - default: meson==0.63.2 + default: meson==1.8.1 # Drop packages that need devel headers python3-numpy: diff --git a/tests/lcitool/projects/qemu.yml b/tests/lcitool/projects/qemu.yml index 252e871..c07242f 100644 --- a/tests/lcitool/projects/qemu.yml +++ b/tests/lcitool/projects/qemu.yml @@ -3,6 +3,7 @@ packages: - alsa - bash - bc + - bindgen - bison - brlapi - bzip2 @@ -42,6 +43,7 @@ packages: - libc-static - libcacard - libcap-ng + - libcbor - libcurl - libdrm - libepoxy @@ -101,6 +103,7 @@ packages: - python3-tomli - python3-venv - rpm2cpio + - rust - sdl2 - sdl2-image - sed @@ -119,6 +122,7 @@ packages: - usbredir - virglrenderer - vte + - vulkan-tools - which - xen - xorriso diff --git a/tests/lcitool/refresh b/tests/lcitool/refresh index ac803e3..d3488b2 100755 --- a/tests/lcitool/refresh +++ b/tests/lcitool/refresh @@ -116,6 +116,42 @@ debian12_extras = [ "ENV QEMU_CONFIGURE_OPTS --enable-netmap\n" ] +# Based on the hub.docker.com/library/rust Dockerfiles +fedora_rustup_nightly_extras = [ + "RUN dnf install -y wget\n", + "ENV RUSTUP_HOME=/usr/local/rustup CARGO_HOME=/usr/local/cargo\n", + "ENV RUSTC=/usr/local/rustup/toolchains/nightly-x86_64-unknown-linux-gnu/bin/rustc\n", + "ENV RUSTDOC=/usr/local/rustup/toolchains/nightly-x86_64-unknown-linux-gnu/bin/rustdoc\n", + "ENV CARGO=/usr/local/rustup/toolchains/nightly-x86_64-unknown-linux-gnu/bin/cargo\n", + "RUN set -eux && \\\n", + " rustArch='x86_64-unknown-linux-gnu' && \\\n", + " rustupSha256='6aeece6993e902708983b209d04c0d1dbb14ebb405ddb87def578d41f920f56d' && \\\n", + ' url="https://static.rust-lang.org/rustup/archive/1.27.1/${rustArch}/rustup-init" && \\\n', + ' wget "$url" && \\\n', + ' echo "${rustupSha256} *rustup-init" | sha256sum -c - && \\\n', + " chmod +x rustup-init && \\\n", + " ./rustup-init -y --no-modify-path --profile default --default-toolchain nightly --default-host ${rustArch} && \\\n", + " chmod -R a+w $RUSTUP_HOME $CARGO_HOME && \\\n", + " /usr/local/cargo/bin/rustup --version && \\\n", + " /usr/local/cargo/bin/rustup run nightly cargo --version && \\\n", + " /usr/local/cargo/bin/rustup run nightly rustc --version && \\\n", + ' test "$CARGO" = "$(/usr/local/cargo/bin/rustup +nightly which cargo)" && \\\n', + ' test "$RUSTDOC" = "$(/usr/local/cargo/bin/rustup +nightly which rustdoc)" && \\\n', + ' test "$RUSTC" = "$(/usr/local/cargo/bin/rustup +nightly which rustc)"\n', + 'ENV PATH=$CARGO_HOME/bin:$PATH\n', + 'RUN /usr/local/cargo/bin/rustup run nightly cargo install bindgen-cli\n', + 'RUN $CARGO --list\n', +] + +ubuntu2204_rust_extras = [ + "ENV RUSTC=/usr/bin/rustc-1.77\n", + "ENV RUSTDOC=/usr/bin/rustdoc-1.77\n", + "ENV CARGO_HOME=/usr/local/cargo\n", + 'ENV PATH=$CARGO_HOME/bin:$PATH\n', + "RUN DEBIAN_FRONTEND=noninteractive eatmydata \\\n", + " apt install -y --no-install-recommends cargo\n", + 'RUN cargo install bindgen-cli\n', +] def cross_build(prefix, targets): conf = "ENV QEMU_CONFIGURE_OPTS --cross-prefix=%s\n" % (prefix) @@ -131,13 +167,20 @@ try: # # Standard native builds # - generate_dockerfile("alpine", "alpine-319") + generate_dockerfile("alpine", "alpine-321") generate_dockerfile("centos9", "centos-stream-9") generate_dockerfile("debian", "debian-12", trailer="".join(debian12_extras)) generate_dockerfile("fedora", "fedora-40") generate_dockerfile("opensuse-leap", "opensuse-leap-15") - generate_dockerfile("ubuntu2204", "ubuntu-2204") + generate_dockerfile("ubuntu2204", "ubuntu-2204", + trailer="".join(ubuntu2204_rust_extras)) + + # + # Non-fatal Rust-enabled build + # + generate_dockerfile("fedora-rust-nightly", "fedora-40", + trailer="".join(fedora_rustup_nightly_extras)) # # Cross compiling builds @@ -154,30 +197,24 @@ try: trailer=cross_build("aarch64-linux-gnu-", "aarch64-softmmu,aarch64-linux-user")) - # migration to bookworm stalled: https://lists.debian.org/debian-arm/2023/09/msg00006.html - generate_dockerfile("debian-armel-cross", "debian-11", - cross="armv6l", - trailer=cross_build("arm-linux-gnueabi-", - "arm-softmmu,arm-linux-user,armeb-linux-user")) - generate_dockerfile("debian-armhf-cross", "debian-12", cross="armv7l", trailer=cross_build("arm-linux-gnueabihf-", "arm-softmmu,arm-linux-user")) - generate_dockerfile("debian-i686-cross", "debian-11", + generate_dockerfile("debian-i686-cross", "debian-12", cross="i686", trailer=cross_build("i686-linux-gnu-", "x86_64-softmmu," "x86_64-linux-user," "i386-softmmu,i386-linux-user")) - generate_dockerfile("debian-mips64el-cross", "debian-11", + generate_dockerfile("debian-mips64el-cross", "debian-12", cross="mips64el", trailer=cross_build("mips64el-linux-gnuabi64-", "mips64el-softmmu,mips64el-linux-user")) - generate_dockerfile("debian-mipsel-cross", "debian-11", + generate_dockerfile("debian-mipsel-cross", "debian-12", cross="mipsel", trailer=cross_build("mipsel-linux-gnu-", "mipsel-softmmu,mipsel-linux-user")) @@ -187,7 +224,9 @@ try: trailer=cross_build("powerpc64le-linux-gnu-", "ppc64-softmmu,ppc64-linux-user")) - generate_dockerfile("debian-riscv64-cross", "debian-sid", + # while not yet a release architecture the packages are still + # build while part of testing + generate_dockerfile("debian-riscv64-cross", "debian-13", project="qemu-minimal", cross="riscv64", trailer=cross_build("riscv64-linux-gnu-", @@ -207,14 +246,13 @@ try: # # Cirrus packages lists for GitLab # - generate_cirrus("freebsd-13") - generate_cirrus("macos-13") + generate_cirrus("freebsd-14") generate_cirrus("macos-14") # # VM packages lists # - generate_pkglist("freebsd", "freebsd-13") + generate_pkglist("freebsd", "freebsd-14") # # Ansible package lists diff --git a/tests/meson.build b/tests/meson.build index 907a4c1..c596192 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -16,6 +16,8 @@ test_qapi_outputs = [ 'test-qapi-events-sub-sub-module.h', 'test-qapi-events.c', 'test-qapi-events.h', + 'test-qapi-features.c', + 'test-qapi-features.h', 'test-qapi-init-commands.c', 'test-qapi-init-commands.h', 'test-qapi-introspect.c', @@ -84,5 +86,5 @@ endif subdir('unit') subdir('qapi-schema') subdir('qtest') -subdir('migration') +subdir('migration-stress') subdir('functional') diff --git a/tests/migration/guestperf-batch.py b/tests/migration-stress/guestperf-batch.py index 9485eef..9485eef 100755 --- a/tests/migration/guestperf-batch.py +++ b/tests/migration-stress/guestperf-batch.py diff --git a/tests/migration/guestperf-plot.py b/tests/migration-stress/guestperf-plot.py index 32977b4..32977b4 100755 --- a/tests/migration/guestperf-plot.py +++ b/tests/migration-stress/guestperf-plot.py diff --git a/tests/migration/guestperf.py b/tests/migration-stress/guestperf.py index 07182f2..07182f2 100755 --- a/tests/migration/guestperf.py +++ b/tests/migration-stress/guestperf.py diff --git a/tests/migration/guestperf/__init__.py b/tests/migration-stress/guestperf/__init__.py index e69de29..e69de29 100644 --- a/tests/migration/guestperf/__init__.py +++ b/tests/migration-stress/guestperf/__init__.py diff --git a/tests/migration/guestperf/comparison.py b/tests/migration-stress/guestperf/comparison.py index 42cc037..dee3ac2 100644 --- a/tests/migration/guestperf/comparison.py +++ b/tests/migration-stress/guestperf/comparison.py @@ -127,7 +127,7 @@ COMPARISONS = [ # varying numbers of channels Comparison("compr-multifd", scenarios = [ Scenario("compr-multifd-channels-4", - multifd=True, multifd_channels=2), + multifd=True, multifd_channels=4), Scenario("compr-multifd-channels-8", multifd=True, multifd_channels=8), Scenario("compr-multifd-channels-32", @@ -158,4 +158,17 @@ COMPARISONS = [ Scenario("compr-dirty-limit-50MB", dirty_limit=True, vcpu_dirty_limit=50), ]), + + # Looking at effect of multifd with + # different compression algorithms + Comparison("compr-multifd-compression", scenarios = [ + Scenario("compr-multifd-compression-zlib", + multifd=True, multifd_channels=2, multifd_compression="zlib"), + Scenario("compr-multifd-compression-zstd", + multifd=True, multifd_channels=2, multifd_compression="zstd"), + Scenario("compr-multifd-compression-qpl", + multifd=True, multifd_channels=2, multifd_compression="qpl"), + Scenario("compr-multifd-compression-uadk", + multifd=True, multifd_channels=2, multifd_compression="uadk"), + ]), ] diff --git a/tests/migration/guestperf/engine.py b/tests/migration-stress/guestperf/engine.py index 608d727..d8462db 100644 --- a/tests/migration/guestperf/engine.py +++ b/tests/migration-stress/guestperf/engine.py @@ -24,13 +24,15 @@ import sys import time from guestperf.progress import Progress, ProgressStats -from guestperf.report import Report +from guestperf.report import Report, ReportResult from guestperf.timings import TimingRecord, Timings sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', '..', 'python')) from qemu.machine import QEMUMachine +# multifd supported compression algorithms +MULTIFD_CMP_ALGS = ("zlib", "zstd", "qpl", "uadk") class Engine(object): @@ -106,7 +108,8 @@ class Engine(object): info.get("dirty-limit-ring-full-time", 0), ) - def _migrate(self, hardware, scenario, src, dst, connect_uri): + def _migrate(self, hardware, scenario, src, + dst, connect_uri, defer_migrate): src_qemu_time = [] src_vcpu_time = [] src_pid = src.get_pid() @@ -190,6 +193,12 @@ class Engine(object): scenario._compression_xbzrle_cache)) if scenario._multifd: + if (scenario._multifd_compression and + (scenario._multifd_compression not in MULTIFD_CMP_ALGS)): + raise Exception("unsupported multifd compression " + "algorithm: %s" % + scenario._multifd_compression) + resp = src.cmd("migrate-set-capabilities", capabilities = [ { "capability": "multifd", @@ -205,6 +214,12 @@ class Engine(object): resp = dst.cmd("migrate-set-parameters", multifd_channels=scenario._multifd_channels) + if scenario._multifd_compression: + resp = src.cmd("migrate-set-parameters", + multifd_compression=scenario._multifd_compression) + resp = dst.cmd("migrate-set-parameters", + multifd_compression=scenario._multifd_compression) + if scenario._dirty_limit: if not hardware._dirty_ring_size: raise Exception("dirty ring size must be configured when " @@ -220,6 +235,8 @@ class Engine(object): resp = src.cmd("migrate-set-parameters", vcpu_dirty_limit=scenario._vcpu_dirty_limit) + if defer_migrate: + resp = dst.cmd("migrate-incoming", uri=connect_uri) resp = src.cmd("migrate", uri=connect_uri) post_copy = False @@ -259,7 +276,11 @@ class Engine(object): src_vcpu_time.extend(self._vcpu_timing(src_pid, src_threads)) sleep_secs -= 1 - return [progress_history, src_qemu_time, src_vcpu_time] + result = ReportResult() + if progress._status == "completed" and not paused: + result = ReportResult(True) + + return [progress_history, src_qemu_time, src_vcpu_time, result] if self._verbose and (loop % 20) == 0: print("Iter %d: remain %5dMB of %5dMB (total %5dMB @ %5dMb/sec)" % ( @@ -373,11 +394,14 @@ class Engine(object): def _get_src_args(self, hardware): return self._get_common_args(hardware) - def _get_dst_args(self, hardware, uri): + def _get_dst_args(self, hardware, uri, defer_migrate): tunnelled = False if self._dst_host != "localhost": tunnelled = True argv = self._get_common_args(hardware, tunnelled) + + if defer_migrate: + return argv + ["-incoming", "defer"] return argv + ["-incoming", uri] @staticmethod @@ -424,6 +448,7 @@ class Engine(object): def run(self, hardware, scenario, result_dir=os.getcwd()): abs_result_dir = os.path.join(result_dir, scenario._name) + defer_migrate = False if self._transport == "tcp": uri = "tcp:%s:9000" % self._dst_host @@ -439,6 +464,9 @@ class Engine(object): except: pass + if scenario._multifd: + defer_migrate = True + if self._dst_host != "localhost": dstmonaddr = ("localhost", 9001) else: @@ -452,7 +480,7 @@ class Engine(object): monitor_address=srcmonaddr) dst = QEMUMachine(self._binary, - args=self._get_dst_args(hardware, uri), + args=self._get_dst_args(hardware, uri, defer_migrate), wrapper=self._get_dst_wrapper(hardware), name="qemu-dst-%d" % os.getpid(), monitor_address=dstmonaddr) @@ -461,10 +489,12 @@ class Engine(object): src.launch() dst.launch() - ret = self._migrate(hardware, scenario, src, dst, uri) + ret = self._migrate(hardware, scenario, src, + dst, uri, defer_migrate) progress_history = ret[0] qemu_timings = ret[1] vcpu_timings = ret[2] + result = ret[3] if uri[0:5] == "unix:" and os.path.exists(uri[5:]): os.remove(uri[5:]) @@ -484,6 +514,7 @@ class Engine(object): Timings(self._get_timings(src) + self._get_timings(dst)), Timings(qemu_timings), Timings(vcpu_timings), + result, self._binary, self._dst_host, self._kernel, self._initrd, self._transport, self._sleep) except Exception as e: diff --git a/tests/migration/guestperf/hardware.py b/tests/migration-stress/guestperf/hardware.py index f779cc0..f779cc0 100644 --- a/tests/migration/guestperf/hardware.py +++ b/tests/migration-stress/guestperf/hardware.py diff --git a/tests/migration/guestperf/plot.py b/tests/migration-stress/guestperf/plot.py index 30b3f66..30b3f66 100644 --- a/tests/migration/guestperf/plot.py +++ b/tests/migration-stress/guestperf/plot.py diff --git a/tests/migration/guestperf/progress.py b/tests/migration-stress/guestperf/progress.py index d490584..d490584 100644 --- a/tests/migration/guestperf/progress.py +++ b/tests/migration-stress/guestperf/progress.py diff --git a/tests/migration/guestperf/report.py b/tests/migration-stress/guestperf/report.py index 1efd40c..e135e01 100644 --- a/tests/migration/guestperf/report.py +++ b/tests/migration-stress/guestperf/report.py @@ -24,6 +24,22 @@ from guestperf.scenario import Scenario from guestperf.progress import Progress from guestperf.timings import Timings +class ReportResult(object): + + def __init__(self, success=False): + self._success = success + + def serialize(self): + return { + "success": self._success, + } + + @classmethod + def deserialize(cls, data): + return cls( + data["success"]) + + class Report(object): def __init__(self, @@ -33,6 +49,7 @@ class Report(object): guest_timings, qemu_timings, vcpu_timings, + result, binary, dst_host, kernel, @@ -46,6 +63,7 @@ class Report(object): self._guest_timings = guest_timings self._qemu_timings = qemu_timings self._vcpu_timings = vcpu_timings + self._result = result self._binary = binary self._dst_host = dst_host self._kernel = kernel @@ -61,6 +79,7 @@ class Report(object): "guest_timings": self._guest_timings.serialize(), "qemu_timings": self._qemu_timings.serialize(), "vcpu_timings": self._vcpu_timings.serialize(), + "result": self._result.serialize(), "binary": self._binary, "dst_host": self._dst_host, "kernel": self._kernel, @@ -78,6 +97,7 @@ class Report(object): Timings.deserialize(data["guest_timings"]), Timings.deserialize(data["qemu_timings"]), Timings.deserialize(data["vcpu_timings"]), + ReportResult.deserialize(data["result"]), data["binary"], data["dst_host"], data["kernel"], diff --git a/tests/migration/guestperf/scenario.py b/tests/migration-stress/guestperf/scenario.py index 154c4f5..4be7faf 100644 --- a/tests/migration/guestperf/scenario.py +++ b/tests/migration-stress/guestperf/scenario.py @@ -30,7 +30,7 @@ class Scenario(object): auto_converge=False, auto_converge_step=10, compression_mt=False, compression_mt_threads=1, compression_xbzrle=False, compression_xbzrle_cache=10, - multifd=False, multifd_channels=2, + multifd=False, multifd_channels=2, multifd_compression="", dirty_limit=False, x_vcpu_dirty_limit_period=500, vcpu_dirty_limit=1): @@ -61,6 +61,7 @@ class Scenario(object): self._multifd = multifd self._multifd_channels = multifd_channels + self._multifd_compression = multifd_compression self._dirty_limit = dirty_limit self._x_vcpu_dirty_limit_period = x_vcpu_dirty_limit_period @@ -85,6 +86,7 @@ class Scenario(object): "compression_xbzrle_cache": self._compression_xbzrle_cache, "multifd": self._multifd, "multifd_channels": self._multifd_channels, + "multifd_compression": self._multifd_compression, "dirty_limit": self._dirty_limit, "x_vcpu_dirty_limit_period": self._x_vcpu_dirty_limit_period, "vcpu_dirty_limit": self._vcpu_dirty_limit, @@ -109,4 +111,5 @@ class Scenario(object): data["compression_xbzrle"], data["compression_xbzrle_cache"], data["multifd"], - data["multifd_channels"]) + data["multifd_channels"], + data["multifd_compression"]) diff --git a/tests/migration/guestperf/shell.py b/tests/migration-stress/guestperf/shell.py index c85d89e..63bbe32 100644 --- a/tests/migration/guestperf/shell.py +++ b/tests/migration-stress/guestperf/shell.py @@ -46,7 +46,8 @@ class BaseShell(object): parser.add_argument("--binary", dest="binary", default="/usr/bin/qemu-system-x86_64") parser.add_argument("--dst-host", dest="dst_host", default="localhost") parser.add_argument("--kernel", dest="kernel", default="/boot/vmlinuz-%s" % platform.release()) - parser.add_argument("--initrd", dest="initrd", default="tests/migration/initrd-stress.img") + parser.add_argument("--initrd", dest="initrd", + default="tests/migration-stress/initrd-stress.img") parser.add_argument("--transport", dest="transport", default="unix") @@ -130,6 +131,8 @@ class Shell(BaseShell): action="store_true") parser.add_argument("--multifd-channels", dest="multifd_channels", default=2, type=int) + parser.add_argument("--multifd-compression", dest="multifd_compression", + default="") parser.add_argument("--dirty-limit", dest="dirty_limit", default=False, action="store_true") @@ -166,6 +169,7 @@ class Shell(BaseShell): multifd=args.multifd, multifd_channels=args.multifd_channels, + multifd_compression=args.multifd_compression, dirty_limit=args.dirty_limit, x_vcpu_dirty_limit_period=\ diff --git a/tests/migration/guestperf/timings.py b/tests/migration-stress/guestperf/timings.py index 2374010..2374010 100644 --- a/tests/migration/guestperf/timings.py +++ b/tests/migration-stress/guestperf/timings.py diff --git a/tests/migration/initrd-stress.sh b/tests/migration-stress/initrd-stress.sh index 0f20ac2..0f20ac2 100755 --- a/tests/migration/initrd-stress.sh +++ b/tests/migration-stress/initrd-stress.sh diff --git a/tests/migration/meson.build b/tests/migration-stress/meson.build index a91aa61..a91aa61 100644 --- a/tests/migration/meson.build +++ b/tests/migration-stress/meson.build diff --git a/tests/migration/stress.c b/tests/migration-stress/stress.c index 88acf8d..88acf8d 100644 --- a/tests/migration/stress.c +++ b/tests/migration-stress/stress.c diff --git a/tests/qapi-schema/alternate-array.out b/tests/qapi-schema/alternate-array.out index a657d85..2f30973 100644 --- a/tests/qapi-schema/alternate-array.out +++ b/tests/qapi-schema/alternate-array.out @@ -1,7 +1,6 @@ module ./builtin object q_empty enum QType - prefix QTYPE member none member qnull member qnum diff --git a/tests/qapi-schema/comments.out b/tests/qapi-schema/comments.out index ce4f6a4..937070c 100644 --- a/tests/qapi-schema/comments.out +++ b/tests/qapi-schema/comments.out @@ -1,7 +1,6 @@ module ./builtin object q_empty enum QType - prefix QTYPE member none member qnull member qnum diff --git a/tests/qapi-schema/doc-good.json b/tests/qapi-schema/doc-good.json index f64bf38..14b808f 100644 --- a/tests/qapi-schema/doc-good.json +++ b/tests/qapi-schema/doc-good.json @@ -12,6 +12,10 @@ ## ## +# Just text, no heading. +## + +## # == Subsection # # *with emphasis* @@ -208,7 +212,7 @@ # # -> "this example" # -# <- "has no title" +# <- ... has no title ... ## { 'command': 'cmd-boxed', 'boxed': true, 'data': 'Object', diff --git a/tests/qapi-schema/doc-good.out b/tests/qapi-schema/doc-good.out index 6d24f11..dc8352e 100644 --- a/tests/qapi-schema/doc-good.out +++ b/tests/qapi-schema/doc-good.out @@ -1,7 +1,6 @@ module ./builtin object q_empty enum QType - prefix QTYPE member none member qnull member qnum @@ -59,6 +58,9 @@ doc freeform = Section doc freeform body= +Just text, no heading. +doc freeform + body= == Subsection *with emphasis* @@ -111,7 +113,7 @@ The _one_ {and only}, description on the same line Also _one_ {and only} feature=enum-member-feat a member feature - section=None + section=Plain @two is undocumented doc symbol=Base body= @@ -169,15 +171,15 @@ description starts on the same line a feature feature=cmd-feat2 another feature - section=None + section=Plain .. note:: @arg3 is undocumented section=Returns @Object section=Errors some - section=TODO + section=Todo frobnicate - section=None + section=Plain .. admonition:: Notes - Lorem ipsum dolor sit amet @@ -210,12 +212,12 @@ If you're bored enough to read this, go see a video of boxed cats a feature feature=cmd-feat2 another feature - section=None + section=Plain .. qmp-example:: -> "this example" - <- "has no title" + <- ... has no title ... doc symbol=EVT_BOXED body= diff --git a/tests/qapi-schema/doc-good.txt b/tests/qapi-schema/doc-good.txt index cb37db6..17a1d56 100644 --- a/tests/qapi-schema/doc-good.txt +++ b/tests/qapi-schema/doc-good.txt @@ -264,7 +264,7 @@ Example:: -> "this example" - <- "has no title" + <- ... has no title ... "EVT_BOXED" (Event) diff --git a/tests/qapi-schema/empty.out b/tests/qapi-schema/empty.out index 3feb3f6..d1981f8 100644 --- a/tests/qapi-schema/empty.out +++ b/tests/qapi-schema/empty.out @@ -1,7 +1,6 @@ module ./builtin object q_empty enum QType - prefix QTYPE member none member qnull member qnum diff --git a/tests/qapi-schema/features-too-many.err b/tests/qapi-schema/features-too-many.err new file mode 100644 index 0000000..bbbd6e5 --- /dev/null +++ b/tests/qapi-schema/features-too-many.err @@ -0,0 +1,2 @@ +features-too-many.json: In command 'go-fish': +features-too-many.json:2: Maximum of 64 schema features is permitted diff --git a/tests/qapi-schema/features-too-many.json b/tests/qapi-schema/features-too-many.json new file mode 100644 index 0000000..aab0a0b --- /dev/null +++ b/tests/qapi-schema/features-too-many.json @@ -0,0 +1,13 @@ +# Max 64 features, with 2 specials, so 63rd custom is invalid +{ 'command': 'go-fish', + 'features': [ + 'f00', 'f01', 'f02', 'f03', 'f04', 'f05', 'f06', 'f07', + 'f08', 'f09', 'f0a', 'f0b', 'f0c', 'f0d', 'f0e', 'f0f', + 'f10', 'f11', 'f12', 'f13', 'f14', 'f15', 'f16', 'f17', + 'f18', 'f19', 'f1a', 'f1b', 'f1c', 'f1d', 'f1e', 'f1f', + 'f20', 'f21', 'f22', 'f23', 'f24', 'f25', 'f26', 'f27', + 'f28', 'f29', 'f2a', 'f2b', 'f2c', 'f2d', 'f2e', 'f2f', + 'f30', 'f31', 'f32', 'f33', 'f34', 'f35', 'f36', 'f37', + 'f38', 'f39', 'f3a', 'f3b', 'f3c', 'f3d', 'f3e' + ] +} diff --git a/tests/qapi-schema/features-too-many.out b/tests/qapi-schema/features-too-many.out new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/tests/qapi-schema/features-too-many.out diff --git a/tests/qapi-schema/include-repetition.out b/tests/qapi-schema/include-repetition.out index 16dbd9b..c564d27 100644 --- a/tests/qapi-schema/include-repetition.out +++ b/tests/qapi-schema/include-repetition.out @@ -1,7 +1,6 @@ module ./builtin object q_empty enum QType - prefix QTYPE member none member qnull member qnum diff --git a/tests/qapi-schema/include-simple.out b/tests/qapi-schema/include-simple.out index 48e923b..ec8200a 100644 --- a/tests/qapi-schema/include-simple.out +++ b/tests/qapi-schema/include-simple.out @@ -1,7 +1,6 @@ module ./builtin object q_empty enum QType - prefix QTYPE member none member qnull member qnum diff --git a/tests/qapi-schema/indented-expr.out b/tests/qapi-schema/indented-expr.out index 6a30ded..a7c22c3 100644 --- a/tests/qapi-schema/indented-expr.out +++ b/tests/qapi-schema/indented-expr.out @@ -1,7 +1,6 @@ module ./builtin object q_empty enum QType - prefix QTYPE member none member qnull member qnum diff --git a/tests/qapi-schema/meson.build b/tests/qapi-schema/meson.build index 0f479d9..9577178 100644 --- a/tests/qapi-schema/meson.build +++ b/tests/qapi-schema/meson.build @@ -105,6 +105,7 @@ schemas = [ 'event-case.json', 'event-member-invalid-dict.json', 'event-nest-struct.json', + 'features-too-many.json', 'features-bad-type.json', 'features-deprecated-type.json', 'features-duplicate-name.json', diff --git a/tests/qapi-schema/qapi-schema-test.out b/tests/qapi-schema/qapi-schema-test.out index e2f0981..4617eb4 100644 --- a/tests/qapi-schema/qapi-schema-test.out +++ b/tests/qapi-schema/qapi-schema-test.out @@ -1,7 +1,6 @@ module ./builtin object q_empty enum QType - prefix QTYPE member none member qnull member qnum diff --git a/tests/qapi-schema/test-qapi.py b/tests/qapi-schema/test-qapi.py index 7e3f9f4..4be9302 100755 --- a/tests/qapi-schema/test-qapi.py +++ b/tests/qapi-schema/test-qapi.py @@ -96,17 +96,8 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor): @staticmethod def _print_if(ifcond, indent=4): - # TODO Drop this hack after replacing OrderedDict by plain - # dict (requires Python 3.7) - def _massage(subcond): - if isinstance(subcond, str): - return subcond - if isinstance(subcond, list): - return [_massage(val) for val in subcond] - return {key: _massage(val) for key, val in subcond.items()} - if ifcond.is_present(): - print('%sif %s' % (' ' * indent, _massage(ifcond.ifcond))) + print('%sif %s' % (' ' * indent, ifcond.ifcond)) @classmethod def _print_features(cls, features, indent=4): @@ -131,7 +122,7 @@ def test_frontend(fname): for feat, section in doc.features.items(): print(' feature=%s\n%s' % (feat, section.text)) for section in doc.sections: - print(' section=%s\n%s' % (section.tag, section.text)) + print(' section=%s\n%s' % (section.kind, section.text)) def open_test_result(dir_name, file_name, update): diff --git a/tests/qemu-iotests/041 b/tests/qemu-iotests/041 index 98d17b1..8452845 100755 --- a/tests/qemu-iotests/041 +++ b/tests/qemu-iotests/041 @@ -1100,10 +1100,8 @@ class TestRepairQuorum(iotests.QMPTestCase): # Check the full error message now self.vm.shutdown() - log = self.vm.get_log() - log = re.sub(r'^\[I \d+\.\d+\] OPENED\n', '', log) + log = iotests.filter_qtest(self.vm.get_log()) log = re.sub(r'^Formatting.*\n', '', log) - log = re.sub(r'\n\[I \+\d+\.\d+\] CLOSED\n?$', '', log) log = re.sub(r'^%s: ' % os.path.basename(iotests.qemu_prog), '', log) self.assertEqual(log, diff --git a/tests/qemu-iotests/051.pc.out b/tests/qemu-iotests/051.pc.out index 7e10c5f..f19b532 100644 --- a/tests/qemu-iotests/051.pc.out +++ b/tests/qemu-iotests/051.pc.out @@ -181,7 +181,7 @@ QEMU X.Y.Z monitor - type 'help' for more information Testing: -drive file=TEST_DIR/t.qcow2,if=none,node-name=disk -object iothread,id=thread0 -device virtio-scsi,iothread=thread0,id=virtio-scsi0 -device scsi-hd,bus=virtio-scsi0.0,drive=disk,share-rw=on -device virtio-scsi,id=virtio-scsi1 -device scsi-hd,bus=virtio-scsi1.0,drive=disk,share-rw=on QEMU X.Y.Z monitor - type 'help' for more information -(qemu) QEMU_PROG: -device scsi-hd,bus=virtio-scsi1.0,drive=disk,share-rw=on: Cannot change iothread of active block backend +(qemu) quit Testing: -drive file=TEST_DIR/t.qcow2,if=none,node-name=disk -object iothread,id=thread0 -device virtio-scsi,iothread=thread0,id=virtio-scsi0 -device scsi-hd,bus=virtio-scsi0.0,drive=disk,share-rw=on -device virtio-blk-pci,drive=disk,iothread=thread0,share-rw=on QEMU X.Y.Z monitor - type 'help' for more information diff --git a/tests/qemu-iotests/106 b/tests/qemu-iotests/106 index ae0fc46..5554843 100755 --- a/tests/qemu-iotests/106 +++ b/tests/qemu-iotests/106 @@ -40,6 +40,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 _supported_fmt raw _supported_proto file fuse _supported_os Linux +_require_disk_usage # in kB CREATION_SIZE=128 diff --git a/tests/qemu-iotests/125 b/tests/qemu-iotests/125 index 46279d6..708e7c5 100755 --- a/tests/qemu-iotests/125 +++ b/tests/qemu-iotests/125 @@ -35,7 +35,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 get_image_size_on_host() { - echo $(($(stat -c '%b * %B' "$TEST_IMG_FILE"))) + disk_usage "$TEST_IMG_FILE" } # get standard environment and filters diff --git a/tests/qemu-iotests/165 b/tests/qemu-iotests/165 index b24907a..b3b1709 100755 --- a/tests/qemu-iotests/165 +++ b/tests/qemu-iotests/165 @@ -82,9 +82,7 @@ class TestPersistentDirtyBitmap(iotests.QMPTestCase): self.vm.shutdown() #catch 'Persistent bitmaps are lost' possible error - log = self.vm.get_log() - log = re.sub(r'^\[I \d+\.\d+\] OPENED\n', '', log) - log = re.sub(r'\[I \+\d+\.\d+\] CLOSED\n?$', '', log) + log = iotests.filter_qtest(self.vm.get_log()) if log: print(log) diff --git a/tests/qemu-iotests/172.out b/tests/qemu-iotests/172.out index 07eebf3..146fc72 100644 --- a/tests/qemu-iotests/172.out +++ b/tests/qemu-iotests/172.out @@ -68,9 +68,6 @@ floppy0 (NODE_NAME): TEST_DIR/t.qcow2 (qcow2) ide1-cd0: [not inserted] Attached to: /machine/unattached/device[N] Removable device: not locked, tray closed - -sd0: [not inserted] - Removable device: not locked, tray closed (qemu) quit @@ -125,9 +122,6 @@ ide1-cd0: [not inserted] floppy0: [not inserted] Attached to: /machine/unattached/device[N] Removable device: not locked, tray closed - -sd0: [not inserted] - Removable device: not locked, tray closed (qemu) quit @@ -183,9 +177,6 @@ floppy1 (NODE_NAME): TEST_DIR/t.qcow2.2 (qcow2) ide1-cd0: [not inserted] Attached to: /machine/unattached/device[N] Removable device: not locked, tray closed - -sd0: [not inserted] - Removable device: not locked, tray closed (qemu) quit @@ -265,9 +256,6 @@ floppy0 (NODE_NAME): TEST_DIR/t.qcow2 (qcow2) ide1-cd0: [not inserted] Attached to: /machine/unattached/device[N] Removable device: not locked, tray closed - -sd0: [not inserted] - Removable device: not locked, tray closed (qemu) quit @@ -322,9 +310,6 @@ ide1-cd0: [not inserted] floppy0: [not inserted] Attached to: /machine/unattached/device[N] Removable device: not locked, tray closed - -sd0: [not inserted] - Removable device: not locked, tray closed (qemu) quit @@ -380,9 +365,6 @@ floppy1 (NODE_NAME): TEST_DIR/t.qcow2.2 (qcow2) ide1-cd0: [not inserted] Attached to: /machine/unattached/device[N] Removable device: not locked, tray closed - -sd0: [not inserted] - Removable device: not locked, tray closed (qemu) quit @@ -422,9 +404,6 @@ none0 (NODE_NAME): TEST_DIR/t.qcow2 (qcow2) ide1-cd0: [not inserted] Attached to: /machine/unattached/device[N] Removable device: not locked, tray closed - -sd0: [not inserted] - Removable device: not locked, tray closed (qemu) quit @@ -461,9 +440,6 @@ none0 (NODE_NAME): TEST_DIR/t.qcow2 (qcow2) ide1-cd0: [not inserted] Attached to: /machine/unattached/device[N] Removable device: not locked, tray closed - -sd0: [not inserted] - Removable device: not locked, tray closed (qemu) quit @@ -519,9 +495,6 @@ none1 (NODE_NAME): TEST_DIR/t.qcow2.2 (qcow2) ide1-cd0: [not inserted] Attached to: /machine/unattached/device[N] Removable device: not locked, tray closed - -sd0: [not inserted] - Removable device: not locked, tray closed (qemu) quit @@ -586,9 +559,6 @@ none0 (NODE_NAME): TEST_DIR/t.qcow2.2 (qcow2) ide1-cd0: [not inserted] Attached to: /machine/unattached/device[N] Removable device: not locked, tray closed - -sd0: [not inserted] - Removable device: not locked, tray closed (qemu) quit @@ -644,9 +614,6 @@ none0 (NODE_NAME): TEST_DIR/t.qcow2.2 (qcow2) ide1-cd0: [not inserted] Attached to: /machine/unattached/device[N] Removable device: not locked, tray closed - -sd0: [not inserted] - Removable device: not locked, tray closed (qemu) quit @@ -702,9 +669,6 @@ none0 (NODE_NAME): TEST_DIR/t.qcow2.2 (qcow2) ide1-cd0: [not inserted] Attached to: /machine/unattached/device[N] Removable device: not locked, tray closed - -sd0: [not inserted] - Removable device: not locked, tray closed (qemu) quit @@ -760,9 +724,6 @@ none0 (NODE_NAME): TEST_DIR/t.qcow2.2 (qcow2) ide1-cd0: [not inserted] Attached to: /machine/unattached/device[N] Removable device: not locked, tray closed - -sd0: [not inserted] - Removable device: not locked, tray closed (qemu) quit @@ -827,9 +788,6 @@ none0 (NODE_NAME): TEST_DIR/t.qcow2.2 (qcow2) ide1-cd0: [not inserted] Attached to: /machine/unattached/device[N] Removable device: not locked, tray closed - -sd0: [not inserted] - Removable device: not locked, tray closed (qemu) quit @@ -885,9 +843,6 @@ none0 (NODE_NAME): TEST_DIR/t.qcow2.2 (qcow2) ide1-cd0: [not inserted] Attached to: /machine/unattached/device[N] Removable device: not locked, tray closed - -sd0: [not inserted] - Removable device: not locked, tray closed (qemu) quit @@ -930,9 +885,6 @@ none0 (NODE_NAME): TEST_DIR/t.qcow2 (qcow2) ide1-cd0: [not inserted] Attached to: /machine/unattached/device[N] Removable device: not locked, tray closed - -sd0: [not inserted] - Removable device: not locked, tray closed (qemu) quit @@ -1106,9 +1058,6 @@ none0 (NODE_NAME): TEST_DIR/t.qcow2 (qcow2) ide1-cd0: [not inserted] Attached to: /machine/unattached/device[N] Removable device: not locked, tray closed - -sd0: [not inserted] - Removable device: not locked, tray closed (qemu) quit @@ -1145,9 +1094,6 @@ none0 (NODE_NAME): TEST_DIR/t.qcow2 (qcow2) ide1-cd0: [not inserted] Attached to: /machine/unattached/device[N] Removable device: not locked, tray closed - -sd0: [not inserted] - Removable device: not locked, tray closed (qemu) quit @@ -1187,9 +1133,6 @@ none0 (NODE_NAME): TEST_DIR/t.qcow2 (qcow2) ide1-cd0: [not inserted] Attached to: /machine/unattached/device[N] Removable device: not locked, tray closed - -sd0: [not inserted] - Removable device: not locked, tray closed (qemu) quit @@ -1226,9 +1169,6 @@ none0 (NODE_NAME): TEST_DIR/t.qcow2 (qcow2) ide1-cd0: [not inserted] Attached to: /machine/unattached/device[N] Removable device: not locked, tray closed - -sd0: [not inserted] - Removable device: not locked, tray closed (qemu) quit diff --git a/tests/qemu-iotests/175 b/tests/qemu-iotests/175 index f74f053..bbbf550 100755 --- a/tests/qemu-iotests/175 +++ b/tests/qemu-iotests/175 @@ -77,6 +77,7 @@ _supported_os Linux _default_cache_mode none _supported_cache_modes none directsync +_require_disk_usage size=$((1 * 1024 * 1024)) diff --git a/tests/qemu-iotests/184.out b/tests/qemu-iotests/184.out index e8f631f..52692b6 100644 --- a/tests/qemu-iotests/184.out +++ b/tests/qemu-iotests/184.out @@ -26,6 +26,7 @@ Testing: { "iops_rd": 0, "detect_zeroes": "off", + "active": true, "image": { "backing-image": { "virtual-size": 1073741824, @@ -59,6 +60,7 @@ Testing: { "iops_rd": 0, "detect_zeroes": "off", + "active": true, "image": { "virtual-size": 1073741824, "filename": "null-co://", diff --git a/tests/qemu-iotests/191.out b/tests/qemu-iotests/191.out index c3309e4..2a72ca7 100644 --- a/tests/qemu-iotests/191.out +++ b/tests/qemu-iotests/191.out @@ -114,6 +114,7 @@ wrote 65536/65536 bytes at offset 1048576 { "iops_rd": 0, "detect_zeroes": "off", + "active": true, "image": { "backing-image": { "virtual-size": 67108864, @@ -155,6 +156,7 @@ wrote 65536/65536 bytes at offset 1048576 { "iops_rd": 0, "detect_zeroes": "off", + "active": true, "image": { "virtual-size": 197120, "filename": "TEST_DIR/t.IMGFMT.ovl2", @@ -183,6 +185,7 @@ wrote 65536/65536 bytes at offset 1048576 { "iops_rd": 0, "detect_zeroes": "off", + "active": true, "image": { "backing-image": { "virtual-size": 67108864, @@ -224,6 +227,7 @@ wrote 65536/65536 bytes at offset 1048576 { "iops_rd": 0, "detect_zeroes": "off", + "active": true, "image": { "virtual-size": 197120, "filename": "TEST_DIR/t.IMGFMT", @@ -252,6 +256,7 @@ wrote 65536/65536 bytes at offset 1048576 { "iops_rd": 0, "detect_zeroes": "off", + "active": true, "image": { "backing-image": { "virtual-size": 67108864, @@ -293,6 +298,7 @@ wrote 65536/65536 bytes at offset 1048576 { "iops_rd": 0, "detect_zeroes": "off", + "active": true, "image": { "virtual-size": 393216, "filename": "TEST_DIR/t.IMGFMT.mid", @@ -321,6 +327,7 @@ wrote 65536/65536 bytes at offset 1048576 { "iops_rd": 0, "detect_zeroes": "off", + "active": true, "image": { "virtual-size": 67108864, "filename": "TEST_DIR/t.IMGFMT.base", @@ -350,6 +357,7 @@ wrote 65536/65536 bytes at offset 1048576 { "iops_rd": 0, "detect_zeroes": "off", + "active": true, "image": { "virtual-size": 393216, "filename": "TEST_DIR/t.IMGFMT.base", @@ -521,6 +529,7 @@ wrote 65536/65536 bytes at offset 1048576 { "iops_rd": 0, "detect_zeroes": "off", + "active": true, "image": { "backing-image": { "virtual-size": 67108864, @@ -562,6 +571,7 @@ wrote 65536/65536 bytes at offset 1048576 { "iops_rd": 0, "detect_zeroes": "off", + "active": true, "image": { "virtual-size": 197120, "filename": "TEST_DIR/t.IMGFMT.ovl2", @@ -590,6 +600,7 @@ wrote 65536/65536 bytes at offset 1048576 { "iops_rd": 0, "detect_zeroes": "off", + "active": true, "image": { "backing-image": { "backing-image": { @@ -642,6 +653,7 @@ wrote 65536/65536 bytes at offset 1048576 { "iops_rd": 0, "detect_zeroes": "off", + "active": true, "image": { "virtual-size": 197120, "filename": "TEST_DIR/t.IMGFMT.ovl3", @@ -670,6 +682,7 @@ wrote 65536/65536 bytes at offset 1048576 { "iops_rd": 0, "detect_zeroes": "off", + "active": true, "image": { "virtual-size": 67108864, "filename": "TEST_DIR/t.IMGFMT.base", @@ -699,6 +712,7 @@ wrote 65536/65536 bytes at offset 1048576 { "iops_rd": 0, "detect_zeroes": "off", + "active": true, "image": { "virtual-size": 393216, "filename": "TEST_DIR/t.IMGFMT.base", @@ -727,6 +741,7 @@ wrote 65536/65536 bytes at offset 1048576 { "iops_rd": 0, "detect_zeroes": "off", + "active": true, "image": { "backing-image": { "virtual-size": 67108864, @@ -768,6 +783,7 @@ wrote 65536/65536 bytes at offset 1048576 { "iops_rd": 0, "detect_zeroes": "off", + "active": true, "image": { "virtual-size": 197120, "filename": "TEST_DIR/t.IMGFMT", diff --git a/tests/qemu-iotests/194 b/tests/qemu-iotests/194 index c0ce82d..e114c0b 100755 --- a/tests/qemu-iotests/194 +++ b/tests/qemu-iotests/194 @@ -34,6 +34,7 @@ with iotests.FilePath('source.img') as source_img_path, \ img_size = '1G' iotests.qemu_img_create('-f', iotests.imgfmt, source_img_path, img_size) + iotests.qemu_io('-f', iotests.imgfmt, '-c', 'write 512M 1M', source_img_path) iotests.qemu_img_create('-f', iotests.imgfmt, dest_img_path, img_size) iotests.log('Launching VMs...') @@ -61,7 +62,8 @@ with iotests.FilePath('source.img') as source_img_path, \ iotests.log('Waiting for `drive-mirror` to complete...') iotests.log(source_vm.event_wait('BLOCK_JOB_READY'), - filters=[iotests.filter_qmp_event]) + filters=[iotests.filter_qmp_event, + iotests.filter_block_job]) iotests.log('Starting migration...') capabilities = [{'capability': 'events', 'state': True}, @@ -87,7 +89,8 @@ with iotests.FilePath('source.img') as source_img_path, \ while True: event2 = source_vm.event_wait('BLOCK_JOB_COMPLETED') - iotests.log(event2, filters=[iotests.filter_qmp_event]) + iotests.log(event2, filters=[iotests.filter_qmp_event, + iotests.filter_block_job]) if event2['event'] == 'BLOCK_JOB_COMPLETED': iotests.log('Stopping the NBD server on destination...') iotests.log(dest_vm.qmp('nbd-server-stop')) diff --git a/tests/qemu-iotests/194.out b/tests/qemu-iotests/194.out index 376ed1d..d02655a 100644 --- a/tests/qemu-iotests/194.out +++ b/tests/qemu-iotests/194.out @@ -7,17 +7,18 @@ Launching NBD server on destination... Starting `drive-mirror` on source... {"return": {}} Waiting for `drive-mirror` to complete... -{"data": {"device": "mirror-job0", "len": 1073741824, "offset": 1073741824, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_READY", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"data": {"device": "mirror-job0", "len": "LEN", "offset": "OFFSET", "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_READY", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} Starting migration... {"return": {}} {"execute": "migrate-start-postcopy", "arguments": {}} {"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": {}} -{"data": {"device": "mirror-job0", "len": 1073741824, "offset": 1073741824, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"data": {"device": "mirror-job0", "len": "LEN", "offset": "OFFSET", "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} Stopping the NBD server on destination... {"return": {}} Wait for migration completion on target... diff --git a/tests/qemu-iotests/203.out b/tests/qemu-iotests/203.out index 9d4abba..8e58705 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/211.out b/tests/qemu-iotests/211.out index f02c754..ff9f9a6 100644 --- a/tests/qemu-iotests/211.out +++ b/tests/qemu-iotests/211.out @@ -17,7 +17,7 @@ file format: IMGFMT virtual size: 128 MiB (134217728 bytes) cluster_size: 1048576 -[{"data": false, "depth": 0, "length": 134217728, "present": true, "start": 0, "zero": true}] +[{"compressed": false, "data": false, "depth": 0, "length": 134217728, "present": true, "start": 0, "zero": true}] === Successful image creation (explicit defaults) === {"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.vdi", "size": 0}}} @@ -35,7 +35,7 @@ file format: IMGFMT virtual size: 64 MiB (67108864 bytes) cluster_size: 1048576 -[{"data": false, "depth": 0, "length": 67108864, "present": true, "start": 0, "zero": true}] +[{"compressed": false, "data": false, "depth": 0, "length": 67108864, "present": true, "start": 0, "zero": true}] === Successful image creation (with non-default options) === {"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.vdi", "size": 0}}} @@ -53,7 +53,7 @@ file format: IMGFMT virtual size: 32 MiB (33554432 bytes) cluster_size: 1048576 -[{"data": true, "depth": 0, "length": 3072, "offset": 1024, "present": true, "start": 0, "zero": false}, {"data": true, "depth": 0, "length": 33551360, "offset": 4096, "present": true, "start": 3072, "zero": true}] +[{"compressed": false, "data": true, "depth": 0, "length": 3072, "offset": 1024, "present": true, "start": 0, "zero": false}, {"compressed": false, "data": true, "depth": 0, "length": 33551360, "offset": 4096, "present": true, "start": 3072, "zero": true}] === Invalid BlockdevRef === {"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "vdi", "file": "this doesn't exist", "size": 33554432}}} diff --git a/tests/qemu-iotests/221 b/tests/qemu-iotests/221 index c463fd4..eba00b8 100755 --- a/tests/qemu-iotests/221 +++ b/tests/qemu-iotests/221 @@ -41,6 +41,7 @@ _supported_os Linux _default_cache_mode writeback _supported_cache_modes writeback writethrough unsafe +_require_disk_usage echo echo "=== Check mapping of unaligned raw image ===" diff --git a/tests/qemu-iotests/234.out b/tests/qemu-iotests/234.out index ac8b643..be3e138 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/240 b/tests/qemu-iotests/240 index 9b281e1..f8af9ff 100755 --- a/tests/qemu-iotests/240 +++ b/tests/qemu-iotests/240 @@ -81,8 +81,6 @@ class TestCase(iotests.QMPTestCase): self.vm.qmp_log('device_del', id='scsi-hd0') self.vm.event_wait('DEVICE_DELETED') - self.vm.qmp_log('device_add', id='scsi-hd1', driver='scsi-hd', drive='hd0', bus="scsi1.0") - self.vm.qmp_log('device_del', id='scsi-hd1') self.vm.event_wait('DEVICE_DELETED') self.vm.qmp_log('blockdev-del', node_name='hd0') diff --git a/tests/qemu-iotests/240.out b/tests/qemu-iotests/240.out index 89ed25e..10dcc42 100644 --- a/tests/qemu-iotests/240.out +++ b/tests/qemu-iotests/240.out @@ -46,10 +46,8 @@ {"execute": "device_add", "arguments": {"bus": "scsi0.0", "drive": "hd0", "driver": "scsi-hd", "id": "scsi-hd0"}} {"return": {}} {"execute": "device_add", "arguments": {"bus": "scsi1.0", "drive": "hd0", "driver": "scsi-hd", "id": "scsi-hd1"}} -{"error": {"class": "GenericError", "desc": "Cannot change iothread of active block backend"}} -{"execute": "device_del", "arguments": {"id": "scsi-hd0"}} {"return": {}} -{"execute": "device_add", "arguments": {"bus": "scsi1.0", "drive": "hd0", "driver": "scsi-hd", "id": "scsi-hd1"}} +{"execute": "device_del", "arguments": {"id": "scsi-hd0"}} {"return": {}} {"execute": "device_del", "arguments": {"id": "scsi-hd1"}} {"return": {}} diff --git a/tests/qemu-iotests/250 b/tests/qemu-iotests/250 index af48f83..c0a0dbc 100755 --- a/tests/qemu-iotests/250 +++ b/tests/qemu-iotests/250 @@ -52,11 +52,6 @@ _unsupported_imgopts data_file # bdrv_co_truncate(bs->file) call in qcow2_co_truncate(), which might succeed # anyway. -disk_usage() -{ - du --block-size=1 $1 | awk '{print $1}' -} - size=2100M _make_test_img -o "cluster_size=1M,preallocation=metadata" $size diff --git a/tests/qemu-iotests/253 b/tests/qemu-iotests/253 index 35039d2..6da85e6 100755 --- a/tests/qemu-iotests/253 +++ b/tests/qemu-iotests/253 @@ -41,6 +41,7 @@ _supported_os Linux _default_cache_mode none _supported_cache_modes none directsync +_require_disk_usage echo echo "=== Check mapping of unaligned raw image ===" diff --git a/tests/qemu-iotests/262.out b/tests/qemu-iotests/262.out index b8a2d35..bd7706b 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/273.out b/tests/qemu-iotests/273.out index 71843f0..c19753c 100644 --- a/tests/qemu-iotests/273.out +++ b/tests/qemu-iotests/273.out @@ -23,6 +23,7 @@ Testing: -blockdev file,node-name=base,filename=TEST_DIR/t.IMGFMT.base -blockdev { "iops_rd": 0, "detect_zeroes": "off", + "active": true, "image": { "backing-image": { "backing-image": { @@ -74,6 +75,7 @@ Testing: -blockdev file,node-name=base,filename=TEST_DIR/t.IMGFMT.base -blockdev { "iops_rd": 0, "detect_zeroes": "off", + "active": true, "image": { "virtual-size": 197120, "filename": "TEST_DIR/t.IMGFMT", @@ -102,6 +104,7 @@ Testing: -blockdev file,node-name=base,filename=TEST_DIR/t.IMGFMT.base -blockdev { "iops_rd": 0, "detect_zeroes": "off", + "active": true, "image": { "backing-image": { "virtual-size": 197120, @@ -142,6 +145,7 @@ Testing: -blockdev file,node-name=base,filename=TEST_DIR/t.IMGFMT.base -blockdev { "iops_rd": 0, "detect_zeroes": "off", + "active": true, "image": { "virtual-size": 197120, "filename": "TEST_DIR/t.IMGFMT.mid", @@ -170,6 +174,7 @@ Testing: -blockdev file,node-name=base,filename=TEST_DIR/t.IMGFMT.base -blockdev { "iops_rd": 0, "detect_zeroes": "off", + "active": true, "image": { "virtual-size": 197120, "filename": "TEST_DIR/t.IMGFMT.base", diff --git a/tests/qemu-iotests/280.out b/tests/qemu-iotests/280.out index 546dbb4..3741114 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/qemu-iotests/302 b/tests/qemu-iotests/302 index a6d79e7..e980ec5 100755 --- a/tests/qemu-iotests/302 +++ b/tests/qemu-iotests/302 @@ -115,13 +115,22 @@ with tarfile.open(tar_file, "w") as tar: disk = tarfile.TarInfo("disk") disk.size = actual_size - tar.addfile(disk) - # 6. Shrink the tar to the actual size, aligned to 512 bytes. + # Since python 3.13 we cannot use addfile() to create the member header. + # Add the tarinfo directly using public but undocumented attributes. - tar_size = offset + (disk.size + 511) & ~511 - tar.fileobj.seek(tar_size) - tar.fileobj.truncate(tar_size) + buf = disk.tobuf(tar.format, tar.encoding, tar.errors) + tar.fileobj.write(buf) + tar.members.append(disk) + + # Update the offset and position to the location of the next member. + + tar.offset = offset + (disk.size + 511) & ~511 + tar.fileobj.seek(tar.offset) + + # 6. Shrink the tar to the actual size. + + tar.fileobj.truncate(tar.offset) with tarfile.open(tar_file) as tar: members = [{"name": m.name, "size": m.size, "offset": m.offset_data} diff --git a/tests/qemu-iotests/308 b/tests/qemu-iotests/308 index ea81dc4..6eced3a 100755 --- a/tests/qemu-iotests/308 +++ b/tests/qemu-iotests/308 @@ -51,6 +51,7 @@ _unsupported_fmt vpc _supported_proto file # We create the FUSE export manually _supported_os Linux # We need /dev/urandom +_require_disk_usage # $1: Export ID # $2: Options (beyond the node-name and ID) @@ -290,7 +291,7 @@ echo '--- Try growing non-growable export ---' # Get the current size so we can write beyond the EOF orig_len=$(get_proto_len "$EXT_MP" "$TEST_IMG") -orig_disk_usage=$(stat -c '%b' "$TEST_IMG") +orig_disk_usage=$(disk_usage "$TEST_IMG") # Should fail (exports are non-growable by default) # (Note that qemu-io can never write beyond the EOF, so we have to use @@ -312,7 +313,7 @@ else echo 'OK: Post-truncate image size is as expected' fi -new_disk_usage=$(stat -c '%b' "$TEST_IMG") +new_disk_usage=$(disk_usage "$TEST_IMG") if [ "$new_disk_usage" -gt "$orig_disk_usage" ]; then echo 'OK: Disk usage grew with fallocate' else diff --git a/tests/qemu-iotests/common.rc b/tests/qemu-iotests/common.rc index 95c1257..e977cb4 100644 --- a/tests/qemu-iotests/common.rc +++ b/tests/qemu-iotests/common.rc @@ -140,6 +140,12 @@ _optstr_add() fi } +# report real disk usage for sparse files +disk_usage() +{ + du --block-size=1 "$1" | awk '{print $1}' +} + # Set the variables to the empty string to turn Valgrind off # for specific processes, e.g. # $ VALGRIND_QEMU_IO= ./check -qcow2 -valgrind 015 @@ -990,6 +996,36 @@ _require_large_file() rm "$FILENAME" } +# Check whether disk_usage can be reliably used. +_require_disk_usage() +{ + local unusable=false + # ZFS triggers known failures on this front; it does not immediately + # allocate files, and then aggressively compresses writes even when full + # allocation was requested. + if [ -z "$TEST_IMG_FILE" ]; then + FILENAME="$TEST_IMG" + else + FILENAME="$TEST_IMG_FILE" + fi + if [ -e "FILENAME" ]; then + echo "unwilling to overwrite existing file" + exit 1 + fi + $QEMU_IMG create -f raw "$FILENAME" 5M > /dev/null + if [ $(disk_usage "$FILENAME") -gt $((1024*1024)) ]; then + unusable=true + fi + $QEMU_IMG create -f raw -o preallocation=full "$FILENAME" 5M > /dev/null + if [ $(disk_usage "$FILENAME") -lt $((4*1024*1024)) ]; then + unusable=true + fi + rm -f "$FILENAME" + if $unusable; then + _notrun "file system on $TEST_DIR does not handle sparse files nicely" + fi +} + # Check that a set of devices is available in the QEMU binary # _require_devices() diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py index ea48af4..0527477 100644 --- a/tests/qemu-iotests/iotests.py +++ b/tests/qemu-iotests/iotests.py @@ -601,13 +601,23 @@ def filter_chown(msg): return chown_re.sub("chown UID:GID", msg) def filter_qmp_event(event): - '''Filter a QMP event dict''' + '''Filter the timestamp of a QMP event dict''' event = dict(event) if 'timestamp' in event: event['timestamp']['seconds'] = 'SECS' event['timestamp']['microseconds'] = 'USECS' return event +def filter_block_job(event): + '''Filter the offset and length of a QMP block job event dict''' + event = dict(event) + if 'data' in event: + if 'offset' in event['data']: + event['data']['offset'] = 'OFFSET' + if 'len' in event['data']: + event['data']['len'] = 'LEN' + return event + def filter_qmp(qmsg, filter_fn): '''Given a string filter, filter a QMP object's values. filter_fn takes a (key, value) pair.''' @@ -701,6 +711,10 @@ def filter_qmp_imgfmt(qmsg): def filter_nbd_exports(output: str) -> str: return re.sub(r'((min|opt|max) block): [0-9]+', r'\1: XXX', output) +def filter_qtest(output: str) -> str: + output = re.sub(r'^\[I \d+\.\d+\] OPENED\n', '', output) + output = re.sub(r'\n?\[I \+\d+\.\d+\] CLOSED\n?$', '', output) + return output Msg = TypeVar('Msg', Dict[str, Any], List[Any], str) @@ -909,6 +923,10 @@ class VM(qtest.QEMUQtestMachine): self._args.append(addr) return self + def add_paused(self): + self._args.append('-S') + return self + def hmp(self, command_line: str, use_log: bool = False) -> QMPMessage: cmd = 'human-monitor-command' kwargs: Dict[str, Any] = {'command-line': command_line} @@ -1614,10 +1632,13 @@ class ReproducibleStreamWrapper: self.stream.write(arg) class ReproducibleTestRunner(unittest.TextTestRunner): - def __init__(self, stream: Optional[TextIO] = None, - resultclass: Type[unittest.TestResult] = - ReproducibleTestResult, - **kwargs: Any) -> None: + def __init__( + self, + stream: Optional[TextIO] = None, + resultclass: Type[unittest.TextTestResult] = + ReproducibleTestResult, + **kwargs: Any + ) -> None: rstream = ReproducibleStreamWrapper(stream or sys.stdout) super().__init__(stream=rstream, # type: ignore descriptions=True, diff --git a/tests/qemu-iotests/pylintrc b/tests/qemu-iotests/pylintrc index 05b75ee..c5f4833 100644 --- a/tests/qemu-iotests/pylintrc +++ b/tests/qemu-iotests/pylintrc @@ -13,6 +13,7 @@ disable=invalid-name, no-else-return, too-few-public-methods, too-many-arguments, + too-many-positional-arguments, too-many-branches, too-many-lines, too-many-locals, diff --git a/tests/qemu-iotests/testenv.py b/tests/qemu-iotests/testenv.py index c8848f2..6326e46 100644 --- a/tests/qemu-iotests/testenv.py +++ b/tests/qemu-iotests/testenv.py @@ -240,9 +240,12 @@ class TestEnv(ContextManager['TestEnv']): ('aarch64', 'virt'), ('avr', 'mega2560'), ('m68k', 'virt'), + ('or1k', 'virt'), ('riscv32', 'virt'), ('riscv64', 'virt'), ('rx', 'gdbsim-r5f562n8'), + ('sh4', 'r2d'), + ('sh4eb', 'r2d'), ('tricore', 'tricore_testboard') ) for suffix, machine in machine_map: diff --git a/tests/qemu-iotests/tests/backup-discard-source b/tests/qemu-iotests/tests/backup-discard-source index 2391b12..17fef9c 100755 --- a/tests/qemu-iotests/tests/backup-discard-source +++ b/tests/qemu-iotests/tests/backup-discard-source @@ -28,20 +28,14 @@ from iotests import qemu_img_create, qemu_img_map, qemu_io temp_img = os.path.join(iotests.test_dir, 'temp') source_img = os.path.join(iotests.test_dir, 'source') target_img = os.path.join(iotests.test_dir, 'target') -size = '1M' - - -def get_actual_size(vm, node_name): - nodes = vm.cmd('query-named-block-nodes', flat=True) - node = next(n for n in nodes if n['node-name'] == node_name) - return node['image']['actual-size'] +size = 1024 * 1024 class TestBackup(iotests.QMPTestCase): def setUp(self): - qemu_img_create('-f', iotests.imgfmt, source_img, size) - qemu_img_create('-f', iotests.imgfmt, temp_img, size) - qemu_img_create('-f', iotests.imgfmt, target_img, size) + qemu_img_create('-f', iotests.imgfmt, source_img, str(size)) + qemu_img_create('-f', iotests.imgfmt, temp_img, str(size)) + qemu_img_create('-f', iotests.imgfmt, target_img, str(size)) qemu_io('-c', 'write 0 1M', source_img) self.vm = iotests.VM() @@ -84,7 +78,12 @@ class TestBackup(iotests.QMPTestCase): } }) - self.assertLess(get_actual_size(self.vm, 'temp'), 512 * 1024) + self.bitmap = { + 'node': 'temp', + 'name': 'bitmap0' + } + + self.vm.cmd('block-dirty-bitmap-add', self.bitmap) def tearDown(self): # That should fail, because region is discarded @@ -98,7 +97,7 @@ class TestBackup(iotests.QMPTestCase): mapping = qemu_img_map(temp_img) self.assertEqual(len(mapping), 1) self.assertEqual(mapping[0]['start'], 0) - self.assertEqual(mapping[0]['length'], 1024 * 1024) + self.assertEqual(mapping[0]['length'], size) self.assertEqual(mapping[0]['data'], False) os.remove(temp_img) @@ -113,6 +112,13 @@ class TestBackup(iotests.QMPTestCase): self.vm.event_wait(name='BLOCK_JOB_COMPLETED') + def get_bitmap_count(self): + nodes = self.vm.cmd('query-named-block-nodes', flat=True) + temp = next(n for n in nodes if n['node-name'] == 'temp') + bitmap = temp['dirty-bitmaps'][0] + assert bitmap['name'] == self.bitmap['name'] + return bitmap['count'] + def test_discard_written(self): """ 1. Guest writes @@ -125,7 +131,7 @@ class TestBackup(iotests.QMPTestCase): self.assert_qmp(result, 'return', '') # Check that data is written to temporary image - self.assertGreater(get_actual_size(self.vm, 'temp'), 1024 * 1024) + self.assertEqual(self.get_bitmap_count(), size) self.do_backup() @@ -138,13 +144,18 @@ class TestBackup(iotests.QMPTestCase): """ self.do_backup() + # backup job did discard operation and pollute the bitmap, + # we have to clean the bitmap, to check next write + self.assertEqual(self.get_bitmap_count(), size) + self.vm.cmd('block-dirty-bitmap-clear', self.bitmap) + # Try trigger copy-before-write operation result = self.vm.hmp_qemu_io('cbw', 'write 0 1M') self.assert_qmp(result, 'return', '') # Check that data is not written to temporary image, as region # is discarded from copy-before-write process - self.assertLess(get_actual_size(self.vm, 'temp'), 512 * 1024) + self.assertEqual(self.get_bitmap_count(), 0) if __name__ == '__main__': diff --git a/tests/qemu-iotests/tests/commit-zero-blocks b/tests/qemu-iotests/tests/commit-zero-blocks new file mode 100755 index 0000000..de00273 --- /dev/null +++ b/tests/qemu-iotests/tests/commit-zero-blocks @@ -0,0 +1,96 @@ +#!/usr/bin/env bash +# group: rw quick +# +# Test for commit of discarded blocks +# +# This tests committing a live snapshot where some of the blocks that +# are present in the base image are discarded in the intermediate image. +# This intends to check that these blocks are also discarded in the base +# image after the commit. +# +# Copyright (C) 2024 Vincent Vanlaer. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# +# creator +owner=libvirt-e6954efa@volkihar.be + +seq=`basename $0` +echo "QA output created by $seq" + +status=1 # failure is the default! + +_cleanup() +{ + _cleanup_qemu + _rm_test_img "${TEST_IMG}.base" + _rm_test_img "${TEST_IMG}.mid" + _cleanup_test_img +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +cd .. +. ./common.rc +. ./common.filter +. ./common.qemu + +_supported_fmt qcow2 +_supported_proto file + +size="1M" + +TEST_IMG="$TEST_IMG.base" _make_test_img $size +TEST_IMG="$TEST_IMG.mid" _make_test_img -b "$TEST_IMG.base" -F $IMGFMT $size +_make_test_img -b "${TEST_IMG}.mid" -F $IMGFMT $size + +$QEMU_IO -c "write -P 0x01 64k 128k" "$TEST_IMG.base" | _filter_qemu_io +$QEMU_IO -c "discard 64k 64k" "$TEST_IMG.mid" | _filter_qemu_io + +echo +echo "=== Base image info before commit ===" +TEST_IMG="${TEST_IMG}.base" _img_info | _filter_img_info +$QEMU_IMG map --output=json "$TEST_IMG.base" | _filter_qemu_img_map + +echo +echo "=== Middle image info before commit ===" +TEST_IMG="${TEST_IMG}.mid" _img_info | _filter_img_info +$QEMU_IMG map --output=json "$TEST_IMG.mid" | _filter_qemu_img_map + +echo +echo === Running QEMU Live Commit Test === +echo + +qemu_comm_method="qmp" +_launch_qemu -drive file="${TEST_IMG}",if=virtio,id=test +h=$QEMU_HANDLE + +_send_qemu_cmd $h "{ 'execute': 'qmp_capabilities' }" "return" + +_send_qemu_cmd $h "{ 'execute': 'block-commit', + 'arguments': { 'device': 'test', + 'top': '"${TEST_IMG}.mid"', + 'base': '"${TEST_IMG}.base"'} }" '"status": "null"' + +_cleanup_qemu + +echo +echo "=== Base image info after commit ===" +TEST_IMG="${TEST_IMG}.base" _img_info | _filter_img_info +$QEMU_IMG map --output=json "$TEST_IMG.base" | _filter_qemu_img_map + +# success, all done +echo "*** done" +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/tests/commit-zero-blocks.out b/tests/qemu-iotests/tests/commit-zero-blocks.out new file mode 100644 index 0000000..85bdc46 --- /dev/null +++ b/tests/qemu-iotests/tests/commit-zero-blocks.out @@ -0,0 +1,54 @@ +QA output created by commit-zero-blocks +Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=1048576 +Formatting 'TEST_DIR/t.IMGFMT.mid', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/t.IMGFMT.mid backing_fmt=IMGFMT +wrote 131072/131072 bytes at offset 65536 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +discard 65536/65536 bytes at offset 65536 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +=== Base image info before commit === +image: TEST_DIR/t.IMGFMT.base +file format: IMGFMT +virtual size: 1 MiB (1048576 bytes) +[{ "start": 0, "length": 65536, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}, +{ "start": 65536, "length": 131072, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 196608, "length": 851968, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}] + +=== Middle image info before commit === +image: TEST_DIR/t.IMGFMT.mid +file format: IMGFMT +virtual size: 1 MiB (1048576 bytes) +backing file: TEST_DIR/t.IMGFMT.base +backing file format: IMGFMT +[{ "start": 0, "length": 65536, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}, +{ "start": 65536, "length": 65536, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}, +{ "start": 131072, "length": 65536, "depth": 1, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 196608, "length": 851968, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}] + +=== Running QEMU Live Commit Test === + +{ 'execute': 'qmp_capabilities' } +{"return": {}} +{ 'execute': 'block-commit', + 'arguments': { 'device': 'test', + 'top': 'TEST_DIR/t.IMGFMT.mid', + 'base': 'TEST_DIR/t.IMGFMT.base'} } +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "test"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "test"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "test"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "test"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "test", "len": 1048576, "offset": 1048576, "speed": 0, "type": "commit"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "test"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "test"}} + +=== Base image info after commit === +image: TEST_DIR/t.IMGFMT.base +file format: IMGFMT +virtual size: 1 MiB (1048576 bytes) +[{ "start": 0, "length": 65536, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}, +{ "start": 65536, "length": 65536, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}, +{ "start": 131072, "length": 65536, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 196608, "length": 851968, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}] +*** done diff --git a/tests/qemu-iotests/tests/copy-before-write b/tests/qemu-iotests/tests/copy-before-write index d33bea5..236cb8a 100755 --- a/tests/qemu-iotests/tests/copy-before-write +++ b/tests/qemu-iotests/tests/copy-before-write @@ -95,8 +95,69 @@ class TestCbwError(iotests.QMPTestCase): self.vm.shutdown() log = self.vm.get_log() - log = re.sub(r'^\[I \d+\.\d+\] OPENED\n', '', log) - log = re.sub(r'\[I \+\d+\.\d+\] CLOSED\n?$', '', log) + log = iotests.filter_qtest(log) + log = iotests.filter_qemu_io(log) + return log + + def do_cbw_error_via_blockdev_backup(self, on_cbw_error=None): + self.vm.cmd('blockdev-add', { + 'node-name': 'source', + 'driver': iotests.imgfmt, + 'file': { + 'driver': 'file', + 'filename': source_img + } + }) + + self.vm.cmd('blockdev-add', { + 'node-name': 'target', + 'driver': iotests.imgfmt, + 'file': { + 'driver': 'blkdebug', + 'image': { + 'driver': 'file', + 'filename': temp_img + }, + 'inject-error': [ + { + 'event': 'write_aio', + 'errno': 5, + 'immediately': False, + 'once': True + } + ] + } + }) + + blockdev_backup_options = { + 'device': 'source', + 'target': 'target', + 'sync': 'none', + 'job-id': 'job-id', + 'filter-node-name': 'cbw' + } + + if on_cbw_error: + blockdev_backup_options['on-cbw-error'] = on_cbw_error + + self.vm.cmd('blockdev-backup', blockdev_backup_options) + + self.vm.cmd('blockdev-add', { + 'node-name': 'access', + 'driver': 'snapshot-access', + 'file': 'cbw' + }) + + result = self.vm.qmp('human-monitor-command', + command_line='qemu-io cbw "write 0 1M"') + self.assert_qmp(result, 'return', '') + + result = self.vm.qmp('human-monitor-command', + command_line='qemu-io access "read 0 1M"') + self.assert_qmp(result, 'return', '') + + self.vm.shutdown() + log = self.vm.get_log() log = iotests.filter_qemu_io(log) return log @@ -126,6 +187,39 @@ read 1048576/1048576 bytes at offset 0 1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) """) + def test_break_snapshot_policy_forwarding(self): + """Ensure CBW filter accepts break-snapshot policy + specified in blockdev-backup QMP command. + """ + log = self.do_cbw_error_via_blockdev_backup('break-snapshot') + self.assertEqual(log, """\ +wrote 1048576/1048576 bytes at offset 0 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read failed: Permission denied +""") + + def test_break_guest_write_policy_forwarding(self): + """Ensure CBW filter accepts break-guest-write policy + specified in blockdev-backup QMP command. + """ + log = self.do_cbw_error_via_blockdev_backup('break-guest-write') + self.assertEqual(log, """\ +write failed: Input/output error +read 1048576/1048576 bytes at offset 0 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +""") + + def test_default_on_cbw_error_policy_forwarding(self): + """Ensure break-guest-write policy is used by default when + on-cbw-error is not explicitly specified. + """ + log = self.do_cbw_error_via_blockdev_backup() + self.assertEqual(log, """\ +write failed: Input/output error +read 1048576/1048576 bytes at offset 0 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +""") + def do_cbw_timeout(self, on_cbw_error): self.vm.cmd('object-add', { 'qom-type': 'throttle-group', diff --git a/tests/qemu-iotests/tests/copy-before-write.out b/tests/qemu-iotests/tests/copy-before-write.out index 89968f3..2f7d390 100644 --- a/tests/qemu-iotests/tests/copy-before-write.out +++ b/tests/qemu-iotests/tests/copy-before-write.out @@ -1,5 +1,5 @@ -.... +....... ---------------------------------------------------------------------- -Ran 4 tests +Ran 7 tests OK diff --git a/tests/qemu-iotests/tests/graph-changes-while-io b/tests/qemu-iotests/tests/graph-changes-while-io index 194fda5..dca1167 100755 --- a/tests/qemu-iotests/tests/graph-changes-while-io +++ b/tests/qemu-iotests/tests/graph-changes-while-io @@ -27,6 +27,7 @@ from iotests import imgfmt, qemu_img, qemu_img_create, qemu_io, \ top = os.path.join(iotests.test_dir, 'top.img') +mid = os.path.join(iotests.test_dir, 'mid.img') nbd_sock = os.path.join(iotests.sock_dir, 'nbd.sock') @@ -57,6 +58,16 @@ class TestGraphChangesWhileIO(QMPTestCase): def tearDown(self) -> None: self.qsd.stop() + os.remove(top) + + def _wait_for_blockjob(self, status: str) -> None: + done = False + while not done: + for event in self.qsd.get_qmp().get_events(wait=10.0): + if event['event'] != 'JOB_STATUS_CHANGE': + continue + if event['data']['status'] == status: + done = True def test_blockdev_add_while_io(self) -> None: # Run qemu-img bench in the background @@ -116,15 +127,92 @@ class TestGraphChangesWhileIO(QMPTestCase): 'device': 'job0', }) - cancelled = False - while not cancelled: - for event in self.qsd.get_qmp().get_events(wait=10.0): - if event['event'] != 'JOB_STATUS_CHANGE': - continue - if event['data']['status'] == 'null': - cancelled = True + self._wait_for_blockjob('null') + + bench_thr.join() + + def test_remove_lower_snapshot_while_io(self) -> None: + # Run qemu-img bench in the background + bench_thr = Thread(target=do_qemu_img_bench, args=(100000, )) + bench_thr.start() + + # While I/O is performed on 'node0' node, consequently add 2 snapshots + # on top of it, then remove (commit) them starting from lower one. + while bench_thr.is_alive(): + # Recreate snapshot images on every iteration + qemu_img_create('-f', imgfmt, mid, '1G') + qemu_img_create('-f', imgfmt, top, '1G') + + self.qsd.cmd('blockdev-add', { + 'driver': imgfmt, + 'node-name': 'mid', + 'file': { + 'driver': 'file', + 'filename': mid + } + }) + + self.qsd.cmd('blockdev-snapshot', { + 'node': 'node0', + 'overlay': 'mid', + }) + + self.qsd.cmd('blockdev-add', { + 'driver': imgfmt, + 'node-name': 'top', + 'file': { + 'driver': 'file', + 'filename': top + } + }) + + self.qsd.cmd('blockdev-snapshot', { + 'node': 'mid', + 'overlay': 'top', + }) + + self.qsd.cmd('block-commit', { + 'job-id': 'commit-mid', + 'device': 'top', + 'top-node': 'mid', + 'base-node': 'node0', + 'auto-finalize': True, + 'auto-dismiss': False, + }) + + self._wait_for_blockjob('concluded') + self.qsd.cmd('job-dismiss', { + 'id': 'commit-mid', + }) + + self.qsd.cmd('block-commit', { + 'job-id': 'commit-top', + 'device': 'top', + 'top-node': 'top', + 'base-node': 'node0', + 'auto-finalize': True, + 'auto-dismiss': False, + }) + + self._wait_for_blockjob('ready') + self.qsd.cmd('job-complete', { + 'id': 'commit-top', + }) + + self._wait_for_blockjob('concluded') + self.qsd.cmd('job-dismiss', { + 'id': 'commit-top', + }) + + self.qsd.cmd('blockdev-del', { + 'node-name': 'mid' + }) + self.qsd.cmd('blockdev-del', { + 'node-name': 'top' + }) bench_thr.join() + os.remove(mid) if __name__ == '__main__': # Format must support raw backing files diff --git a/tests/qemu-iotests/tests/graph-changes-while-io.out b/tests/qemu-iotests/tests/graph-changes-while-io.out index fbc63e6..8d7e9967 100644 --- a/tests/qemu-iotests/tests/graph-changes-while-io.out +++ b/tests/qemu-iotests/tests/graph-changes-while-io.out @@ -1,5 +1,5 @@ -.. +... ---------------------------------------------------------------------- -Ran 2 tests +Ran 3 tests OK diff --git a/tests/qemu-iotests/tests/inactive-node-nbd b/tests/qemu-iotests/tests/inactive-node-nbd new file mode 100755 index 0000000..a95b37e --- /dev/null +++ b/tests/qemu-iotests/tests/inactive-node-nbd @@ -0,0 +1,303 @@ +#!/usr/bin/env python3 +# group: rw quick +# +# Copyright (C) Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# +# Creator/Owner: Kevin Wolf <kwolf@redhat.com> + +import iotests + +from iotests import QemuIoInteractive +from iotests import filter_qemu_io, filter_qtest, filter_qmp_testfiles + +iotests.script_initialize(supported_fmts=['generic'], + supported_protocols=['file'], + supported_platforms=['linux']) + +def get_export(node_name='disk-fmt', allow_inactive=None): + exp = { + 'id': 'exp0', + 'type': 'nbd', + 'node-name': node_name, + 'writable': True, + } + + if allow_inactive is not None: + exp['allow-inactive'] = allow_inactive + + return exp + +def node_is_active(_vm, node_name): + nodes = _vm.cmd('query-named-block-nodes', flat=True) + node = next(n for n in nodes if n['node-name'] == node_name) + return node['active'] + +with iotests.FilePath('disk.img') as path, \ + iotests.FilePath('snap.qcow2') as snap_path, \ + iotests.FilePath('snap2.qcow2') as snap2_path, \ + iotests.FilePath('target.img') as target_path, \ + iotests.FilePath('nbd.sock', base_dir=iotests.sock_dir) as nbd_sock, \ + iotests.VM() as vm: + + img_size = '10M' + + iotests.log('Preparing disk...') + iotests.qemu_img_create('-f', iotests.imgfmt, path, img_size) + iotests.qemu_img_create('-f', iotests.imgfmt, target_path, img_size) + + iotests.qemu_img_create('-f', 'qcow2', '-b', path, '-F', iotests.imgfmt, + snap_path) + iotests.qemu_img_create('-f', 'qcow2', '-b', snap_path, '-F', 'qcow2', + snap2_path) + + iotests.log('Launching VM...') + vm.add_blockdev(f'file,node-name=disk-file,filename={path}') + vm.add_blockdev(f'{iotests.imgfmt},file=disk-file,node-name=disk-fmt,' + 'active=off') + vm.add_blockdev(f'file,node-name=target-file,filename={target_path}') + vm.add_blockdev(f'{iotests.imgfmt},file=target-file,node-name=target-fmt') + vm.add_blockdev(f'file,node-name=snap-file,filename={snap_path}') + vm.add_blockdev(f'file,node-name=snap2-file,filename={snap2_path}') + + # Actually running the VM activates all images + vm.add_paused() + + vm.launch() + vm.qmp_log('nbd-server-start', + addr={'type': 'unix', 'data':{'path': nbd_sock}}, + filters=[filter_qmp_testfiles]) + + iotests.log('\n=== Creating export of inactive node ===') + + iotests.log('\nExports activate nodes without allow-inactive') + iotests.log('disk-fmt active: %s' % node_is_active(vm, 'disk-fmt')) + vm.qmp_log('block-export-add', **get_export()) + iotests.log('disk-fmt active: %s' % node_is_active(vm, 'disk-fmt')) + vm.qmp_log('query-block-exports') + vm.qmp_log('block-export-del', id='exp0') + vm.event_wait('BLOCK_EXPORT_DELETED') + vm.qmp_log('query-block-exports') + + iotests.log('\nExports activate nodes with allow-inactive=false') + vm.qmp_log('blockdev-set-active', node_name='disk-fmt', active=False) + iotests.log('disk-fmt active: %s' % node_is_active(vm, 'disk-fmt')) + vm.qmp_log('block-export-add', **get_export(allow_inactive=False)) + iotests.log('disk-fmt active: %s' % node_is_active(vm, 'disk-fmt')) + vm.qmp_log('query-block-exports') + vm.qmp_log('block-export-del', id='exp0') + vm.event_wait('BLOCK_EXPORT_DELETED') + vm.qmp_log('query-block-exports') + + iotests.log('\nExport leaves nodes inactive with allow-inactive=true') + vm.qmp_log('blockdev-set-active', node_name='disk-fmt', active=False) + iotests.log('disk-fmt active: %s' % node_is_active(vm, 'disk-fmt')) + vm.qmp_log('block-export-add', **get_export(allow_inactive=True)) + iotests.log('disk-fmt active: %s' % node_is_active(vm, 'disk-fmt')) + vm.qmp_log('query-block-exports') + vm.qmp_log('block-export-del', id='exp0') + vm.event_wait('BLOCK_EXPORT_DELETED') + vm.qmp_log('query-block-exports') + + iotests.log('\n=== Inactivating node with existing export ===') + + iotests.log('\nInactivating nodes with an export fails without ' + 'allow-inactive') + vm.qmp_log('blockdev-set-active', node_name='disk-fmt', active=True) + vm.qmp_log('block-export-add', **get_export(node_name='disk-fmt')) + vm.qmp_log('blockdev-set-active', node_name='disk-fmt', active=False) + iotests.log('disk-fmt active: %s' % node_is_active(vm, 'disk-fmt')) + vm.qmp_log('query-block-exports') + vm.qmp_log('block-export-del', id='exp0') + vm.event_wait('BLOCK_EXPORT_DELETED') + vm.qmp_log('query-block-exports') + + iotests.log('\nInactivating nodes with an export fails with ' + 'allow-inactive=false') + vm.qmp_log('blockdev-set-active', node_name='disk-fmt', active=True) + vm.qmp_log('block-export-add', + **get_export(node_name='disk-fmt', allow_inactive=False)) + vm.qmp_log('blockdev-set-active', node_name='disk-fmt', active=False) + iotests.log('disk-fmt active: %s' % node_is_active(vm, 'disk-fmt')) + vm.qmp_log('query-block-exports') + vm.qmp_log('block-export-del', id='exp0') + vm.event_wait('BLOCK_EXPORT_DELETED') + vm.qmp_log('query-block-exports') + + iotests.log('\nInactivating nodes with an export works with ' + 'allow-inactive=true') + vm.qmp_log('blockdev-set-active', node_name='disk-fmt', active=True) + vm.qmp_log('block-export-add', + **get_export(node_name='disk-fmt', allow_inactive=True)) + vm.qmp_log('blockdev-set-active', node_name='disk-fmt', active=False) + iotests.log('disk-fmt active: %s' % node_is_active(vm, 'disk-fmt')) + vm.qmp_log('query-block-exports') + vm.qmp_log('block-export-del', id='exp0') + vm.event_wait('BLOCK_EXPORT_DELETED') + vm.qmp_log('query-block-exports') + + iotests.log('\n=== Inactive nodes with parent ===') + + iotests.log('\nInactivating nodes with an active parent fails') + vm.qmp_log('blockdev-set-active', node_name='disk-fmt', active=True) + vm.qmp_log('blockdev-set-active', node_name='disk-file', active=False) + iotests.log('disk-file active: %s' % node_is_active(vm, 'disk-file')) + iotests.log('disk-fmt active: %s' % node_is_active(vm, 'disk-fmt')) + + iotests.log('\nInactivating nodes with an inactive parent works') + vm.qmp_log('blockdev-set-active', node_name='disk-fmt', active=False) + vm.qmp_log('blockdev-set-active', node_name='disk-file', active=False) + iotests.log('disk-file active: %s' % node_is_active(vm, 'disk-file')) + iotests.log('disk-fmt active: %s' % node_is_active(vm, 'disk-fmt')) + + iotests.log('\nCreating active parent node with an inactive child fails') + vm.qmp_log('blockdev-add', driver='raw', file='disk-fmt', + node_name='disk-filter') + vm.qmp_log('blockdev-add', driver='raw', file='disk-fmt', + node_name='disk-filter', active=True) + + iotests.log('\nCreating inactive parent node with an inactive child works') + vm.qmp_log('blockdev-add', driver='raw', file='disk-fmt', + node_name='disk-filter', active=False) + vm.qmp_log('blockdev-del', node_name='disk-filter') + + iotests.log('\n=== Resizing an inactive node ===') + vm.qmp_log('block_resize', node_name='disk-fmt', size=16*1024*1024) + + iotests.log('\n=== Taking a snapshot of an inactive node ===') + + iotests.log('\nActive overlay over inactive backing file automatically ' + 'makes both inactive for compatibility') + vm.qmp_log('blockdev-add', driver='qcow2', node_name='snap-fmt', + file='snap-file', backing=None) + iotests.log('disk-fmt active: %s' % node_is_active(vm, 'disk-fmt')) + iotests.log('snap-fmt active: %s' % node_is_active(vm, 'snap-fmt')) + vm.qmp_log('blockdev-snapshot', node='disk-fmt', overlay='snap-fmt') + iotests.log('disk-fmt active: %s' % node_is_active(vm, 'disk-fmt')) + iotests.log('snap-fmt active: %s' % node_is_active(vm, 'snap-fmt')) + vm.qmp_log('blockdev-del', node_name='snap-fmt') + + iotests.log('\nInactive overlay over inactive backing file just works') + vm.qmp_log('blockdev-add', driver='qcow2', node_name='snap-fmt', + file='snap-file', backing=None, active=False) + vm.qmp_log('blockdev-snapshot', node='disk-fmt', overlay='snap-fmt') + + iotests.log('\n=== Block jobs with inactive nodes ===') + + iotests.log('\nStreaming into an inactive node') + vm.qmp_log('block-stream', device='snap-fmt', + filters=[iotests.filter_qmp_generated_node_ids]) + + iotests.log('\nCommitting an inactive root node (active commit)') + vm.qmp_log('block-commit', job_id='job0', device='snap-fmt', + filters=[iotests.filter_qmp_generated_node_ids]) + + iotests.log('\nCommitting an inactive intermediate node to inactive base') + vm.qmp_log('blockdev-add', driver='qcow2', node_name='snap2-fmt', + file='snap2-file', backing='snap-fmt', active=False) + + iotests.log('disk-fmt active: %s' % node_is_active(vm, 'disk-fmt')) + iotests.log('snap-fmt active: %s' % node_is_active(vm, 'snap-fmt')) + iotests.log('snap2-fmt active: %s' % node_is_active(vm, 'snap2-fmt')) + + vm.qmp_log('block-commit', job_id='job0', device='snap2-fmt', + top_node='snap-fmt', + filters=[iotests.filter_qmp_generated_node_ids]) + + iotests.log('\nCommitting an inactive intermediate node to active base') + vm.qmp_log('blockdev-set-active', node_name='disk-fmt', active=True) + vm.qmp_log('block-commit', job_id='job0', device='snap2-fmt', + top_node='snap-fmt', + filters=[iotests.filter_qmp_generated_node_ids]) + + iotests.log('\nMirror from inactive source to active target') + vm.qmp_log('blockdev-mirror', job_id='job0', device='snap2-fmt', + target='target-fmt', sync='full', + filters=[iotests.filter_qmp_generated_node_ids]) + + iotests.log('\nMirror from active source to inactive target') + + iotests.log('disk-fmt active: %s' % node_is_active(vm, 'disk-fmt')) + iotests.log('snap-fmt active: %s' % node_is_active(vm, 'snap-fmt')) + iotests.log('snap2-fmt active: %s' % node_is_active(vm, 'snap2-fmt')) + iotests.log('target-fmt active: %s' % node_is_active(vm, 'target-fmt')) + + # Activating snap2-fmt recursively activates the whole backing chain + vm.qmp_log('blockdev-set-active', node_name='snap2-fmt', active=True) + vm.qmp_log('blockdev-set-active', node_name='target-fmt', active=False) + + iotests.log('disk-fmt active: %s' % node_is_active(vm, 'disk-fmt')) + iotests.log('snap-fmt active: %s' % node_is_active(vm, 'snap-fmt')) + iotests.log('snap2-fmt active: %s' % node_is_active(vm, 'snap2-fmt')) + iotests.log('target-fmt active: %s' % node_is_active(vm, 'target-fmt')) + + vm.qmp_log('blockdev-mirror', job_id='job0', device='snap2-fmt', + target='target-fmt', sync='full', + filters=[iotests.filter_qmp_generated_node_ids]) + + iotests.log('\nBackup from active source to inactive target') + + vm.qmp_log('blockdev-backup', job_id='job0', device='snap2-fmt', + target='target-fmt', sync='full', + filters=[iotests.filter_qmp_generated_node_ids]) + + iotests.log('\nBackup from inactive source to active target') + + # Inactivating snap2-fmt recursively inactivates the whole backing chain + vm.qmp_log('blockdev-set-active', node_name='snap2-fmt', active=False) + vm.qmp_log('blockdev-set-active', node_name='target-fmt', active=True) + + iotests.log('disk-fmt active: %s' % node_is_active(vm, 'disk-fmt')) + iotests.log('snap-fmt active: %s' % node_is_active(vm, 'snap-fmt')) + iotests.log('snap2-fmt active: %s' % node_is_active(vm, 'snap2-fmt')) + iotests.log('target-fmt active: %s' % node_is_active(vm, 'target-fmt')) + + vm.qmp_log('blockdev-backup', job_id='job0', device='snap2-fmt', + target='target-fmt', sync='full', + filters=[iotests.filter_qmp_generated_node_ids]) + + iotests.log('\n=== Accessing export on inactive node ===') + + # Use the target node because it has the right image format and isn't the + # (read-only) backing file of a qcow2 node + vm.qmp_log('blockdev-set-active', node_name='target-fmt', active=False) + vm.qmp_log('block-export-add', + **get_export(node_name='target-fmt', allow_inactive=True)) + + # The read should succeed, everything else should fail gracefully + qemu_io = QemuIoInteractive('-f', 'raw', + f'nbd+unix:///target-fmt?socket={nbd_sock}') + iotests.log(qemu_io.cmd('read 0 64k'), filters=[filter_qemu_io]) + iotests.log(qemu_io.cmd('write 0 64k'), filters=[filter_qemu_io]) + iotests.log(qemu_io.cmd('write -z 0 64k'), filters=[filter_qemu_io]) + iotests.log(qemu_io.cmd('write -zu 0 64k'), filters=[filter_qemu_io]) + iotests.log(qemu_io.cmd('discard 0 64k'), filters=[filter_qemu_io]) + iotests.log(qemu_io.cmd('flush'), filters=[filter_qemu_io]) + iotests.log(qemu_io.cmd('map'), filters=[filter_qemu_io]) + qemu_io.close() + + iotests.log('\n=== Resuming VM activates all images ===') + vm.qmp_log('cont') + + iotests.log('disk-fmt active: %s' % node_is_active(vm, 'disk-fmt')) + iotests.log('snap-fmt active: %s' % node_is_active(vm, 'snap-fmt')) + iotests.log('snap2-fmt active: %s' % node_is_active(vm, 'snap2-fmt')) + iotests.log('target-fmt active: %s' % node_is_active(vm, 'target-fmt')) + + iotests.log('\nShutting down...') + vm.shutdown() + log = vm.get_log() + if log: + iotests.log(log, [filter_qtest, filter_qemu_io]) diff --git a/tests/qemu-iotests/tests/inactive-node-nbd.out b/tests/qemu-iotests/tests/inactive-node-nbd.out new file mode 100644 index 0000000..a458b4f --- /dev/null +++ b/tests/qemu-iotests/tests/inactive-node-nbd.out @@ -0,0 +1,239 @@ +Preparing disk... +Launching VM... +{"execute": "nbd-server-start", "arguments": {"addr": {"data": {"path": "SOCK_DIR/PID-nbd.sock"}, "type": "unix"}}} +{"return": {}} + +=== Creating export of inactive node === + +Exports activate nodes without allow-inactive +disk-fmt active: False +{"execute": "block-export-add", "arguments": {"id": "exp0", "node-name": "disk-fmt", "type": "nbd", "writable": true}} +{"return": {}} +disk-fmt active: True +{"execute": "query-block-exports", "arguments": {}} +{"return": [{"id": "exp0", "node-name": "disk-fmt", "shutting-down": false, "type": "nbd"}]} +{"execute": "block-export-del", "arguments": {"id": "exp0"}} +{"return": {}} +{"execute": "query-block-exports", "arguments": {}} +{"return": []} + +Exports activate nodes with allow-inactive=false +{"execute": "blockdev-set-active", "arguments": {"active": false, "node-name": "disk-fmt"}} +{"return": {}} +disk-fmt active: False +{"execute": "block-export-add", "arguments": {"allow-inactive": false, "id": "exp0", "node-name": "disk-fmt", "type": "nbd", "writable": true}} +{"return": {}} +disk-fmt active: True +{"execute": "query-block-exports", "arguments": {}} +{"return": [{"id": "exp0", "node-name": "disk-fmt", "shutting-down": false, "type": "nbd"}]} +{"execute": "block-export-del", "arguments": {"id": "exp0"}} +{"return": {}} +{"execute": "query-block-exports", "arguments": {}} +{"return": []} + +Export leaves nodes inactive with allow-inactive=true +{"execute": "blockdev-set-active", "arguments": {"active": false, "node-name": "disk-fmt"}} +{"return": {}} +disk-fmt active: False +{"execute": "block-export-add", "arguments": {"allow-inactive": true, "id": "exp0", "node-name": "disk-fmt", "type": "nbd", "writable": true}} +{"return": {}} +disk-fmt active: False +{"execute": "query-block-exports", "arguments": {}} +{"return": [{"id": "exp0", "node-name": "disk-fmt", "shutting-down": false, "type": "nbd"}]} +{"execute": "block-export-del", "arguments": {"id": "exp0"}} +{"return": {}} +{"execute": "query-block-exports", "arguments": {}} +{"return": []} + +=== Inactivating node with existing export === + +Inactivating nodes with an export fails without allow-inactive +{"execute": "blockdev-set-active", "arguments": {"active": true, "node-name": "disk-fmt"}} +{"return": {}} +{"execute": "block-export-add", "arguments": {"id": "exp0", "node-name": "disk-fmt", "type": "nbd", "writable": true}} +{"return": {}} +{"execute": "blockdev-set-active", "arguments": {"active": false, "node-name": "disk-fmt"}} +{"error": {"class": "GenericError", "desc": "Failed to inactivate node: Operation not permitted"}} +disk-fmt active: True +{"execute": "query-block-exports", "arguments": {}} +{"return": [{"id": "exp0", "node-name": "disk-fmt", "shutting-down": false, "type": "nbd"}]} +{"execute": "block-export-del", "arguments": {"id": "exp0"}} +{"return": {}} +{"execute": "query-block-exports", "arguments": {}} +{"return": []} + +Inactivating nodes with an export fails with allow-inactive=false +{"execute": "blockdev-set-active", "arguments": {"active": true, "node-name": "disk-fmt"}} +{"return": {}} +{"execute": "block-export-add", "arguments": {"allow-inactive": false, "id": "exp0", "node-name": "disk-fmt", "type": "nbd", "writable": true}} +{"return": {}} +{"execute": "blockdev-set-active", "arguments": {"active": false, "node-name": "disk-fmt"}} +{"error": {"class": "GenericError", "desc": "Failed to inactivate node: Operation not permitted"}} +disk-fmt active: True +{"execute": "query-block-exports", "arguments": {}} +{"return": [{"id": "exp0", "node-name": "disk-fmt", "shutting-down": false, "type": "nbd"}]} +{"execute": "block-export-del", "arguments": {"id": "exp0"}} +{"return": {}} +{"execute": "query-block-exports", "arguments": {}} +{"return": []} + +Inactivating nodes with an export works with allow-inactive=true +{"execute": "blockdev-set-active", "arguments": {"active": true, "node-name": "disk-fmt"}} +{"return": {}} +{"execute": "block-export-add", "arguments": {"allow-inactive": true, "id": "exp0", "node-name": "disk-fmt", "type": "nbd", "writable": true}} +{"return": {}} +{"execute": "blockdev-set-active", "arguments": {"active": false, "node-name": "disk-fmt"}} +{"return": {}} +disk-fmt active: False +{"execute": "query-block-exports", "arguments": {}} +{"return": [{"id": "exp0", "node-name": "disk-fmt", "shutting-down": false, "type": "nbd"}]} +{"execute": "block-export-del", "arguments": {"id": "exp0"}} +{"return": {}} +{"execute": "query-block-exports", "arguments": {}} +{"return": []} + +=== Inactive nodes with parent === + +Inactivating nodes with an active parent fails +{"execute": "blockdev-set-active", "arguments": {"active": true, "node-name": "disk-fmt"}} +{"return": {}} +{"execute": "blockdev-set-active", "arguments": {"active": false, "node-name": "disk-file"}} +{"error": {"class": "GenericError", "desc": "Node has active parent node"}} +disk-file active: True +disk-fmt active: True + +Inactivating nodes with an inactive parent works +{"execute": "blockdev-set-active", "arguments": {"active": false, "node-name": "disk-fmt"}} +{"return": {}} +{"execute": "blockdev-set-active", "arguments": {"active": false, "node-name": "disk-file"}} +{"return": {}} +disk-file active: False +disk-fmt active: False + +Creating active parent node with an inactive child fails +{"execute": "blockdev-add", "arguments": {"driver": "raw", "file": "disk-fmt", "node-name": "disk-filter"}} +{"error": {"class": "GenericError", "desc": "Inactive 'disk-fmt' can't be a file child of active 'disk-filter'"}} +{"execute": "blockdev-add", "arguments": {"active": true, "driver": "raw", "file": "disk-fmt", "node-name": "disk-filter"}} +{"error": {"class": "GenericError", "desc": "Inactive 'disk-fmt' can't be a file child of active 'disk-filter'"}} + +Creating inactive parent node with an inactive child works +{"execute": "blockdev-add", "arguments": {"active": false, "driver": "raw", "file": "disk-fmt", "node-name": "disk-filter"}} +{"return": {}} +{"execute": "blockdev-del", "arguments": {"node-name": "disk-filter"}} +{"return": {}} + +=== Resizing an inactive node === +{"execute": "block_resize", "arguments": {"node-name": "disk-fmt", "size": 16777216}} +{"error": {"class": "GenericError", "desc": "Permission 'resize' unavailable on inactive node"}} + +=== Taking a snapshot of an inactive node === + +Active overlay over inactive backing file automatically makes both inactive for compatibility +{"execute": "blockdev-add", "arguments": {"backing": null, "driver": "qcow2", "file": "snap-file", "node-name": "snap-fmt"}} +{"return": {}} +disk-fmt active: False +snap-fmt active: True +{"execute": "blockdev-snapshot", "arguments": {"node": "disk-fmt", "overlay": "snap-fmt"}} +{"return": {}} +disk-fmt active: False +snap-fmt active: False +{"execute": "blockdev-del", "arguments": {"node-name": "snap-fmt"}} +{"return": {}} + +Inactive overlay over inactive backing file just works +{"execute": "blockdev-add", "arguments": {"active": false, "backing": null, "driver": "qcow2", "file": "snap-file", "node-name": "snap-fmt"}} +{"return": {}} +{"execute": "blockdev-snapshot", "arguments": {"node": "disk-fmt", "overlay": "snap-fmt"}} +{"return": {}} + +=== Block jobs with inactive nodes === + +Streaming into an inactive node +{"execute": "block-stream", "arguments": {"device": "snap-fmt"}} +{"error": {"class": "GenericError", "desc": "Could not create node: Inactive 'snap-fmt' can't be a file child of active 'NODE_NAME'"}} + +Committing an inactive root node (active commit) +{"execute": "block-commit", "arguments": {"device": "snap-fmt", "job-id": "job0"}} +{"error": {"class": "GenericError", "desc": "Inactive 'snap-fmt' can't be a backing child of active 'NODE_NAME'"}} + +Committing an inactive intermediate node to inactive base +{"execute": "blockdev-add", "arguments": {"active": false, "backing": "snap-fmt", "driver": "qcow2", "file": "snap2-file", "node-name": "snap2-fmt"}} +{"return": {}} +disk-fmt active: False +snap-fmt active: False +snap2-fmt active: False +{"execute": "block-commit", "arguments": {"device": "snap2-fmt", "job-id": "job0", "top-node": "snap-fmt"}} +{"error": {"class": "GenericError", "desc": "Inactive 'snap-fmt' can't be a backing child of active 'NODE_NAME'"}} + +Committing an inactive intermediate node to active base +{"execute": "blockdev-set-active", "arguments": {"active": true, "node-name": "disk-fmt"}} +{"return": {}} +{"execute": "block-commit", "arguments": {"device": "snap2-fmt", "job-id": "job0", "top-node": "snap-fmt"}} +{"error": {"class": "GenericError", "desc": "Inactive 'snap-fmt' can't be a backing child of active 'NODE_NAME'"}} + +Mirror from inactive source to active target +{"execute": "blockdev-mirror", "arguments": {"device": "snap2-fmt", "job-id": "job0", "sync": "full", "target": "target-fmt"}} +{"error": {"class": "GenericError", "desc": "Inactive 'snap2-fmt' can't be a backing child of active 'NODE_NAME'"}} + +Mirror from active source to inactive target +disk-fmt active: True +snap-fmt active: False +snap2-fmt active: False +target-fmt active: True +{"execute": "blockdev-set-active", "arguments": {"active": true, "node-name": "snap2-fmt"}} +{"return": {}} +{"execute": "blockdev-set-active", "arguments": {"active": false, "node-name": "target-fmt"}} +{"return": {}} +disk-fmt active: True +snap-fmt active: True +snap2-fmt active: True +target-fmt active: False +{"execute": "blockdev-mirror", "arguments": {"device": "snap2-fmt", "job-id": "job0", "sync": "full", "target": "target-fmt"}} +{"error": {"class": "GenericError", "desc": "Permission 'write' unavailable on inactive node"}} + +Backup from active source to inactive target +{"execute": "blockdev-backup", "arguments": {"device": "snap2-fmt", "job-id": "job0", "sync": "full", "target": "target-fmt"}} +{"error": {"class": "GenericError", "desc": "Could not create node: Inactive 'target-fmt' can't be a target child of active 'NODE_NAME'"}} + +Backup from inactive source to active target +{"execute": "blockdev-set-active", "arguments": {"active": false, "node-name": "snap2-fmt"}} +{"return": {}} +{"execute": "blockdev-set-active", "arguments": {"active": true, "node-name": "target-fmt"}} +{"return": {}} +disk-fmt active: False +snap-fmt active: False +snap2-fmt active: False +target-fmt active: True +{"execute": "blockdev-backup", "arguments": {"device": "snap2-fmt", "job-id": "job0", "sync": "full", "target": "target-fmt"}} +{"error": {"class": "GenericError", "desc": "Could not create node: Inactive 'snap2-fmt' can't be a file child of active 'NODE_NAME'"}} + +=== Accessing export on inactive node === +{"execute": "blockdev-set-active", "arguments": {"active": false, "node-name": "target-fmt"}} +{"return": {}} +{"execute": "block-export-add", "arguments": {"allow-inactive": true, "id": "exp0", "node-name": "target-fmt", "type": "nbd", "writable": true}} +{"return": {}} +read 65536/65536 bytes at offset 0 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +write failed: Operation not permitted + +write failed: Operation not permitted + +write failed: Operation not permitted + +discard failed: Operation not permitted + + +qemu-io: Failed to get allocation status: Operation not permitted + + +=== Resuming VM activates all images === +{"execute": "cont", "arguments": {}} +{"return": {}} +disk-fmt active: True +snap-fmt active: True +snap2-fmt active: True +target-fmt active: True + +Shutting down... + diff --git a/tests/qemu-iotests/tests/migrate-bitmaps-test b/tests/qemu-iotests/tests/migrate-bitmaps-test index f98e721..8fb4099 100755 --- a/tests/qemu-iotests/tests/migrate-bitmaps-test +++ b/tests/qemu-iotests/tests/migrate-bitmaps-test @@ -122,11 +122,10 @@ class TestDirtyBitmapMigration(iotests.QMPTestCase): # catch 'Could not reopen qcow2 layer: Bitmap already exists' # possible error - log = self.vm_a.get_log() - log = re.sub(r'^\[I \d+\.\d+\] OPENED\n', '', log) - log = re.sub(r'^(wrote .* bytes at offset .*\n.*KiB.*ops.*sec.*\n){3}', + log = iotests.filter_qtest(self.vm_a.get_log()) + log = re.sub(r'^(wrote .* bytes at offset .*\n' + r'.*KiB.*ops.*sec.*\n?){3}', '', log) - log = re.sub(r'\[I \+\d+\.\d+\] CLOSED\n?$', '', log) self.assertEqual(log, '') # test that bitmap is still persistent diff --git a/tests/qemu-iotests/tests/mirror-sparse b/tests/qemu-iotests/tests/mirror-sparse new file mode 100755 index 0000000..cfcaa60 --- /dev/null +++ b/tests/qemu-iotests/tests/mirror-sparse @@ -0,0 +1,128 @@ +#!/usr/bin/env bash +# group: rw auto quick +# +# Test blockdev-mirror with raw sparse destination +# +# Copyright (C) 2025 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +seq="$(basename $0)" +echo "QA output created by $seq" + +status=1 # failure is the default! + +_cleanup() +{ + _cleanup_test_img + _cleanup_qemu +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +cd .. +. ./common.rc +. ./common.filter +. ./common.qemu + +_supported_fmt qcow2 raw # Format of the source. dst is always raw file +_supported_proto file +_supported_os Linux +_require_disk_usage + +echo +echo "=== Initial image setup ===" +echo + +TEST_IMG="$TEST_IMG.base" _make_test_img 20M +$QEMU_IO -c 'w 8M 2M' -f $IMGFMT "$TEST_IMG.base" | _filter_qemu_io + +_launch_qemu \ + -blockdev '{"driver":"file", "cache":{"direct":true, "no-flush":false}, + "filename":"'"$TEST_IMG.base"'", "node-name":"src-file"}' \ + -blockdev '{"driver":"'$IMGFMT'", "node-name":"src", "file":"src-file"}' +h1=$QEMU_HANDLE +_send_qemu_cmd $h1 '{"execute": "qmp_capabilities"}' 'return' + +# Check several combinations; most should result in a sparse destination; +# the destination should only be fully allocated if pre-allocated +# and not punching holes due to detect-zeroes +# do_test creation discard zeroes result +do_test() { + creation=$1 + discard=$2 + zeroes=$3 + expected=$4 + +echo +echo "=== Testing creation=$creation discard=$discard zeroes=$zeroes ===" +echo + +rm -f $TEST_IMG +if test $creation = external; then + truncate --size=20M $TEST_IMG +else + _send_qemu_cmd $h1 '{"execute": "blockdev-create", "arguments": + {"options": {"driver":"file", "filename":"'$TEST_IMG'", + "size":'$((20*1024*1024))', "preallocation":"'$creation'"}, + "job-id":"job1"}}' 'concluded' + _send_qemu_cmd $h1 '{"execute": "job-dismiss", "arguments": + {"id": "job1"}}' 'return' +fi +_send_qemu_cmd $h1 '{"execute": "blockdev-add", "arguments": + {"node-name": "dst", "driver":"file", + "filename":"'$TEST_IMG'", "aio":"threads", + "auto-read-only":true, "discard":"'$discard'", + "detect-zeroes":"'$zeroes'"}}' 'return' +_send_qemu_cmd $h1 '{"execute":"blockdev-mirror", "arguments": + {"sync":"full", "device":"src", "target":"dst", + "job-id":"job2"}}' 'return' +_timed_wait_for $h1 '"ready"' +_send_qemu_cmd $h1 '{"execute": "job-complete", "arguments": + {"id":"job2"}}' 'return' \ + | _filter_block_job_offset | _filter_block_job_len +_send_qemu_cmd $h1 '{"execute": "blockdev-del", "arguments": + {"node-name": "dst"}}' 'return' \ + | _filter_block_job_offset | _filter_block_job_len +$QEMU_IMG compare -U -f $IMGFMT -F raw $TEST_IMG.base $TEST_IMG +# Some filesystems can fudge allocations for various reasons; rather +# than expecting precise 2M and 20M images, it is better to allow for slop. +result=$(disk_usage $TEST_IMG) +if test $result -lt $((4*1024*1024)); then + actual=sparse +elif test $result -gt $((19*1024*1024)); then + actual=full +else + actual="unexpected size ($result)" +fi +echo "Destination is $actual; expected $expected" +} + +do_test external ignore off sparse +do_test external unmap off sparse +do_test external unmap unmap sparse +do_test off ignore off sparse +do_test off unmap off sparse +do_test off unmap unmap sparse +do_test full ignore off full +do_test full unmap off sparse +do_test full unmap unmap sparse + +_send_qemu_cmd $h1 '{"execute":"quit"}' '' + +# success, all done +echo '*** done' +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/tests/mirror-sparse.out b/tests/qemu-iotests/tests/mirror-sparse.out new file mode 100644 index 0000000..2103b89 --- /dev/null +++ b/tests/qemu-iotests/tests/mirror-sparse.out @@ -0,0 +1,365 @@ +QA output created by mirror-sparse + +=== Initial image setup === + +Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=20971520 +wrote 2097152/2097152 bytes at offset 8388608 +2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +{"execute": "qmp_capabilities"} +{"return": {}} + +=== Testing creation=external discard=ignore zeroes=off === + +{"execute": "blockdev-add", "arguments": + {"node-name": "dst", "driver":"file", + "filename":"TEST_DIR/t.IMGFMT", "aio":"threads", + "auto-read-only":true, "discard":"ignore", + "detect-zeroes":"off"}} +{"return": {}} +{"execute":"blockdev-mirror", "arguments": + {"sync":"full", "device":"src", "target":"dst", + "job-id":"job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job2"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "job2"}} +{"execute": "job-complete", "arguments": + {"id":"job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "job2", "len": LEN, "offset": OFFSET, "speed": 0, "type": "mirror"}} +{"return": {}} +{"execute": "blockdev-del", "arguments": + {"node-name": "dst"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "job2", "len": LEN, "offset": OFFSET, "speed": 0, "type": "mirror"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job2"}} +{"return": {}} +Images are identical. +Destination is sparse; expected sparse + +=== Testing creation=external discard=unmap zeroes=off === + +{"execute": "blockdev-add", "arguments": + {"node-name": "dst", "driver":"file", + "filename":"TEST_DIR/t.IMGFMT", "aio":"threads", + "auto-read-only":true, "discard":"unmap", + "detect-zeroes":"off"}} +{"return": {}} +{"execute":"blockdev-mirror", "arguments": + {"sync":"full", "device":"src", "target":"dst", + "job-id":"job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job2"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "job2"}} +{"execute": "job-complete", "arguments": + {"id":"job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "job2", "len": LEN, "offset": OFFSET, "speed": 0, "type": "mirror"}} +{"return": {}} +{"execute": "blockdev-del", "arguments": + {"node-name": "dst"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "job2", "len": LEN, "offset": OFFSET, "speed": 0, "type": "mirror"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job2"}} +{"return": {}} +Images are identical. +Destination is sparse; expected sparse + +=== Testing creation=external discard=unmap zeroes=unmap === + +{"execute": "blockdev-add", "arguments": + {"node-name": "dst", "driver":"file", + "filename":"TEST_DIR/t.IMGFMT", "aio":"threads", + "auto-read-only":true, "discard":"unmap", + "detect-zeroes":"unmap"}} +{"return": {}} +{"execute":"blockdev-mirror", "arguments": + {"sync":"full", "device":"src", "target":"dst", + "job-id":"job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job2"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "job2"}} +{"execute": "job-complete", "arguments": + {"id":"job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "job2", "len": LEN, "offset": OFFSET, "speed": 0, "type": "mirror"}} +{"return": {}} +{"execute": "blockdev-del", "arguments": + {"node-name": "dst"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "job2", "len": LEN, "offset": OFFSET, "speed": 0, "type": "mirror"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job2"}} +{"return": {}} +Images are identical. +Destination is sparse; expected sparse + +=== Testing creation=off discard=ignore zeroes=off === + +{"execute": "blockdev-create", "arguments": + {"options": {"driver":"file", "filename":"TEST_DIR/t.IMGFMT", + "size":20971520, "preallocation":"off"}, + "job-id":"job1"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job1"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job1"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "job1"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "job1"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job1"}} +{"execute": "job-dismiss", "arguments": + {"id": "job1"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job1"}} +{"return": {}} +{"execute": "blockdev-add", "arguments": + {"node-name": "dst", "driver":"file", + "filename":"TEST_DIR/t.IMGFMT", "aio":"threads", + "auto-read-only":true, "discard":"ignore", + "detect-zeroes":"off"}} +{"return": {}} +{"execute":"blockdev-mirror", "arguments": + {"sync":"full", "device":"src", "target":"dst", + "job-id":"job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job2"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "job2"}} +{"execute": "job-complete", "arguments": + {"id":"job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "job2", "len": LEN, "offset": OFFSET, "speed": 0, "type": "mirror"}} +{"return": {}} +{"execute": "blockdev-del", "arguments": + {"node-name": "dst"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "job2", "len": LEN, "offset": OFFSET, "speed": 0, "type": "mirror"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job2"}} +{"return": {}} +Images are identical. +Destination is sparse; expected sparse + +=== Testing creation=off discard=unmap zeroes=off === + +{"execute": "blockdev-create", "arguments": + {"options": {"driver":"file", "filename":"TEST_DIR/t.IMGFMT", + "size":20971520, "preallocation":"off"}, + "job-id":"job1"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job1"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job1"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "job1"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "job1"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job1"}} +{"execute": "job-dismiss", "arguments": + {"id": "job1"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job1"}} +{"return": {}} +{"execute": "blockdev-add", "arguments": + {"node-name": "dst", "driver":"file", + "filename":"TEST_DIR/t.IMGFMT", "aio":"threads", + "auto-read-only":true, "discard":"unmap", + "detect-zeroes":"off"}} +{"return": {}} +{"execute":"blockdev-mirror", "arguments": + {"sync":"full", "device":"src", "target":"dst", + "job-id":"job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job2"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "job2"}} +{"execute": "job-complete", "arguments": + {"id":"job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "job2", "len": LEN, "offset": OFFSET, "speed": 0, "type": "mirror"}} +{"return": {}} +{"execute": "blockdev-del", "arguments": + {"node-name": "dst"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "job2", "len": LEN, "offset": OFFSET, "speed": 0, "type": "mirror"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job2"}} +{"return": {}} +Images are identical. +Destination is sparse; expected sparse + +=== Testing creation=off discard=unmap zeroes=unmap === + +{"execute": "blockdev-create", "arguments": + {"options": {"driver":"file", "filename":"TEST_DIR/t.IMGFMT", + "size":20971520, "preallocation":"off"}, + "job-id":"job1"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job1"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job1"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "job1"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "job1"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job1"}} +{"execute": "job-dismiss", "arguments": + {"id": "job1"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job1"}} +{"return": {}} +{"execute": "blockdev-add", "arguments": + {"node-name": "dst", "driver":"file", + "filename":"TEST_DIR/t.IMGFMT", "aio":"threads", + "auto-read-only":true, "discard":"unmap", + "detect-zeroes":"unmap"}} +{"return": {}} +{"execute":"blockdev-mirror", "arguments": + {"sync":"full", "device":"src", "target":"dst", + "job-id":"job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job2"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "job2"}} +{"execute": "job-complete", "arguments": + {"id":"job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "job2", "len": LEN, "offset": OFFSET, "speed": 0, "type": "mirror"}} +{"return": {}} +{"execute": "blockdev-del", "arguments": + {"node-name": "dst"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "job2", "len": LEN, "offset": OFFSET, "speed": 0, "type": "mirror"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job2"}} +{"return": {}} +Images are identical. +Destination is sparse; expected sparse + +=== Testing creation=full discard=ignore zeroes=off === + +{"execute": "blockdev-create", "arguments": + {"options": {"driver":"file", "filename":"TEST_DIR/t.IMGFMT", + "size":20971520, "preallocation":"full"}, + "job-id":"job1"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job1"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job1"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "job1"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "job1"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job1"}} +{"execute": "job-dismiss", "arguments": + {"id": "job1"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job1"}} +{"return": {}} +{"execute": "blockdev-add", "arguments": + {"node-name": "dst", "driver":"file", + "filename":"TEST_DIR/t.IMGFMT", "aio":"threads", + "auto-read-only":true, "discard":"ignore", + "detect-zeroes":"off"}} +{"return": {}} +{"execute":"blockdev-mirror", "arguments": + {"sync":"full", "device":"src", "target":"dst", + "job-id":"job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job2"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "job2"}} +{"execute": "job-complete", "arguments": + {"id":"job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "job2", "len": LEN, "offset": OFFSET, "speed": 0, "type": "mirror"}} +{"return": {}} +{"execute": "blockdev-del", "arguments": + {"node-name": "dst"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "job2", "len": LEN, "offset": OFFSET, "speed": 0, "type": "mirror"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job2"}} +{"return": {}} +Images are identical. +Destination is full; expected full + +=== Testing creation=full discard=unmap zeroes=off === + +{"execute": "blockdev-create", "arguments": + {"options": {"driver":"file", "filename":"TEST_DIR/t.IMGFMT", + "size":20971520, "preallocation":"full"}, + "job-id":"job1"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job1"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job1"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "job1"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "job1"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job1"}} +{"execute": "job-dismiss", "arguments": + {"id": "job1"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job1"}} +{"return": {}} +{"execute": "blockdev-add", "arguments": + {"node-name": "dst", "driver":"file", + "filename":"TEST_DIR/t.IMGFMT", "aio":"threads", + "auto-read-only":true, "discard":"unmap", + "detect-zeroes":"off"}} +{"return": {}} +{"execute":"blockdev-mirror", "arguments": + {"sync":"full", "device":"src", "target":"dst", + "job-id":"job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job2"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "job2"}} +{"execute": "job-complete", "arguments": + {"id":"job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "job2", "len": LEN, "offset": OFFSET, "speed": 0, "type": "mirror"}} +{"return": {}} +{"execute": "blockdev-del", "arguments": + {"node-name": "dst"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "job2", "len": LEN, "offset": OFFSET, "speed": 0, "type": "mirror"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job2"}} +{"return": {}} +Images are identical. +Destination is sparse; expected sparse + +=== Testing creation=full discard=unmap zeroes=unmap === + +{"execute": "blockdev-create", "arguments": + {"options": {"driver":"file", "filename":"TEST_DIR/t.IMGFMT", + "size":20971520, "preallocation":"full"}, + "job-id":"job1"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job1"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job1"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "job1"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "job1"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job1"}} +{"execute": "job-dismiss", "arguments": + {"id": "job1"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job1"}} +{"return": {}} +{"execute": "blockdev-add", "arguments": + {"node-name": "dst", "driver":"file", + "filename":"TEST_DIR/t.IMGFMT", "aio":"threads", + "auto-read-only":true, "discard":"unmap", + "detect-zeroes":"unmap"}} +{"return": {}} +{"execute":"blockdev-mirror", "arguments": + {"sync":"full", "device":"src", "target":"dst", + "job-id":"job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job2"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "job2"}} +{"execute": "job-complete", "arguments": + {"id":"job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "job2", "len": LEN, "offset": OFFSET, "speed": 0, "type": "mirror"}} +{"return": {}} +{"execute": "blockdev-del", "arguments": + {"node-name": "dst"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "job2", "len": LEN, "offset": OFFSET, "speed": 0, "type": "mirror"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job2"}} +{"return": {}} +Images are identical. +Destination is sparse; expected sparse +{"execute":"quit"} +*** done diff --git a/tests/qemu-iotests/tests/qcow2-encryption b/tests/qemu-iotests/tests/qcow2-encryption new file mode 100755 index 0000000..95f6195 --- /dev/null +++ b/tests/qemu-iotests/tests/qcow2-encryption @@ -0,0 +1,75 @@ +#!/usr/bin/env bash +# group: rw quick +# +# Test case for encryption support in qcow2 +# +# Copyright (C) 2025 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +# creator +owner=kwolf@redhat.com + +seq="$(basename $0)" +echo "QA output created by $seq" + +status=1 # failure is the default! + +_cleanup() +{ + _cleanup_test_img +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ../common.rc +. ../common.filter + +# This tests qcow2-specific low-level functionality +_supported_fmt qcow2 +_supported_proto file +_require_working_luks + +IMG_SIZE=64M + +echo +echo "=== Create an encrypted image ===" +echo + +_make_test_img --object secret,id=sec0,data=123456 -o encrypt.format=luks,encrypt.key-secret=sec0 $IMG_SIZE +$PYTHON ../qcow2.py "$TEST_IMG" dump-header-exts +_img_info +$QEMU_IMG check \ + --object secret,id=sec0,data=123456 \ + --image-opts file.filename="$TEST_IMG",encrypt.key-secret=sec0 \ + | _filter_qemu_img_check + +echo +echo "=== Remove the header extension ===" +echo + +$PYTHON ../qcow2.py "$TEST_IMG" del-header-ext 0x0537be77 +$PYTHON ../qcow2.py "$TEST_IMG" dump-header-exts +_img_info +$QEMU_IMG check \ + --object secret,id=sec0,data=123456 \ + --image-opts file.filename="$TEST_IMG",encrypt.key-secret=sec0 2>&1 \ + | _filter_qemu_img_check \ + | _filter_testdir + +# success, all done +echo "*** done" +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/tests/qcow2-encryption.out b/tests/qemu-iotests/tests/qcow2-encryption.out new file mode 100644 index 0000000..9b549dc2 --- /dev/null +++ b/tests/qemu-iotests/tests/qcow2-encryption.out @@ -0,0 +1,32 @@ +QA output created by qcow2-encryption + +=== Create an encrypted image === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +Header extension: +magic 0x537be77 (Crypto header) +length 16 +data <binary> + +Header extension: +magic 0x6803f857 (Feature table) +length 384 +data <binary> + +image: TEST_DIR/t.IMGFMT +file format: IMGFMT +virtual size: 64 MiB (67108864 bytes) +encrypted: yes +cluster_size: 65536 +No errors were found on the image. + +=== Remove the header extension === + +Header extension: +magic 0x6803f857 (Feature table) +length 384 +data <binary> + +qemu-img: Could not open 'TEST_DIR/t.IMGFMT': Missing CRYPTO header for crypt method 2 +qemu-img: Could not open 'file.filename=TEST_DIR/t.qcow2,encrypt.key-secret=sec0': Missing CRYPTO header for crypt method 2 +*** done diff --git a/tests/qemu-iotests/tests/qsd-migrate b/tests/qemu-iotests/tests/qsd-migrate new file mode 100755 index 0000000..a4c6592 --- /dev/null +++ b/tests/qemu-iotests/tests/qsd-migrate @@ -0,0 +1,140 @@ +#!/usr/bin/env python3 +# group: rw quick +# +# Copyright (C) Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# +# Creator/Owner: Kevin Wolf <kwolf@redhat.com> + +import iotests + +from iotests import filter_qemu_io, filter_qtest + +iotests.script_initialize(supported_fmts=['qcow2', 'qed', 'raw'], + supported_protocols=['file'], + supported_platforms=['linux']) + +with iotests.FilePath('disk.img') as path, \ + iotests.FilePath('nbd-src.sock', base_dir=iotests.sock_dir) as nbd_src, \ + iotests.FilePath('nbd-dst.sock', base_dir=iotests.sock_dir) as nbd_dst, \ + iotests.FilePath('migrate.sock', base_dir=iotests.sock_dir) as mig_sock, \ + iotests.VM(path_suffix="-src") as vm_src, \ + iotests.VM(path_suffix="-dst") as vm_dst: + + img_size = '10M' + + iotests.log('Preparing disk...') + iotests.qemu_img_create('-f', iotests.imgfmt, path, img_size) + + iotests.log('Launching source QSD...') + qsd_src = iotests.QemuStorageDaemon( + '--blockdev', f'file,node-name=disk-file,filename={path}', + '--blockdev', f'{iotests.imgfmt},file=disk-file,node-name=disk-fmt', + '--nbd-server', f'addr.type=unix,addr.path={nbd_src}', + '--export', 'nbd,id=exp0,node-name=disk-fmt,writable=true,' + 'allow-inactive=true', + qmp=True, + ) + + iotests.log('Launching source VM...') + vm_src.add_args('-blockdev', f'nbd,node-name=disk,server.type=unix,' + f'server.path={nbd_src},export=disk-fmt') + vm_src.add_args('-device', 'virtio-blk,drive=disk,id=virtio0') + vm_src.launch() + + iotests.log('Launching destination QSD...') + qsd_dst = iotests.QemuStorageDaemon( + '--blockdev', f'file,node-name=disk-file,filename={path},active=off', + '--blockdev', f'{iotests.imgfmt},file=disk-file,node-name=disk-fmt,' + f'active=off', + '--nbd-server', f'addr.type=unix,addr.path={nbd_dst}', + '--export', 'nbd,id=exp0,node-name=disk-fmt,writable=true,' + 'allow-inactive=true', + qmp=True, + instance_id='b', + ) + + iotests.log('Launching destination VM...') + vm_dst.add_args('-blockdev', f'nbd,node-name=disk,server.type=unix,' + f'server.path={nbd_dst},export=disk-fmt') + vm_dst.add_args('-device', 'virtio-blk,drive=disk,id=virtio0') + vm_dst.add_args('-incoming', f'unix:{mig_sock}') + vm_dst.launch() + + iotests.log('\nTest I/O on the source') + vm_src.hmp_qemu_io('virtio0/virtio-backend', 'write -P 0x11 0 4k', + use_log=True, qdev=True) + vm_src.hmp_qemu_io('virtio0/virtio-backend', 'read -P 0x11 0 4k', + use_log=True, qdev=True) + + iotests.log('\nStarting migration...') + + mig_caps = [ + {'capability': 'events', 'state': True}, + {'capability': 'pause-before-switchover', 'state': True}, + ] + vm_src.qmp_log('migrate-set-capabilities', capabilities=mig_caps) + vm_dst.qmp_log('migrate-set-capabilities', capabilities=mig_caps) + vm_src.qmp_log('migrate', uri=f'unix:{mig_sock}', + filters=[iotests.filter_qmp_testfiles]) + + vm_src.event_wait('MIGRATION', + match={'data': {'status': 'pre-switchover'}}) + + iotests.log('\nPre-switchover: Reconfigure QSD instances') + + iotests.log(qsd_src.qmp('blockdev-set-active', {'active': False})) + + # Reading is okay from both sides while the image is inactive. Note that + # the destination may have stale data until it activates the image, though. + vm_src.hmp_qemu_io('virtio0/virtio-backend', 'read -P 0x11 0 4k', + use_log=True, qdev=True) + vm_dst.hmp_qemu_io('virtio0/virtio-backend', 'read 0 4k', + use_log=True, qdev=True) + + iotests.log(qsd_dst.qmp('blockdev-set-active', {'active': True})) + + iotests.log('\nCompleting migration...') + + vm_src.qmp_log('migrate-continue', state='pre-switchover') + vm_dst.event_wait('MIGRATION', match={'data': {'status': 'completed'}}) + + iotests.log('\nTest I/O on the destination') + + # Now the destination must see what the source wrote + vm_dst.hmp_qemu_io('virtio0/virtio-backend', 'read -P 0x11 0 4k', + use_log=True, qdev=True) + + # And be able to overwrite it + vm_dst.hmp_qemu_io('virtio0/virtio-backend', 'write -P 0x22 0 4k', + use_log=True, qdev=True) + vm_dst.hmp_qemu_io('virtio0/virtio-backend', 'read -P 0x22 0 4k', + use_log=True, qdev=True) + + iotests.log('\nDone') + + vm_src.shutdown() + iotests.log('\n--- vm_src log ---') + log = vm_src.get_log() + if log: + iotests.log(log, [filter_qtest, filter_qemu_io]) + qsd_src.stop() + + vm_dst.shutdown() + iotests.log('\n--- vm_dst log ---') + log = vm_dst.get_log() + if log: + iotests.log(log, [filter_qtest, filter_qemu_io]) + qsd_dst.stop() diff --git a/tests/qemu-iotests/tests/qsd-migrate.out b/tests/qemu-iotests/tests/qsd-migrate.out new file mode 100644 index 0000000..4a5241e --- /dev/null +++ b/tests/qemu-iotests/tests/qsd-migrate.out @@ -0,0 +1,59 @@ +Preparing disk... +Launching source QSD... +Launching source VM... +Launching destination QSD... +Launching destination VM... + +Test I/O on the source +{"execute": "human-monitor-command", "arguments": {"command-line": "qemu-io -d virtio0/virtio-backend \"write -P 0x11 0 4k\""}} +{"return": ""} +{"execute": "human-monitor-command", "arguments": {"command-line": "qemu-io -d virtio0/virtio-backend \"read -P 0x11 0 4k\""}} +{"return": ""} + +Starting migration... +{"execute": "migrate-set-capabilities", "arguments": {"capabilities": [{"capability": "events", "state": true}, {"capability": "pause-before-switchover", "state": true}]}} +{"return": {}} +{"execute": "migrate-set-capabilities", "arguments": {"capabilities": [{"capability": "events", "state": true}, {"capability": "pause-before-switchover", "state": true}]}} +{"return": {}} +{"execute": "migrate", "arguments": {"uri": "unix:SOCK_DIR/PID-migrate.sock"}} +{"return": {}} + +Pre-switchover: Reconfigure QSD instances +{"return": {}} +{"execute": "human-monitor-command", "arguments": {"command-line": "qemu-io -d virtio0/virtio-backend \"read -P 0x11 0 4k\""}} +{"return": ""} +{"execute": "human-monitor-command", "arguments": {"command-line": "qemu-io -d virtio0/virtio-backend \"read 0 4k\""}} +{"return": ""} +{"return": {}} + +Completing migration... +{"execute": "migrate-continue", "arguments": {"state": "pre-switchover"}} +{"return": {}} + +Test I/O on the destination +{"execute": "human-monitor-command", "arguments": {"command-line": "qemu-io -d virtio0/virtio-backend \"read -P 0x11 0 4k\""}} +{"return": ""} +{"execute": "human-monitor-command", "arguments": {"command-line": "qemu-io -d virtio0/virtio-backend \"write -P 0x22 0 4k\""}} +{"return": ""} +{"execute": "human-monitor-command", "arguments": {"command-line": "qemu-io -d virtio0/virtio-backend \"read -P 0x22 0 4k\""}} +{"return": ""} + +Done + +--- vm_src log --- +wrote 4096/4096 bytes at offset 0 +4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 4096/4096 bytes at offset 0 +4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 4096/4096 bytes at offset 0 +4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +--- vm_dst log --- +read 4096/4096 bytes at offset 0 +4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 4096/4096 bytes at offset 0 +4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 4096/4096 bytes at offset 0 +4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 4096/4096 bytes at offset 0 +4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) diff --git a/tests/qemu-iotests/tests/write-zeroes-unmap b/tests/qemu-iotests/tests/write-zeroes-unmap index 7cfeeaf..f90fb8e 100755 --- a/tests/qemu-iotests/tests/write-zeroes-unmap +++ b/tests/qemu-iotests/tests/write-zeroes-unmap @@ -32,6 +32,7 @@ cd .. _supported_fmt raw _supported_proto file _supported_os Linux +_require_disk_usage create_test_image() { _make_test_img -f $IMGFMT 1m diff --git a/tests/qtest/acpi-utils.c b/tests/qtest/acpi-utils.c index 673fc97..9dc24fb 100644 --- a/tests/qtest/acpi-utils.c +++ b/tests/qtest/acpi-utils.c @@ -156,5 +156,4 @@ uint64_t acpi_find_rsdp_address_uefi(QTestState *qts, uint64_t start, g_usleep(TEST_DELAY); } g_assert_not_reached(); - return 0; } diff --git a/tests/qtest/adm1266-test.c b/tests/qtest/adm1266-test.c index 6c312c4..5ae8206 100644 --- a/tests/qtest/adm1266-test.c +++ b/tests/qtest/adm1266-test.c @@ -13,8 +13,8 @@ #include "libqtest-single.h" #include "libqos/qgraph.h" #include "libqos/i2c.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qnum.h" +#include "qobject/qdict.h" +#include "qobject/qnum.h" #include "qemu/bitops.h" #define TEST_ID "adm1266-test" diff --git a/tests/qtest/adm1272-test.c b/tests/qtest/adm1272-test.c index 63f8514..2abda8d 100644 --- a/tests/qtest/adm1272-test.c +++ b/tests/qtest/adm1272-test.c @@ -12,8 +12,8 @@ #include "libqtest-single.h" #include "libqos/qgraph.h" #include "libqos/i2c.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qnum.h" +#include "qobject/qdict.h" +#include "qobject/qnum.h" #include "qemu/bitops.h" #define TEST_ID "adm1272-test" diff --git a/tests/qtest/ahci-test.c b/tests/qtest/ahci-test.c index 5a1923f..e8aabfc 100644 --- a/tests/qtest/ahci-test.c +++ b/tests/qtest/ahci-test.c @@ -30,7 +30,7 @@ #include "libqos/ahci.h" #include "libqos/pci-pc.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "qemu/host-utils.h" #include "hw/pci/pci_ids.h" @@ -1881,7 +1881,6 @@ static void test_io_interface(gconstpointer opaque) sector = offset_sector(opts->offset, opts->address_type, bufsize); test_io_rw_interface(opts->address_type, opts->io_type, bufsize, sector); g_free(opts); - return; } static void create_ahci_io_test(enum IOMode type, enum AddrMode addr, diff --git a/tests/qtest/arm-cpu-features.c b/tests/qtest/arm-cpu-features.c index cfd6f77..eb8ddeb 100644 --- a/tests/qtest/arm-cpu-features.c +++ b/tests/qtest/arm-cpu-features.c @@ -11,8 +11,8 @@ #include "qemu/osdep.h" #include "qemu/bitops.h" #include "libqtest.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qjson.h" +#include "qobject/qdict.h" +#include "qobject/qjson.h" /* * We expect the SVE max-vq to be 16. Also it must be <= 64 @@ -419,21 +419,28 @@ static void pauth_tests_default(QTestState *qts, const char *cpu_type) assert_has_feature_enabled(qts, cpu_type, "pauth"); assert_has_feature_disabled(qts, cpu_type, "pauth-impdef"); assert_has_feature_disabled(qts, cpu_type, "pauth-qarma3"); + assert_has_feature_disabled(qts, cpu_type, "pauth-qarma5"); assert_set_feature(qts, cpu_type, "pauth", false); assert_set_feature(qts, cpu_type, "pauth", true); assert_set_feature(qts, cpu_type, "pauth-impdef", true); assert_set_feature(qts, cpu_type, "pauth-impdef", false); assert_set_feature(qts, cpu_type, "pauth-qarma3", true); assert_set_feature(qts, cpu_type, "pauth-qarma3", false); + assert_set_feature(qts, cpu_type, "pauth-qarma5", true); + assert_set_feature(qts, cpu_type, "pauth-qarma5", false); assert_error(qts, cpu_type, - "cannot enable pauth-impdef or pauth-qarma3 without pauth", + "cannot enable pauth-impdef, pauth-qarma3 or pauth-qarma5 without pauth", "{ 'pauth': false, 'pauth-impdef': true }"); assert_error(qts, cpu_type, - "cannot enable pauth-impdef or pauth-qarma3 without pauth", + "cannot enable pauth-impdef, pauth-qarma3 or pauth-qarma5 without pauth", "{ 'pauth': false, 'pauth-qarma3': true }"); assert_error(qts, cpu_type, - "cannot enable both pauth-impdef and pauth-qarma3", - "{ 'pauth': true, 'pauth-impdef': true, 'pauth-qarma3': true }"); + "cannot enable pauth-impdef, pauth-qarma3 or pauth-qarma5 without pauth", + "{ 'pauth': false, 'pauth-qarma5': true }"); + assert_error(qts, cpu_type, + "cannot enable pauth-impdef, pauth-qarma3 and pauth-qarma5 at the same time", + "{ 'pauth': true, 'pauth-impdef': true, 'pauth-qarma3': true," + " 'pauth-qarma5': true }"); } static void test_query_cpu_model_expansion(const void *data) diff --git a/tests/qtest/aspeed-hace-utils.c b/tests/qtest/aspeed-hace-utils.c new file mode 100644 index 0000000..0f7f911 --- /dev/null +++ b/tests/qtest/aspeed-hace-utils.c @@ -0,0 +1,646 @@ +/* + * QTest testcase for the ASPEED Hash and Crypto Engine + * + * SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2021 IBM Corp. + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "qemu/bitops.h" +#include "aspeed-hace-utils.h" + +/* + * Test vector is the ascii "abc" + * + * Expected results were generated using command line utitiles: + * + * echo -n -e 'abc' | dd of=/tmp/test + * for hash in sha512sum sha384sum sha256sum md5sum; do $hash /tmp/test; done + * + */ +static const uint8_t test_vector[3] = {0x61, 0x62, 0x63}; + +static const uint8_t test_result_sha512[64] = { + 0xdd, 0xaf, 0x35, 0xa1, 0x93, 0x61, 0x7a, 0xba, 0xcc, 0x41, 0x73, 0x49, + 0xae, 0x20, 0x41, 0x31, 0x12, 0xe6, 0xfa, 0x4e, 0x89, 0xa9, 0x7e, 0xa2, + 0x0a, 0x9e, 0xee, 0xe6, 0x4b, 0x55, 0xd3, 0x9a, 0x21, 0x92, 0x99, 0x2a, + 0x27, 0x4f, 0xc1, 0xa8, 0x36, 0xba, 0x3c, 0x23, 0xa3, 0xfe, 0xeb, 0xbd, + 0x45, 0x4d, 0x44, 0x23, 0x64, 0x3c, 0xe8, 0x0e, 0x2a, 0x9a, 0xc9, 0x4f, + 0xa5, 0x4c, 0xa4, 0x9f}; + +static const uint8_t test_result_sha384[48] = { + 0xcb, 0x00, 0x75, 0x3f, 0x45, 0xa3, 0x5e, 0x8b, 0xb5, 0xa0, 0x3d, 0x69, + 0x9a, 0xc6, 0x50, 0x07, 0x27, 0x2c, 0x32, 0xab, 0x0e, 0xde, 0xd1, 0x63, + 0x1a, 0x8b, 0x60, 0x5a, 0x43, 0xff, 0x5b, 0xed, 0x80, 0x86, 0x07, 0x2b, + 0xa1, 0xe7, 0xcc, 0x23, 0x58, 0xba, 0xec, 0xa1, 0x34, 0xc8, 0x25, 0xa7}; + +static const uint8_t test_result_sha256[32] = { + 0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea, 0x41, 0x41, 0x40, 0xde, + 0x5d, 0xae, 0x22, 0x23, 0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c, + 0xb4, 0x10, 0xff, 0x61, 0xf2, 0x00, 0x15, 0xad}; + +static const uint8_t test_result_md5[16] = { + 0x90, 0x01, 0x50, 0x98, 0x3c, 0xd2, 0x4f, 0xb0, 0xd6, 0x96, 0x3f, 0x7d, + 0x28, 0xe1, 0x7f, 0x72}; + +/* + * The Scatter-Gather Test vector is the ascii "abc" "def" "ghi", broken + * into blocks of 3 characters as shown + * + * Expected results were generated using command line utitiles: + * + * echo -n -e 'abcdefghijkl' | dd of=/tmp/test + * for hash in sha512sum sha384sum sha256sum; do $hash /tmp/test; done + * + */ +static const uint8_t test_vector_sg1[6] = {0x61, 0x62, 0x63, 0x64, 0x65, 0x66}; +static const uint8_t test_vector_sg2[3] = {0x67, 0x68, 0x69}; +static const uint8_t test_vector_sg3[3] = {0x6a, 0x6b, 0x6c}; + +static const uint8_t test_result_sg_sha512[64] = { + 0x17, 0x80, 0x7c, 0x72, 0x8e, 0xe3, 0xba, 0x35, 0xe7, 0xcf, 0x7a, 0xf8, + 0x23, 0x11, 0x6d, 0x26, 0xe4, 0x1e, 0x5d, 0x4d, 0x6c, 0x2f, 0xf1, 0xf3, + 0x72, 0x0d, 0x3d, 0x96, 0xaa, 0xcb, 0x6f, 0x69, 0xde, 0x64, 0x2e, 0x63, + 0xd5, 0xb7, 0x3f, 0xc3, 0x96, 0xc1, 0x2b, 0xe3, 0x8b, 0x2b, 0xd5, 0xd8, + 0x84, 0x25, 0x7c, 0x32, 0xc8, 0xf6, 0xd0, 0x85, 0x4a, 0xe6, 0xb5, 0x40, + 0xf8, 0x6d, 0xda, 0x2e}; + +static const uint8_t test_result_sg_sha384[48] = { + 0x10, 0x3c, 0xa9, 0x6c, 0x06, 0xa1, 0xce, 0x79, 0x8f, 0x08, 0xf8, 0xef, + 0xf0, 0xdf, 0xb0, 0xcc, 0xdb, 0x56, 0x7d, 0x48, 0xb2, 0x85, 0xb2, 0x3d, + 0x0c, 0xd7, 0x73, 0x45, 0x46, 0x67, 0xa3, 0xc2, 0xfa, 0x5f, 0x1b, 0x58, + 0xd9, 0xcd, 0xf2, 0x32, 0x9b, 0xd9, 0x97, 0x97, 0x30, 0xbf, 0xaa, 0xff}; + +static const uint8_t test_result_sg_sha256[32] = { + 0xd6, 0x82, 0xed, 0x4c, 0xa4, 0xd9, 0x89, 0xc1, 0x34, 0xec, 0x94, 0xf1, + 0x55, 0x1e, 0x1e, 0xc5, 0x80, 0xdd, 0x6d, 0x5a, 0x6e, 0xcd, 0xe9, 0xf3, + 0xd3, 0x5e, 0x6e, 0x4a, 0x71, 0x7f, 0xbd, 0xe4}; + +/* + * The accumulative mode requires firmware to provide internal initial state + * and message padding (including length L at the end of padding). + * + * This test vector is a ascii text "abc" with padding message. + * + * Expected results were generated using command line utitiles: + * + * echo -n -e 'abc' | dd of=/tmp/test + * for hash in sha512sum sha384sum sha256sum; do $hash /tmp/test; done + */ +static const uint8_t test_vector_accum_512[128] = { + 0x61, 0x62, 0x63, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18}; + +static const uint8_t test_vector_accum_384[128] = { + 0x61, 0x62, 0x63, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18}; + +static const uint8_t test_vector_accum_256[64] = { + 0x61, 0x62, 0x63, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18}; + +static const uint8_t test_result_accum_sha512[64] = { + 0xdd, 0xaf, 0x35, 0xa1, 0x93, 0x61, 0x7a, 0xba, 0xcc, 0x41, 0x73, 0x49, + 0xae, 0x20, 0x41, 0x31, 0x12, 0xe6, 0xfa, 0x4e, 0x89, 0xa9, 0x7e, 0xa2, + 0x0a, 0x9e, 0xee, 0xe6, 0x4b, 0x55, 0xd3, 0x9a, 0x21, 0x92, 0x99, 0x2a, + 0x27, 0x4f, 0xc1, 0xa8, 0x36, 0xba, 0x3c, 0x23, 0xa3, 0xfe, 0xeb, 0xbd, + 0x45, 0x4d, 0x44, 0x23, 0x64, 0x3c, 0xe8, 0x0e, 0x2a, 0x9a, 0xc9, 0x4f, + 0xa5, 0x4c, 0xa4, 0x9f}; + +static const uint8_t test_result_accum_sha384[48] = { + 0xcb, 0x00, 0x75, 0x3f, 0x45, 0xa3, 0x5e, 0x8b, 0xb5, 0xa0, 0x3d, 0x69, + 0x9a, 0xc6, 0x50, 0x07, 0x27, 0x2c, 0x32, 0xab, 0x0e, 0xde, 0xd1, 0x63, + 0x1a, 0x8b, 0x60, 0x5a, 0x43, 0xff, 0x5b, 0xed, 0x80, 0x86, 0x07, 0x2b, + 0xa1, 0xe7, 0xcc, 0x23, 0x58, 0xba, 0xec, 0xa1, 0x34, 0xc8, 0x25, 0xa7}; + +static const uint8_t test_result_accum_sha256[32] = { + 0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea, 0x41, 0x41, 0x40, 0xde, + 0x5d, 0xae, 0x22, 0x23, 0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c, + 0xb4, 0x10, 0xff, 0x61, 0xf2, 0x00, 0x15, 0xad}; + +static void write_regs(QTestState *s, uint32_t base, uint64_t src, + uint32_t length, uint64_t out, uint32_t method) +{ + qtest_writel(s, base + HACE_HASH_SRC, extract64(src, 0, 32)); + qtest_writel(s, base + HACE_HASH_SRC_HI, extract64(src, 32, 32)); + qtest_writel(s, base + HACE_HASH_DIGEST, extract64(out, 0, 32)); + qtest_writel(s, base + HACE_HASH_DIGEST_HI, extract64(out, 32, 32)); + qtest_writel(s, base + HACE_HASH_DATA_LEN, length); + qtest_writel(s, base + HACE_HASH_CMD, HACE_SHA_BE_EN | method); +} + +void aspeed_test_md5(const char *machine, const uint32_t base, + const uint64_t src_addr) + +{ + QTestState *s = qtest_init(machine); + + uint64_t digest_addr = src_addr + 0x010000; + uint8_t digest[16] = {0}; + + /* Check engine is idle, no busy or irq bits set */ + g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0); + + /* Write test vector into memory */ + qtest_memwrite(s, src_addr, test_vector, sizeof(test_vector)); + + write_regs(s, base, src_addr, sizeof(test_vector), + digest_addr, HACE_ALGO_MD5); + + /* Check hash IRQ status is asserted */ + g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0x00000200); + + /* Clear IRQ status and check status is deasserted */ + qtest_writel(s, base + HACE_STS, 0x00000200); + g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0); + + /* Read computed digest from memory */ + qtest_memread(s, digest_addr, digest, sizeof(digest)); + + /* Check result of computation */ + g_assert_cmpmem(digest, sizeof(digest), + test_result_md5, sizeof(digest)); + + qtest_quit(s); +} + +void aspeed_test_sha256(const char *machine, const uint32_t base, + const uint64_t src_addr) +{ + QTestState *s = qtest_init(machine); + + const uint64_t digest_addr = src_addr + 0x10000; + uint8_t digest[32] = {0}; + + /* Check engine is idle, no busy or irq bits set */ + g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0); + + /* Write test vector into memory */ + qtest_memwrite(s, src_addr, test_vector, sizeof(test_vector)); + + write_regs(s, base, src_addr, sizeof(test_vector), digest_addr, + HACE_ALGO_SHA256); + + /* Check hash IRQ status is asserted */ + g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0x00000200); + + /* Clear IRQ status and check status is deasserted */ + qtest_writel(s, base + HACE_STS, 0x00000200); + g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0); + + /* Read computed digest from memory */ + qtest_memread(s, digest_addr, digest, sizeof(digest)); + + /* Check result of computation */ + g_assert_cmpmem(digest, sizeof(digest), + test_result_sha256, sizeof(digest)); + + qtest_quit(s); +} + +void aspeed_test_sha384(const char *machine, const uint32_t base, + const uint64_t src_addr) +{ + QTestState *s = qtest_init(machine); + + const uint64_t digest_addr = src_addr + 0x10000; + uint8_t digest[48] = {0}; + + /* Check engine is idle, no busy or irq bits set */ + g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0); + + /* Write test vector into memory */ + qtest_memwrite(s, src_addr, test_vector, sizeof(test_vector)); + + write_regs(s, base, src_addr, sizeof(test_vector), digest_addr, + HACE_ALGO_SHA384); + + /* Check hash IRQ status is asserted */ + g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0x00000200); + + /* Clear IRQ status and check status is deasserted */ + qtest_writel(s, base + HACE_STS, 0x00000200); + g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0); + + /* Read computed digest from memory */ + qtest_memread(s, digest_addr, digest, sizeof(digest)); + + /* Check result of computation */ + g_assert_cmpmem(digest, sizeof(digest), + test_result_sha384, sizeof(digest)); + + qtest_quit(s); +} + +void aspeed_test_sha512(const char *machine, const uint32_t base, + const uint64_t src_addr) +{ + QTestState *s = qtest_init(machine); + + const uint64_t digest_addr = src_addr + 0x10000; + uint8_t digest[64] = {0}; + + /* Check engine is idle, no busy or irq bits set */ + g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0); + + /* Write test vector into memory */ + qtest_memwrite(s, src_addr, test_vector, sizeof(test_vector)); + + write_regs(s, base, src_addr, sizeof(test_vector), digest_addr, + HACE_ALGO_SHA512); + + /* Check hash IRQ status is asserted */ + g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0x00000200); + + /* Clear IRQ status and check status is deasserted */ + qtest_writel(s, base + HACE_STS, 0x00000200); + g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0); + + /* Read computed digest from memory */ + qtest_memread(s, digest_addr, digest, sizeof(digest)); + + /* Check result of computation */ + g_assert_cmpmem(digest, sizeof(digest), + test_result_sha512, sizeof(digest)); + + qtest_quit(s); +} + +void aspeed_test_sha256_sg(const char *machine, const uint32_t base, + const uint64_t src_addr) +{ + QTestState *s = qtest_init(machine); + + const uint64_t src_addr_1 = src_addr + 0x10000; + const uint64_t src_addr_2 = src_addr + 0x20000; + const uint64_t src_addr_3 = src_addr + 0x30000; + const uint64_t digest_addr = src_addr + 0x40000; + uint8_t digest[32] = {0}; + struct AspeedSgList array[] = { + { cpu_to_le32(sizeof(test_vector_sg1)), + cpu_to_le32(src_addr_1) }, + { cpu_to_le32(sizeof(test_vector_sg2)), + cpu_to_le32(src_addr_2) }, + { cpu_to_le32(sizeof(test_vector_sg3) | SG_LIST_LEN_LAST), + cpu_to_le32(src_addr_3) }, + }; + + /* Check engine is idle, no busy or irq bits set */ + g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0); + + /* Write test vector into memory */ + qtest_memwrite(s, src_addr_1, test_vector_sg1, sizeof(test_vector_sg1)); + qtest_memwrite(s, src_addr_2, test_vector_sg2, sizeof(test_vector_sg2)); + qtest_memwrite(s, src_addr_3, test_vector_sg3, sizeof(test_vector_sg3)); + qtest_memwrite(s, src_addr, array, sizeof(array)); + + write_regs(s, base, src_addr, + (sizeof(test_vector_sg1) + + sizeof(test_vector_sg2) + + sizeof(test_vector_sg3)), + digest_addr, HACE_ALGO_SHA256 | HACE_SG_EN); + + /* Check hash IRQ status is asserted */ + g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0x00000200); + + /* Clear IRQ status and check status is deasserted */ + qtest_writel(s, base + HACE_STS, 0x00000200); + g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0); + + /* Read computed digest from memory */ + qtest_memread(s, digest_addr, digest, sizeof(digest)); + + /* Check result of computation */ + g_assert_cmpmem(digest, sizeof(digest), + test_result_sg_sha256, sizeof(digest)); + + qtest_quit(s); +} + +void aspeed_test_sha384_sg(const char *machine, const uint32_t base, + const uint64_t src_addr) +{ + QTestState *s = qtest_init(machine); + + const uint64_t src_addr_1 = src_addr + 0x10000; + const uint64_t src_addr_2 = src_addr + 0x20000; + const uint64_t src_addr_3 = src_addr + 0x30000; + const uint64_t digest_addr = src_addr + 0x40000; + uint8_t digest[48] = {0}; + struct AspeedSgList array[] = { + { cpu_to_le32(sizeof(test_vector_sg1)), + cpu_to_le32(src_addr_1) }, + { cpu_to_le32(sizeof(test_vector_sg2)), + cpu_to_le32(src_addr_2) }, + { cpu_to_le32(sizeof(test_vector_sg3) | SG_LIST_LEN_LAST), + cpu_to_le32(src_addr_3) }, + }; + + /* Check engine is idle, no busy or irq bits set */ + g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0); + + /* Write test vector into memory */ + qtest_memwrite(s, src_addr_1, test_vector_sg1, sizeof(test_vector_sg1)); + qtest_memwrite(s, src_addr_2, test_vector_sg2, sizeof(test_vector_sg2)); + qtest_memwrite(s, src_addr_3, test_vector_sg3, sizeof(test_vector_sg3)); + qtest_memwrite(s, src_addr, array, sizeof(array)); + + write_regs(s, base, src_addr, + (sizeof(test_vector_sg1) + + sizeof(test_vector_sg2) + + sizeof(test_vector_sg3)), + digest_addr, HACE_ALGO_SHA384 | HACE_SG_EN); + + /* Check hash IRQ status is asserted */ + g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0x00000200); + + /* Clear IRQ status and check status is deasserted */ + qtest_writel(s, base + HACE_STS, 0x00000200); + g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0); + + /* Read computed digest from memory */ + qtest_memread(s, digest_addr, digest, sizeof(digest)); + + /* Check result of computation */ + g_assert_cmpmem(digest, sizeof(digest), + test_result_sg_sha384, sizeof(digest)); + + qtest_quit(s); +} + +void aspeed_test_sha512_sg(const char *machine, const uint32_t base, + const uint64_t src_addr) +{ + QTestState *s = qtest_init(machine); + + const uint64_t src_addr_1 = src_addr + 0x10000; + const uint64_t src_addr_2 = src_addr + 0x20000; + const uint64_t src_addr_3 = src_addr + 0x30000; + const uint64_t digest_addr = src_addr + 0x40000; + uint8_t digest[64] = {0}; + struct AspeedSgList array[] = { + { cpu_to_le32(sizeof(test_vector_sg1)), + cpu_to_le32(src_addr_1) }, + { cpu_to_le32(sizeof(test_vector_sg2)), + cpu_to_le32(src_addr_2) }, + { cpu_to_le32(sizeof(test_vector_sg3) | SG_LIST_LEN_LAST), + cpu_to_le32(src_addr_3) }, + }; + + /* Check engine is idle, no busy or irq bits set */ + g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0); + + /* Write test vector into memory */ + qtest_memwrite(s, src_addr_1, test_vector_sg1, sizeof(test_vector_sg1)); + qtest_memwrite(s, src_addr_2, test_vector_sg2, sizeof(test_vector_sg2)); + qtest_memwrite(s, src_addr_3, test_vector_sg3, sizeof(test_vector_sg3)); + qtest_memwrite(s, src_addr, array, sizeof(array)); + + write_regs(s, base, src_addr, + (sizeof(test_vector_sg1) + + sizeof(test_vector_sg2) + + sizeof(test_vector_sg3)), + digest_addr, HACE_ALGO_SHA512 | HACE_SG_EN); + + /* Check hash IRQ status is asserted */ + g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0x00000200); + + /* Clear IRQ status and check status is deasserted */ + qtest_writel(s, base + HACE_STS, 0x00000200); + g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0); + + /* Read computed digest from memory */ + qtest_memread(s, digest_addr, digest, sizeof(digest)); + + /* Check result of computation */ + g_assert_cmpmem(digest, sizeof(digest), + test_result_sg_sha512, sizeof(digest)); + + qtest_quit(s); +} + +void aspeed_test_sha256_accum(const char *machine, const uint32_t base, + const uint64_t src_addr) +{ + QTestState *s = qtest_init(machine); + + const uint64_t buffer_addr = src_addr + 0x10000; + const uint64_t digest_addr = src_addr + 0x40000; + uint8_t digest[32] = {0}; + struct AspeedSgList array[] = { + { cpu_to_le32(sizeof(test_vector_accum_256) | SG_LIST_LEN_LAST), + cpu_to_le32(buffer_addr) }, + }; + + /* Check engine is idle, no busy or irq bits set */ + g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0); + + /* Write test vector into memory */ + qtest_memwrite(s, buffer_addr, test_vector_accum_256, + sizeof(test_vector_accum_256)); + qtest_memwrite(s, src_addr, array, sizeof(array)); + + write_regs(s, base, src_addr, sizeof(test_vector_accum_256), + digest_addr, HACE_ALGO_SHA256 | HACE_SG_EN | HACE_ACCUM_EN); + + /* Check hash IRQ status is asserted */ + g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0x00000200); + + /* Clear IRQ status and check status is deasserted */ + qtest_writel(s, base + HACE_STS, 0x00000200); + g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0); + + /* Read computed digest from memory */ + qtest_memread(s, digest_addr, digest, sizeof(digest)); + + /* Check result of computation */ + g_assert_cmpmem(digest, sizeof(digest), + test_result_accum_sha256, sizeof(digest)); + + qtest_quit(s); +} + +void aspeed_test_sha384_accum(const char *machine, const uint32_t base, + const uint64_t src_addr) +{ + QTestState *s = qtest_init(machine); + + const uint64_t buffer_addr = src_addr + 0x10000; + const uint64_t digest_addr = src_addr + 0x40000; + uint8_t digest[48] = {0}; + struct AspeedSgList array[] = { + { cpu_to_le32(sizeof(test_vector_accum_384) | SG_LIST_LEN_LAST), + cpu_to_le32(buffer_addr) }, + }; + + /* Check engine is idle, no busy or irq bits set */ + g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0); + + /* Write test vector into memory */ + qtest_memwrite(s, buffer_addr, test_vector_accum_384, + sizeof(test_vector_accum_384)); + qtest_memwrite(s, src_addr, array, sizeof(array)); + + write_regs(s, base, src_addr, sizeof(test_vector_accum_384), + digest_addr, HACE_ALGO_SHA384 | HACE_SG_EN | HACE_ACCUM_EN); + + /* Check hash IRQ status is asserted */ + g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0x00000200); + + /* Clear IRQ status and check status is deasserted */ + qtest_writel(s, base + HACE_STS, 0x00000200); + g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0); + + /* Read computed digest from memory */ + qtest_memread(s, digest_addr, digest, sizeof(digest)); + + /* Check result of computation */ + g_assert_cmpmem(digest, sizeof(digest), + test_result_accum_sha384, sizeof(digest)); + + qtest_quit(s); +} + +void aspeed_test_sha512_accum(const char *machine, const uint32_t base, + const uint64_t src_addr) +{ + QTestState *s = qtest_init(machine); + + const uint64_t buffer_addr = src_addr + 0x10000; + const uint64_t digest_addr = src_addr + 0x40000; + uint8_t digest[64] = {0}; + struct AspeedSgList array[] = { + { cpu_to_le32(sizeof(test_vector_accum_512) | SG_LIST_LEN_LAST), + cpu_to_le32(buffer_addr) }, + }; + + /* Check engine is idle, no busy or irq bits set */ + g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0); + + /* Write test vector into memory */ + qtest_memwrite(s, buffer_addr, test_vector_accum_512, + sizeof(test_vector_accum_512)); + qtest_memwrite(s, src_addr, array, sizeof(array)); + + write_regs(s, base, src_addr, sizeof(test_vector_accum_512), + digest_addr, HACE_ALGO_SHA512 | HACE_SG_EN | HACE_ACCUM_EN); + + /* Check hash IRQ status is asserted */ + g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0x00000200); + + /* Clear IRQ status and check status is deasserted */ + qtest_writel(s, base + HACE_STS, 0x00000200); + g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0); + + /* Read computed digest from memory */ + qtest_memread(s, digest_addr, digest, sizeof(digest)); + + /* Check result of computation */ + g_assert_cmpmem(digest, sizeof(digest), + test_result_accum_sha512, sizeof(digest)); + + qtest_quit(s); +} + +void aspeed_test_addresses(const char *machine, const uint32_t base, + const struct AspeedMasks *expected) +{ + QTestState *s = qtest_init(machine); + + /* + * Check command mode is zero, meaning engine is in direct access mode, + * as this affects the masking behavior of the HASH_SRC register. + */ + g_assert_cmphex(qtest_readl(s, base + HACE_CMD), ==, 0); + g_assert_cmphex(qtest_readl(s, base + HACE_HASH_SRC), ==, 0); + g_assert_cmphex(qtest_readl(s, base + HACE_HASH_SRC_HI), ==, 0); + g_assert_cmphex(qtest_readl(s, base + HACE_HASH_DIGEST), ==, 0); + g_assert_cmphex(qtest_readl(s, base + HACE_HASH_DIGEST_HI), ==, 0); + g_assert_cmphex(qtest_readl(s, base + HACE_HASH_KEY_BUFF), ==, 0); + g_assert_cmphex(qtest_readl(s, base + HACE_HASH_KEY_BUFF_HI), ==, 0); + g_assert_cmphex(qtest_readl(s, base + HACE_HASH_DATA_LEN), ==, 0); + + /* Check that the address masking is correct */ + qtest_writel(s, base + HACE_HASH_SRC, 0xffffffff); + g_assert_cmphex(qtest_readl(s, base + HACE_HASH_SRC), ==, expected->src); + + qtest_writel(s, base + HACE_HASH_SRC_HI, 0xffffffff); + g_assert_cmphex(qtest_readl(s, base + HACE_HASH_SRC_HI), + ==, expected->src_hi); + + qtest_writel(s, base + HACE_HASH_DIGEST, 0xffffffff); + g_assert_cmphex(qtest_readl(s, base + HACE_HASH_DIGEST), ==, + expected->dest); + + qtest_writel(s, base + HACE_HASH_DIGEST_HI, 0xffffffff); + g_assert_cmphex(qtest_readl(s, base + HACE_HASH_DIGEST_HI), ==, + expected->dest_hi); + + qtest_writel(s, base + HACE_HASH_KEY_BUFF, 0xffffffff); + g_assert_cmphex(qtest_readl(s, base + HACE_HASH_KEY_BUFF), ==, + expected->key); + + qtest_writel(s, base + HACE_HASH_KEY_BUFF_HI, 0xffffffff); + g_assert_cmphex(qtest_readl(s, base + HACE_HASH_KEY_BUFF_HI), ==, + expected->key_hi); + + qtest_writel(s, base + HACE_HASH_DATA_LEN, 0xffffffff); + g_assert_cmphex(qtest_readl(s, base + HACE_HASH_DATA_LEN), ==, + expected->len); + + /* Reset to zero */ + qtest_writel(s, base + HACE_HASH_SRC, 0); + qtest_writel(s, base + HACE_HASH_SRC_HI, 0); + qtest_writel(s, base + HACE_HASH_DIGEST, 0); + qtest_writel(s, base + HACE_HASH_DIGEST_HI, 0); + qtest_writel(s, base + HACE_HASH_KEY_BUFF, 0); + qtest_writel(s, base + HACE_HASH_KEY_BUFF_HI, 0); + qtest_writel(s, base + HACE_HASH_DATA_LEN, 0); + + /* Check that all bits are now zero */ + g_assert_cmphex(qtest_readl(s, base + HACE_HASH_SRC), ==, 0); + g_assert_cmphex(qtest_readl(s, base + HACE_HASH_SRC_HI), ==, 0); + g_assert_cmphex(qtest_readl(s, base + HACE_HASH_DIGEST), ==, 0); + g_assert_cmphex(qtest_readl(s, base + HACE_HASH_DIGEST_HI), ==, 0); + g_assert_cmphex(qtest_readl(s, base + HACE_HASH_KEY_BUFF), ==, 0); + g_assert_cmphex(qtest_readl(s, base + HACE_HASH_KEY_BUFF_HI), ==, 0); + g_assert_cmphex(qtest_readl(s, base + HACE_HASH_DATA_LEN), ==, 0); + + qtest_quit(s); +} + diff --git a/tests/qtest/aspeed-hace-utils.h b/tests/qtest/aspeed-hace-utils.h new file mode 100644 index 0000000..c8b2ec4 --- /dev/null +++ b/tests/qtest/aspeed-hace-utils.h @@ -0,0 +1,84 @@ +/* + * QTest testcase for the ASPEED Hash and Crypto Engine + * + * SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2021 IBM Corp. + */ + +#ifndef TESTS_ASPEED_HACE_UTILS_H +#define TESTS_ASPEED_HACE_UTILS_H + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "qemu/bitops.h" + +#define HACE_CMD 0x10 +#define HACE_SHA_BE_EN BIT(3) +#define HACE_MD5_LE_EN BIT(2) +#define HACE_ALGO_MD5 0 +#define HACE_ALGO_SHA1 BIT(5) +#define HACE_ALGO_SHA224 BIT(6) +#define HACE_ALGO_SHA256 (BIT(4) | BIT(6)) +#define HACE_ALGO_SHA512 (BIT(5) | BIT(6)) +#define HACE_ALGO_SHA384 (BIT(5) | BIT(6) | BIT(10)) +#define HACE_SG_EN BIT(18) +#define HACE_ACCUM_EN BIT(8) + +#define HACE_STS 0x1c +#define HACE_RSA_ISR BIT(13) +#define HACE_CRYPTO_ISR BIT(12) +#define HACE_HASH_ISR BIT(9) +#define HACE_RSA_BUSY BIT(2) +#define HACE_CRYPTO_BUSY BIT(1) +#define HACE_HASH_BUSY BIT(0) +#define HACE_HASH_SRC 0x20 +#define HACE_HASH_DIGEST 0x24 +#define HACE_HASH_KEY_BUFF 0x28 +#define HACE_HASH_DATA_LEN 0x2c +#define HACE_HASH_CMD 0x30 +#define HACE_HASH_SRC_HI 0x90 +#define HACE_HASH_DIGEST_HI 0x94 +#define HACE_HASH_KEY_BUFF_HI 0x98 + +/* Scatter-Gather Hash */ +#define SG_LIST_LEN_LAST BIT(31) +struct AspeedSgList { + uint32_t len; + uint32_t addr; +} __attribute__ ((__packed__)); + +struct AspeedMasks { + uint32_t src; + uint32_t dest; + uint32_t key; + uint32_t len; + uint32_t src_hi; + uint32_t dest_hi; + uint32_t key_hi; +}; + +void aspeed_test_md5(const char *machine, const uint32_t base, + const uint64_t src_addr); +void aspeed_test_sha256(const char *machine, const uint32_t base, + const uint64_t src_addr); +void aspeed_test_sha384(const char *machine, const uint32_t base, + const uint64_t src_addr); +void aspeed_test_sha512(const char *machine, const uint32_t base, + const uint64_t src_addr); +void aspeed_test_sha256_sg(const char *machine, const uint32_t base, + const uint64_t src_addr); +void aspeed_test_sha384_sg(const char *machine, const uint32_t base, + const uint64_t src_addr); +void aspeed_test_sha512_sg(const char *machine, const uint32_t base, + const uint64_t src_addr); +void aspeed_test_sha256_accum(const char *machine, const uint32_t base, + const uint64_t src_addr); +void aspeed_test_sha384_accum(const char *machine, const uint32_t base, + const uint64_t src_addr); +void aspeed_test_sha512_accum(const char *machine, const uint32_t base, + const uint64_t src_addr); +void aspeed_test_addresses(const char *machine, const uint32_t base, + const struct AspeedMasks *expected); + +#endif /* TESTS_ASPEED_HACE_UTILS_H */ + diff --git a/tests/qtest/aspeed-smc-utils.c b/tests/qtest/aspeed-smc-utils.c new file mode 100644 index 0000000..c27d09e --- /dev/null +++ b/tests/qtest/aspeed-smc-utils.c @@ -0,0 +1,686 @@ +/* + * QTest testcase for the M25P80 Flash (Using the Aspeed SPI + * Controller) + * + * Copyright (C) 2016 IBM Corp. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu/osdep.h" +#include "qemu/bswap.h" +#include "libqtest-single.h" +#include "qemu/bitops.h" +#include "aspeed-smc-utils.h" + +/* + * Use an explicit bswap for the values read/wrote to the flash region + * as they are BE and the Aspeed CPU is LE. + */ +static inline uint32_t make_be32(uint32_t data) +{ + return bswap32(data); +} + +static inline void spi_writel(const AspeedSMCTestData *data, uint64_t offset, + uint32_t value) +{ + qtest_writel(data->s, data->spi_base + offset, value); +} + +static inline uint32_t spi_readl(const AspeedSMCTestData *data, uint64_t offset) +{ + return qtest_readl(data->s, data->spi_base + offset); +} + +static inline void flash_writeb(const AspeedSMCTestData *data, uint64_t offset, + uint8_t value) +{ + qtest_writeb(data->s, data->flash_base + offset, value); +} + +static inline void flash_writel(const AspeedSMCTestData *data, uint64_t offset, + uint32_t value) +{ + qtest_writel(data->s, data->flash_base + offset, value); +} + +static inline uint8_t flash_readb(const AspeedSMCTestData *data, + uint64_t offset) +{ + return qtest_readb(data->s, data->flash_base + offset); +} + +static inline uint32_t flash_readl(const AspeedSMCTestData *data, + uint64_t offset) +{ + return qtest_readl(data->s, data->flash_base + offset); +} + +static void spi_conf(const AspeedSMCTestData *data, uint32_t value) +{ + uint32_t conf = spi_readl(data, R_CONF); + + conf |= value; + spi_writel(data, R_CONF, conf); +} + +static void spi_conf_remove(const AspeedSMCTestData *data, uint32_t value) +{ + uint32_t conf = spi_readl(data, R_CONF); + + conf &= ~value; + spi_writel(data, R_CONF, conf); +} + +static void spi_ce_ctrl(const AspeedSMCTestData *data, uint32_t value) +{ + uint32_t conf = spi_readl(data, R_CE_CTRL); + + conf |= value; + spi_writel(data, R_CE_CTRL, conf); +} + +static void spi_ctrl_setmode(const AspeedSMCTestData *data, uint8_t mode, + uint8_t cmd) +{ + uint32_t ctrl_reg = R_CTRL0 + data->cs * 4; + uint32_t ctrl = spi_readl(data, ctrl_reg); + ctrl &= ~(CTRL_USERMODE | 0xff << 16); + ctrl |= mode | (cmd << 16); + spi_writel(data, ctrl_reg, ctrl); +} + +static void spi_ctrl_start_user(const AspeedSMCTestData *data) +{ + uint32_t ctrl_reg = R_CTRL0 + data->cs * 4; + uint32_t ctrl = spi_readl(data, ctrl_reg); + + ctrl |= CTRL_USERMODE | CTRL_CE_STOP_ACTIVE; + spi_writel(data, ctrl_reg, ctrl); + + ctrl &= ~CTRL_CE_STOP_ACTIVE; + spi_writel(data, ctrl_reg, ctrl); +} + +static void spi_ctrl_stop_user(const AspeedSMCTestData *data) +{ + uint32_t ctrl_reg = R_CTRL0 + data->cs * 4; + uint32_t ctrl = spi_readl(data, ctrl_reg); + + ctrl |= CTRL_USERMODE | CTRL_CE_STOP_ACTIVE; + spi_writel(data, ctrl_reg, ctrl); +} + +static void spi_ctrl_set_io_mode(const AspeedSMCTestData *data, uint32_t value) +{ + uint32_t ctrl_reg = R_CTRL0 + data->cs * 4; + uint32_t ctrl = spi_readl(data, ctrl_reg); + uint32_t mode; + + mode = value & CTRL_IO_MODE_MASK; + ctrl &= ~CTRL_IO_MODE_MASK; + ctrl |= mode; + spi_writel(data, ctrl_reg, ctrl); +} + +static void flash_reset(const AspeedSMCTestData *data) +{ + spi_conf(data, 1 << (CONF_ENABLE_W0 + data->cs)); + + spi_ctrl_start_user(data); + flash_writeb(data, 0, RESET_ENABLE); + flash_writeb(data, 0, RESET_MEMORY); + flash_writeb(data, 0, WREN); + flash_writeb(data, 0, BULK_ERASE); + flash_writeb(data, 0, WRDI); + spi_ctrl_stop_user(data); + + spi_conf_remove(data, 1 << (CONF_ENABLE_W0 + data->cs)); +} + +static void read_page(const AspeedSMCTestData *data, uint32_t addr, + uint32_t *page) +{ + int i; + + spi_ctrl_start_user(data); + + flash_writeb(data, 0, EN_4BYTE_ADDR); + flash_writeb(data, 0, READ); + flash_writel(data, 0, make_be32(addr)); + + /* Continuous read are supported */ + for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { + page[i] = make_be32(flash_readl(data, 0)); + } + spi_ctrl_stop_user(data); +} + +static void read_page_mem(const AspeedSMCTestData *data, uint32_t addr, + uint32_t *page) +{ + int i; + + /* move out USER mode to use direct reads from the AHB bus */ + spi_ctrl_setmode(data, CTRL_READMODE, READ); + + for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { + page[i] = make_be32(flash_readl(data, addr + i * 4)); + } +} + +static void write_page_mem(const AspeedSMCTestData *data, uint32_t addr, + uint32_t write_value) +{ + spi_ctrl_setmode(data, CTRL_WRITEMODE, PP); + + for (int i = 0; i < FLASH_PAGE_SIZE / 4; i++) { + flash_writel(data, addr + i * 4, write_value); + } +} + +static void assert_page_mem(const AspeedSMCTestData *data, uint32_t addr, + uint32_t expected_value) +{ + uint32_t page[FLASH_PAGE_SIZE / 4]; + read_page_mem(data, addr, page); + for (int i = 0; i < FLASH_PAGE_SIZE / 4; i++) { + g_assert_cmphex(page[i], ==, expected_value); + } +} + +void aspeed_smc_test_read_jedec(const void *data) +{ + const AspeedSMCTestData *test_data = (const AspeedSMCTestData *)data; + uint32_t jedec = 0x0; + + spi_conf(test_data, 1 << (CONF_ENABLE_W0 + test_data->cs)); + + spi_ctrl_start_user(test_data); + flash_writeb(test_data, 0, JEDEC_READ); + jedec |= flash_readb(test_data, 0) << 16; + jedec |= flash_readb(test_data, 0) << 8; + jedec |= flash_readb(test_data, 0); + spi_ctrl_stop_user(test_data); + + flash_reset(test_data); + + g_assert_cmphex(jedec, ==, test_data->jedec_id); +} + +void aspeed_smc_test_erase_sector(const void *data) +{ + const AspeedSMCTestData *test_data = (const AspeedSMCTestData *)data; + uint32_t some_page_addr = test_data->page_addr; + uint32_t page[FLASH_PAGE_SIZE / 4]; + int i; + + spi_conf(test_data, 1 << (CONF_ENABLE_W0 + test_data->cs)); + + /* + * Previous page should be full of 0xffs after backend is + * initialized + */ + read_page(test_data, some_page_addr - FLASH_PAGE_SIZE, page); + for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { + g_assert_cmphex(page[i], ==, 0xffffffff); + } + + spi_ctrl_start_user(test_data); + flash_writeb(test_data, 0, EN_4BYTE_ADDR); + flash_writeb(test_data, 0, WREN); + flash_writeb(test_data, 0, PP); + flash_writel(test_data, 0, make_be32(some_page_addr)); + + /* Fill the page with its own addresses */ + for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { + flash_writel(test_data, 0, make_be32(some_page_addr + i * 4)); + } + spi_ctrl_stop_user(test_data); + + /* Check the page is correctly written */ + read_page(test_data, some_page_addr, page); + for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { + g_assert_cmphex(page[i], ==, some_page_addr + i * 4); + } + + spi_ctrl_start_user(test_data); + flash_writeb(test_data, 0, WREN); + flash_writeb(test_data, 0, EN_4BYTE_ADDR); + flash_writeb(test_data, 0, ERASE_SECTOR); + flash_writel(test_data, 0, make_be32(some_page_addr)); + spi_ctrl_stop_user(test_data); + + /* Check the page is erased */ + read_page(test_data, some_page_addr, page); + for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { + g_assert_cmphex(page[i], ==, 0xffffffff); + } + + flash_reset(test_data); +} + +void aspeed_smc_test_erase_all(const void *data) +{ + const AspeedSMCTestData *test_data = (const AspeedSMCTestData *)data; + uint32_t some_page_addr = test_data->page_addr; + uint32_t page[FLASH_PAGE_SIZE / 4]; + int i; + + spi_conf(test_data, 1 << (CONF_ENABLE_W0 + test_data->cs)); + + /* + * Previous page should be full of 0xffs after backend is + * initialized + */ + read_page(test_data, some_page_addr - FLASH_PAGE_SIZE, page); + for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { + g_assert_cmphex(page[i], ==, 0xffffffff); + } + + spi_ctrl_start_user(test_data); + flash_writeb(test_data, 0, EN_4BYTE_ADDR); + flash_writeb(test_data, 0, WREN); + flash_writeb(test_data, 0, PP); + flash_writel(test_data, 0, make_be32(some_page_addr)); + + /* Fill the page with its own addresses */ + for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { + flash_writel(test_data, 0, make_be32(some_page_addr + i * 4)); + } + spi_ctrl_stop_user(test_data); + + /* Check the page is correctly written */ + read_page(test_data, some_page_addr, page); + for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { + g_assert_cmphex(page[i], ==, some_page_addr + i * 4); + } + + spi_ctrl_start_user(test_data); + flash_writeb(test_data, 0, WREN); + flash_writeb(test_data, 0, BULK_ERASE); + spi_ctrl_stop_user(test_data); + + /* Check the page is erased */ + read_page(test_data, some_page_addr, page); + for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { + g_assert_cmphex(page[i], ==, 0xffffffff); + } + + flash_reset(test_data); +} + +void aspeed_smc_test_write_page(const void *data) +{ + const AspeedSMCTestData *test_data = (const AspeedSMCTestData *)data; + uint32_t my_page_addr = test_data->page_addr; + uint32_t some_page_addr = my_page_addr + FLASH_PAGE_SIZE; + uint32_t page[FLASH_PAGE_SIZE / 4]; + int i; + + spi_conf(test_data, 1 << (CONF_ENABLE_W0 + test_data->cs)); + + spi_ctrl_start_user(test_data); + flash_writeb(test_data, 0, EN_4BYTE_ADDR); + flash_writeb(test_data, 0, WREN); + flash_writeb(test_data, 0, PP); + flash_writel(test_data, 0, make_be32(my_page_addr)); + + /* Fill the page with its own addresses */ + for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { + flash_writel(test_data, 0, make_be32(my_page_addr + i * 4)); + } + spi_ctrl_stop_user(test_data); + + /* Check what was written */ + read_page(test_data, my_page_addr, page); + for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { + g_assert_cmphex(page[i], ==, my_page_addr + i * 4); + } + + /* Check some other page. It should be full of 0xff */ + read_page(test_data, some_page_addr, page); + for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { + g_assert_cmphex(page[i], ==, 0xffffffff); + } + + flash_reset(test_data); +} + +void aspeed_smc_test_read_page_mem(const void *data) +{ + const AspeedSMCTestData *test_data = (const AspeedSMCTestData *)data; + uint32_t my_page_addr = test_data->page_addr; + uint32_t some_page_addr = my_page_addr + FLASH_PAGE_SIZE; + uint32_t page[FLASH_PAGE_SIZE / 4]; + int i; + + /* + * Enable 4BYTE mode for controller. + */ + spi_ce_ctrl(test_data, 1 << (CRTL_EXTENDED0 + test_data->cs)); + + /* Enable 4BYTE mode for flash. */ + spi_conf(test_data, 1 << (CONF_ENABLE_W0 + test_data->cs)); + spi_ctrl_start_user(test_data); + flash_writeb(test_data, 0, EN_4BYTE_ADDR); + flash_writeb(test_data, 0, WREN); + flash_writeb(test_data, 0, PP); + flash_writel(test_data, 0, make_be32(my_page_addr)); + + /* Fill the page with its own addresses */ + for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { + flash_writel(test_data, 0, make_be32(my_page_addr + i * 4)); + } + spi_ctrl_stop_user(test_data); + spi_conf_remove(test_data, 1 << (CONF_ENABLE_W0 + test_data->cs)); + + /* Check what was written */ + read_page_mem(test_data, my_page_addr, page); + for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { + g_assert_cmphex(page[i], ==, my_page_addr + i * 4); + } + + /* Check some other page. It should be full of 0xff */ + read_page_mem(test_data, some_page_addr, page); + for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { + g_assert_cmphex(page[i], ==, 0xffffffff); + } + + flash_reset(test_data); +} + +void aspeed_smc_test_write_page_mem(const void *data) +{ + const AspeedSMCTestData *test_data = (const AspeedSMCTestData *)data; + uint32_t my_page_addr = test_data->page_addr; + uint32_t page[FLASH_PAGE_SIZE / 4]; + int i; + + /* + * Enable 4BYTE mode for controller. + */ + spi_ce_ctrl(test_data, 1 << (CRTL_EXTENDED0 + test_data->cs)); + + /* Enable 4BYTE mode for flash. */ + spi_conf(test_data, 1 << (CONF_ENABLE_W0 + test_data->cs)); + spi_ctrl_start_user(test_data); + flash_writeb(test_data, 0, EN_4BYTE_ADDR); + flash_writeb(test_data, 0, WREN); + spi_ctrl_stop_user(test_data); + + /* move out USER mode to use direct writes to the AHB bus */ + spi_ctrl_setmode(test_data, CTRL_WRITEMODE, PP); + + for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { + flash_writel(test_data, my_page_addr + i * 4, + make_be32(my_page_addr + i * 4)); + } + + /* Check what was written */ + read_page_mem(test_data, my_page_addr, page); + for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { + g_assert_cmphex(page[i], ==, my_page_addr + i * 4); + } + + flash_reset(test_data); +} + +void aspeed_smc_test_read_status_reg(const void *data) +{ + const AspeedSMCTestData *test_data = (const AspeedSMCTestData *)data; + uint8_t r; + + spi_conf(test_data, 1 << (CONF_ENABLE_W0 + test_data->cs)); + + spi_ctrl_start_user(test_data); + flash_writeb(test_data, 0, RDSR); + r = flash_readb(test_data, 0); + spi_ctrl_stop_user(test_data); + + g_assert_cmphex(r & SR_WEL, ==, 0); + g_assert(!qtest_qom_get_bool + (test_data->s, test_data->node, "write-enable")); + + spi_ctrl_start_user(test_data); + flash_writeb(test_data, 0, WREN); + flash_writeb(test_data, 0, RDSR); + r = flash_readb(test_data, 0); + spi_ctrl_stop_user(test_data); + + g_assert_cmphex(r & SR_WEL, ==, SR_WEL); + g_assert(qtest_qom_get_bool + (test_data->s, test_data->node, "write-enable")); + + spi_ctrl_start_user(test_data); + flash_writeb(test_data, 0, WRDI); + flash_writeb(test_data, 0, RDSR); + r = flash_readb(test_data, 0); + spi_ctrl_stop_user(test_data); + + g_assert_cmphex(r & SR_WEL, ==, 0); + g_assert(!qtest_qom_get_bool + (test_data->s, test_data->node, "write-enable")); + + flash_reset(test_data); +} + +void aspeed_smc_test_status_reg_write_protection(const void *data) +{ + const AspeedSMCTestData *test_data = (const AspeedSMCTestData *)data; + uint8_t r; + + spi_conf(test_data, 1 << (CONF_ENABLE_W0 + test_data->cs)); + + /* default case: WP# is high and SRWD is low -> status register writable */ + spi_ctrl_start_user(test_data); + flash_writeb(test_data, 0, WREN); + /* test ability to write SRWD */ + flash_writeb(test_data, 0, WRSR); + flash_writeb(test_data, 0, SRWD); + flash_writeb(test_data, 0, RDSR); + r = flash_readb(test_data, 0); + spi_ctrl_stop_user(test_data); + g_assert_cmphex(r & SRWD, ==, SRWD); + + /* WP# high and SRWD high -> status register writable */ + spi_ctrl_start_user(test_data); + flash_writeb(test_data, 0, WREN); + /* test ability to write SRWD */ + flash_writeb(test_data, 0, WRSR); + flash_writeb(test_data, 0, 0); + flash_writeb(test_data, 0, RDSR); + r = flash_readb(test_data, 0); + spi_ctrl_stop_user(test_data); + g_assert_cmphex(r & SRWD, ==, 0); + + /* WP# low and SRWD low -> status register writable */ + qtest_set_irq_in(test_data->s, test_data->node, "WP#", 0, 0); + spi_ctrl_start_user(test_data); + flash_writeb(test_data, 0, WREN); + /* test ability to write SRWD */ + flash_writeb(test_data, 0, WRSR); + flash_writeb(test_data, 0, SRWD); + flash_writeb(test_data, 0, RDSR); + r = flash_readb(test_data, 0); + spi_ctrl_stop_user(test_data); + g_assert_cmphex(r & SRWD, ==, SRWD); + + /* WP# low and SRWD high -> status register NOT writable */ + spi_ctrl_start_user(test_data); + flash_writeb(test_data, 0 , WREN); + /* test ability to write SRWD */ + flash_writeb(test_data, 0, WRSR); + flash_writeb(test_data, 0, 0); + flash_writeb(test_data, 0, RDSR); + r = flash_readb(test_data, 0); + spi_ctrl_stop_user(test_data); + /* write is not successful */ + g_assert_cmphex(r & SRWD, ==, SRWD); + + qtest_set_irq_in(test_data->s, test_data->node, "WP#", 0, 1); + flash_reset(test_data); +} + +void aspeed_smc_test_write_block_protect(const void *data) +{ + const AspeedSMCTestData *test_data = (const AspeedSMCTestData *)data; + uint32_t sector_size = 65536; + uint32_t n_sectors = 512; + + spi_ce_ctrl(test_data, 1 << (CRTL_EXTENDED0 + test_data->cs)); + spi_conf(test_data, 1 << (CONF_ENABLE_W0 + test_data->cs)); + + uint32_t bp_bits = 0b0; + + for (int i = 0; i < 16; i++) { + bp_bits = ((i & 0b1000) << 3) | ((i & 0b0111) << 2); + + spi_ctrl_start_user(test_data); + flash_writeb(test_data, 0, WREN); + flash_writeb(test_data, 0, BULK_ERASE); + flash_writeb(test_data, 0, WREN); + flash_writeb(test_data, 0, WRSR); + flash_writeb(test_data, 0, bp_bits); + flash_writeb(test_data, 0, EN_4BYTE_ADDR); + flash_writeb(test_data, 0, WREN); + spi_ctrl_stop_user(test_data); + + uint32_t num_protected_sectors = i ? MIN(1 << (i - 1), n_sectors) : 0; + uint32_t protection_start = n_sectors - num_protected_sectors; + uint32_t protection_end = n_sectors; + + for (int sector = 0; sector < n_sectors; sector++) { + uint32_t addr = sector * sector_size; + + assert_page_mem(test_data, addr, 0xffffffff); + write_page_mem(test_data, addr, make_be32(0xabcdef12)); + + uint32_t expected_value = protection_start <= sector + && sector < protection_end + ? 0xffffffff : 0xabcdef12; + + assert_page_mem(test_data, addr, expected_value); + } + } + + flash_reset(test_data); +} + +void aspeed_smc_test_write_block_protect_bottom_bit(const void *data) +{ + const AspeedSMCTestData *test_data = (const AspeedSMCTestData *)data; + uint32_t sector_size = 65536; + uint32_t n_sectors = 512; + + spi_ce_ctrl(test_data, 1 << (CRTL_EXTENDED0 + test_data->cs)); + spi_conf(test_data, 1 << (CONF_ENABLE_W0 + test_data->cs)); + + /* top bottom bit is enabled */ + uint32_t bp_bits = 0b00100 << 3; + + for (int i = 0; i < 16; i++) { + bp_bits = (((i & 0b1000) | 0b0100) << 3) | ((i & 0b0111) << 2); + + spi_ctrl_start_user(test_data); + flash_writeb(test_data, 0, WREN); + flash_writeb(test_data, 0, BULK_ERASE); + flash_writeb(test_data, 0, WREN); + flash_writeb(test_data, 0, WRSR); + flash_writeb(test_data, 0, bp_bits); + flash_writeb(test_data, 0, EN_4BYTE_ADDR); + flash_writeb(test_data, 0, WREN); + spi_ctrl_stop_user(test_data); + + uint32_t num_protected_sectors = i ? MIN(1 << (i - 1), n_sectors) : 0; + uint32_t protection_start = 0; + uint32_t protection_end = num_protected_sectors; + + for (int sector = 0; sector < n_sectors; sector++) { + uint32_t addr = sector * sector_size; + + assert_page_mem(test_data, addr, 0xffffffff); + write_page_mem(test_data, addr, make_be32(0xabcdef12)); + + uint32_t expected_value = protection_start <= sector + && sector < protection_end + ? 0xffffffff : 0xabcdef12; + + assert_page_mem(test_data, addr, expected_value); + } + } + + flash_reset(test_data); +} + +void aspeed_smc_test_write_page_qpi(const void *data) +{ + const AspeedSMCTestData *test_data = (const AspeedSMCTestData *)data; + uint32_t my_page_addr = test_data->page_addr; + uint32_t some_page_addr = my_page_addr + FLASH_PAGE_SIZE; + uint32_t page[FLASH_PAGE_SIZE / 4]; + uint32_t page_pattern[] = { + 0xebd8c134, 0x5da196bc, 0xae15e729, 0x5085ccdf + }; + int i; + + spi_conf(test_data, 1 << (CONF_ENABLE_W0 + test_data->cs)); + + spi_ctrl_start_user(test_data); + flash_writeb(test_data, 0, EN_4BYTE_ADDR); + flash_writeb(test_data, 0, WREN); + flash_writeb(test_data, 0, PP); + flash_writel(test_data, 0, make_be32(my_page_addr)); + + /* Set QPI mode */ + spi_ctrl_set_io_mode(test_data, CTRL_IO_QUAD_IO); + + /* Fill the page pattern */ + for (i = 0; i < ARRAY_SIZE(page_pattern); i++) { + flash_writel(test_data, 0, make_be32(page_pattern[i])); + } + + /* Fill the page with its own addresses */ + for (; i < FLASH_PAGE_SIZE / 4; i++) { + flash_writel(test_data, 0, make_be32(my_page_addr + i * 4)); + } + + /* Restore io mode */ + spi_ctrl_set_io_mode(test_data, 0); + spi_ctrl_stop_user(test_data); + + /* Check what was written */ + read_page(test_data, my_page_addr, page); + for (i = 0; i < ARRAY_SIZE(page_pattern); i++) { + g_assert_cmphex(page[i], ==, page_pattern[i]); + } + for (; i < FLASH_PAGE_SIZE / 4; i++) { + g_assert_cmphex(page[i], ==, my_page_addr + i * 4); + } + + /* Check some other page. It should be full of 0xff */ + read_page(test_data, some_page_addr, page); + for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { + g_assert_cmphex(page[i], ==, 0xffffffff); + } + + flash_reset(test_data); +} + diff --git a/tests/qtest/aspeed-smc-utils.h b/tests/qtest/aspeed-smc-utils.h new file mode 100644 index 0000000..b07870f --- /dev/null +++ b/tests/qtest/aspeed-smc-utils.h @@ -0,0 +1,95 @@ +/* + * QTest testcase for the M25P80 Flash (Using the Aspeed SPI + * Controller) + * + * Copyright (C) 2016 IBM Corp. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef TESTS_ASPEED_SMC_UTILS_H +#define TESTS_ASPEED_SMC_UTILS_H + +#include "qemu/osdep.h" +#include "qemu/bswap.h" +#include "libqtest-single.h" +#include "qemu/bitops.h" + +/* + * ASPEED SPI Controller registers + */ +#define R_CONF 0x00 +#define CONF_ENABLE_W0 16 +#define R_CE_CTRL 0x04 +#define CRTL_EXTENDED0 0 /* 32 bit addressing for SPI */ +#define R_CTRL0 0x10 +#define CTRL_IO_QUAD_IO BIT(31) +#define CTRL_CE_STOP_ACTIVE BIT(2) +#define CTRL_READMODE 0x0 +#define CTRL_FREADMODE 0x1 +#define CTRL_WRITEMODE 0x2 +#define CTRL_USERMODE 0x3 +#define SR_WEL BIT(1) + +/* + * Flash commands + */ +enum { + JEDEC_READ = 0x9f, + RDSR = 0x5, + WRDI = 0x4, + BULK_ERASE = 0xc7, + READ = 0x03, + PP = 0x02, + WRSR = 0x1, + WREN = 0x6, + SRWD = 0x80, + RESET_ENABLE = 0x66, + RESET_MEMORY = 0x99, + EN_4BYTE_ADDR = 0xB7, + ERASE_SECTOR = 0xd8, +}; + +#define CTRL_IO_MODE_MASK (BIT(31) | BIT(30) | BIT(29) | BIT(28)) +#define FLASH_PAGE_SIZE 256 + +typedef struct AspeedSMCTestData { + QTestState *s; + uint64_t spi_base; + uint64_t flash_base; + uint32_t jedec_id; + char *tmp_path; + uint8_t cs; + const char *node; + uint32_t page_addr; +} AspeedSMCTestData; + +void aspeed_smc_test_read_jedec(const void *data); +void aspeed_smc_test_erase_sector(const void *data); +void aspeed_smc_test_erase_all(const void *data); +void aspeed_smc_test_write_page(const void *data); +void aspeed_smc_test_read_page_mem(const void *data); +void aspeed_smc_test_write_page_mem(const void *data); +void aspeed_smc_test_read_status_reg(const void *data); +void aspeed_smc_test_status_reg_write_protection(const void *data); +void aspeed_smc_test_write_block_protect(const void *data); +void aspeed_smc_test_write_block_protect_bottom_bit(const void *data); +void aspeed_smc_test_write_page_qpi(const void *data); + +#endif /* TESTS_ASPEED_SMC_UTILS_H */ diff --git a/tests/qtest/aspeed_gpio-test.c b/tests/qtest/aspeed_gpio-test.c index d38f51d..12675d4 100644 --- a/tests/qtest/aspeed_gpio-test.c +++ b/tests/qtest/aspeed_gpio-test.c @@ -25,7 +25,7 @@ #include "qemu/osdep.h" #include "qemu/bitops.h" #include "qemu/timer.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "libqtest-single.h" #define AST2600_GPIO_BASE 0x1E780000 diff --git a/tests/qtest/aspeed_hace-test.c b/tests/qtest/aspeed_hace-test.c index ce86a44..3877702 100644 --- a/tests/qtest/aspeed_hace-test.c +++ b/tests/qtest/aspeed_hace-test.c @@ -6,599 +6,222 @@ */ #include "qemu/osdep.h" - #include "libqtest.h" #include "qemu/bitops.h" +#include "aspeed-hace-utils.h" -#define HACE_CMD 0x10 -#define HACE_SHA_BE_EN BIT(3) -#define HACE_MD5_LE_EN BIT(2) -#define HACE_ALGO_MD5 0 -#define HACE_ALGO_SHA1 BIT(5) -#define HACE_ALGO_SHA224 BIT(6) -#define HACE_ALGO_SHA256 (BIT(4) | BIT(6)) -#define HACE_ALGO_SHA512 (BIT(5) | BIT(6)) -#define HACE_ALGO_SHA384 (BIT(5) | BIT(6) | BIT(10)) -#define HACE_SG_EN BIT(18) -#define HACE_ACCUM_EN BIT(8) - -#define HACE_STS 0x1c -#define HACE_RSA_ISR BIT(13) -#define HACE_CRYPTO_ISR BIT(12) -#define HACE_HASH_ISR BIT(9) -#define HACE_RSA_BUSY BIT(2) -#define HACE_CRYPTO_BUSY BIT(1) -#define HACE_HASH_BUSY BIT(0) -#define HACE_HASH_SRC 0x20 -#define HACE_HASH_DIGEST 0x24 -#define HACE_HASH_KEY_BUFF 0x28 -#define HACE_HASH_DATA_LEN 0x2c -#define HACE_HASH_CMD 0x30 -/* Scatter-Gather Hash */ -#define SG_LIST_LEN_LAST BIT(31) -struct AspeedSgList { - uint32_t len; - uint32_t addr; -} __attribute__ ((__packed__)); - -/* - * Test vector is the ascii "abc" - * - * Expected results were generated using command line utitiles: - * - * echo -n -e 'abc' | dd of=/tmp/test - * for hash in sha512sum sha256sum md5sum; do $hash /tmp/test; done - * - */ -static const uint8_t test_vector[] = {0x61, 0x62, 0x63}; - -static const uint8_t test_result_sha512[] = { - 0xdd, 0xaf, 0x35, 0xa1, 0x93, 0x61, 0x7a, 0xba, 0xcc, 0x41, 0x73, 0x49, - 0xae, 0x20, 0x41, 0x31, 0x12, 0xe6, 0xfa, 0x4e, 0x89, 0xa9, 0x7e, 0xa2, - 0x0a, 0x9e, 0xee, 0xe6, 0x4b, 0x55, 0xd3, 0x9a, 0x21, 0x92, 0x99, 0x2a, - 0x27, 0x4f, 0xc1, 0xa8, 0x36, 0xba, 0x3c, 0x23, 0xa3, 0xfe, 0xeb, 0xbd, - 0x45, 0x4d, 0x44, 0x23, 0x64, 0x3c, 0xe8, 0x0e, 0x2a, 0x9a, 0xc9, 0x4f, - 0xa5, 0x4c, 0xa4, 0x9f}; +static const struct AspeedMasks ast1030_masks = { + .src = 0x7fffffff, + .dest = 0x7ffffff8, + .key = 0x7ffffff8, + .len = 0x0fffffff, +}; -static const uint8_t test_result_sha256[] = { - 0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea, 0x41, 0x41, 0x40, 0xde, - 0x5d, 0xae, 0x22, 0x23, 0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c, - 0xb4, 0x10, 0xff, 0x61, 0xf2, 0x00, 0x15, 0xad}; +static const struct AspeedMasks ast2600_masks = { + .src = 0x7fffffff, + .dest = 0x7ffffff8, + .key = 0x7ffffff8, + .len = 0x0fffffff, +}; -static const uint8_t test_result_md5[] = { - 0x90, 0x01, 0x50, 0x98, 0x3c, 0xd2, 0x4f, 0xb0, 0xd6, 0x96, 0x3f, 0x7d, - 0x28, 0xe1, 0x7f, 0x72}; +static const struct AspeedMasks ast2500_masks = { + .src = 0x3fffffff, + .dest = 0x3ffffff8, + .key = 0x3fffffc0, + .len = 0x0fffffff, +}; -/* - * The Scatter-Gather Test vector is the ascii "abc" "def" "ghi", broken - * into blocks of 3 characters as shown - * - * Expected results were generated using command line utitiles: - * - * echo -n -e 'abcdefghijkl' | dd of=/tmp/test - * for hash in sha512sum sha256sum; do $hash /tmp/test; done - * - */ -static const uint8_t test_vector_sg1[] = {0x61, 0x62, 0x63, 0x64, 0x65, 0x66}; -static const uint8_t test_vector_sg2[] = {0x67, 0x68, 0x69}; -static const uint8_t test_vector_sg3[] = {0x6a, 0x6b, 0x6c}; - -static const uint8_t test_result_sg_sha512[] = { - 0x17, 0x80, 0x7c, 0x72, 0x8e, 0xe3, 0xba, 0x35, 0xe7, 0xcf, 0x7a, 0xf8, - 0x23, 0x11, 0x6d, 0x26, 0xe4, 0x1e, 0x5d, 0x4d, 0x6c, 0x2f, 0xf1, 0xf3, - 0x72, 0x0d, 0x3d, 0x96, 0xaa, 0xcb, 0x6f, 0x69, 0xde, 0x64, 0x2e, 0x63, - 0xd5, 0xb7, 0x3f, 0xc3, 0x96, 0xc1, 0x2b, 0xe3, 0x8b, 0x2b, 0xd5, 0xd8, - 0x84, 0x25, 0x7c, 0x32, 0xc8, 0xf6, 0xd0, 0x85, 0x4a, 0xe6, 0xb5, 0x40, - 0xf8, 0x6d, 0xda, 0x2e}; - -static const uint8_t test_result_sg_sha256[] = { - 0xd6, 0x82, 0xed, 0x4c, 0xa4, 0xd9, 0x89, 0xc1, 0x34, 0xec, 0x94, 0xf1, - 0x55, 0x1e, 0x1e, 0xc5, 0x80, 0xdd, 0x6d, 0x5a, 0x6e, 0xcd, 0xe9, 0xf3, - 0xd3, 0x5e, 0x6e, 0x4a, 0x71, 0x7f, 0xbd, 0xe4}; +static const struct AspeedMasks ast2400_masks = { + .src = 0x0fffffff, + .dest = 0x0ffffff8, + .key = 0x0fffffc0, + .len = 0x0fffffff, +}; -/* - * The accumulative mode requires firmware to provide internal initial state - * and message padding (including length L at the end of padding). - * - * This test vector is a ascii text "abc" with padding message. - * - * Expected results were generated using command line utitiles: - * - * echo -n -e 'abc' | dd of=/tmp/test - * for hash in sha512sum sha256sum; do $hash /tmp/test; done - */ -static const uint8_t test_vector_accum_512[] = { - 0x61, 0x62, 0x63, 0x80, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18}; - -static const uint8_t test_vector_accum_256[] = { - 0x61, 0x62, 0x63, 0x80, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18}; - -static const uint8_t test_result_accum_sha512[] = { - 0xdd, 0xaf, 0x35, 0xa1, 0x93, 0x61, 0x7a, 0xba, 0xcc, 0x41, 0x73, 0x49, - 0xae, 0x20, 0x41, 0x31, 0x12, 0xe6, 0xfa, 0x4e, 0x89, 0xa9, 0x7e, 0xa2, - 0x0a, 0x9e, 0xee, 0xe6, 0x4b, 0x55, 0xd3, 0x9a, 0x21, 0x92, 0x99, 0x2a, - 0x27, 0x4f, 0xc1, 0xa8, 0x36, 0xba, 0x3c, 0x23, 0xa3, 0xfe, 0xeb, 0xbd, - 0x45, 0x4d, 0x44, 0x23, 0x64, 0x3c, 0xe8, 0x0e, 0x2a, 0x9a, 0xc9, 0x4f, - 0xa5, 0x4c, 0xa4, 0x9f}; - -static const uint8_t test_result_accum_sha256[] = { - 0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea, 0x41, 0x41, 0x40, 0xde, - 0x5d, 0xae, 0x22, 0x23, 0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c, - 0xb4, 0x10, 0xff, 0x61, 0xf2, 0x00, 0x15, 0xad}; - -static void write_regs(QTestState *s, uint32_t base, uint32_t src, - uint32_t length, uint32_t out, uint32_t method) +/* ast1030 */ +static void test_md5_ast1030(void) { - qtest_writel(s, base + HACE_HASH_SRC, src); - qtest_writel(s, base + HACE_HASH_DIGEST, out); - qtest_writel(s, base + HACE_HASH_DATA_LEN, length); - qtest_writel(s, base + HACE_HASH_CMD, HACE_SHA_BE_EN | method); + aspeed_test_md5("-machine ast1030-evb", 0x7e6d0000, 0x00000000); } -static void test_md5(const char *machine, const uint32_t base, - const uint32_t src_addr) - +static void test_sha256_ast1030(void) { - QTestState *s = qtest_init(machine); - - uint32_t digest_addr = src_addr + 0x01000000; - uint8_t digest[16] = {0}; - - /* Check engine is idle, no busy or irq bits set */ - g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0); - - /* Write test vector into memory */ - qtest_memwrite(s, src_addr, test_vector, sizeof(test_vector)); - - write_regs(s, base, src_addr, sizeof(test_vector), digest_addr, HACE_ALGO_MD5); - - /* Check hash IRQ status is asserted */ - g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0x00000200); - - /* Clear IRQ status and check status is deasserted */ - qtest_writel(s, base + HACE_STS, 0x00000200); - g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0); - - /* Read computed digest from memory */ - qtest_memread(s, digest_addr, digest, sizeof(digest)); - - /* Check result of computation */ - g_assert_cmpmem(digest, sizeof(digest), - test_result_md5, sizeof(digest)); - - qtest_quit(s); + aspeed_test_sha256("-machine ast1030-evb", 0x7e6d0000, 0x00000000); } -static void test_sha256(const char *machine, const uint32_t base, - const uint32_t src_addr) +static void test_sha256_sg_ast1030(void) { - QTestState *s = qtest_init(machine); - - const uint32_t digest_addr = src_addr + 0x1000000; - uint8_t digest[32] = {0}; - - /* Check engine is idle, no busy or irq bits set */ - g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0); - - /* Write test vector into memory */ - qtest_memwrite(s, src_addr, test_vector, sizeof(test_vector)); - - write_regs(s, base, src_addr, sizeof(test_vector), digest_addr, HACE_ALGO_SHA256); - - /* Check hash IRQ status is asserted */ - g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0x00000200); - - /* Clear IRQ status and check status is deasserted */ - qtest_writel(s, base + HACE_STS, 0x00000200); - g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0); - - /* Read computed digest from memory */ - qtest_memread(s, digest_addr, digest, sizeof(digest)); - - /* Check result of computation */ - g_assert_cmpmem(digest, sizeof(digest), - test_result_sha256, sizeof(digest)); - - qtest_quit(s); + aspeed_test_sha256_sg("-machine ast1030-evb", 0x7e6d0000, 0x00000000); } -static void test_sha512(const char *machine, const uint32_t base, - const uint32_t src_addr) +static void test_sha384_ast1030(void) { - QTestState *s = qtest_init(machine); - - const uint32_t digest_addr = src_addr + 0x1000000; - uint8_t digest[64] = {0}; - - /* Check engine is idle, no busy or irq bits set */ - g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0); - - /* Write test vector into memory */ - qtest_memwrite(s, src_addr, test_vector, sizeof(test_vector)); - - write_regs(s, base, src_addr, sizeof(test_vector), digest_addr, HACE_ALGO_SHA512); - - /* Check hash IRQ status is asserted */ - g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0x00000200); - - /* Clear IRQ status and check status is deasserted */ - qtest_writel(s, base + HACE_STS, 0x00000200); - g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0); - - /* Read computed digest from memory */ - qtest_memread(s, digest_addr, digest, sizeof(digest)); - - /* Check result of computation */ - g_assert_cmpmem(digest, sizeof(digest), - test_result_sha512, sizeof(digest)); - - qtest_quit(s); + aspeed_test_sha384("-machine ast1030-evb", 0x7e6d0000, 0x00000000); } -static void test_sha256_sg(const char *machine, const uint32_t base, - const uint32_t src_addr) +static void test_sha384_sg_ast1030(void) { - QTestState *s = qtest_init(machine); - - const uint32_t src_addr_1 = src_addr + 0x1000000; - const uint32_t src_addr_2 = src_addr + 0x2000000; - const uint32_t src_addr_3 = src_addr + 0x3000000; - const uint32_t digest_addr = src_addr + 0x4000000; - uint8_t digest[32] = {0}; - struct AspeedSgList array[] = { - { cpu_to_le32(sizeof(test_vector_sg1)), - cpu_to_le32(src_addr_1) }, - { cpu_to_le32(sizeof(test_vector_sg2)), - cpu_to_le32(src_addr_2) }, - { cpu_to_le32(sizeof(test_vector_sg3) | SG_LIST_LEN_LAST), - cpu_to_le32(src_addr_3) }, - }; - - /* Check engine is idle, no busy or irq bits set */ - g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0); - - /* Write test vector into memory */ - qtest_memwrite(s, src_addr_1, test_vector_sg1, sizeof(test_vector_sg1)); - qtest_memwrite(s, src_addr_2, test_vector_sg2, sizeof(test_vector_sg2)); - qtest_memwrite(s, src_addr_3, test_vector_sg3, sizeof(test_vector_sg3)); - qtest_memwrite(s, src_addr, array, sizeof(array)); - - write_regs(s, base, src_addr, - (sizeof(test_vector_sg1) - + sizeof(test_vector_sg2) - + sizeof(test_vector_sg3)), - digest_addr, HACE_ALGO_SHA256 | HACE_SG_EN); - - /* Check hash IRQ status is asserted */ - g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0x00000200); - - /* Clear IRQ status and check status is deasserted */ - qtest_writel(s, base + HACE_STS, 0x00000200); - g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0); - - /* Read computed digest from memory */ - qtest_memread(s, digest_addr, digest, sizeof(digest)); - - /* Check result of computation */ - g_assert_cmpmem(digest, sizeof(digest), - test_result_sg_sha256, sizeof(digest)); - - qtest_quit(s); + aspeed_test_sha384_sg("-machine ast1030-evb", 0x7e6d0000, 0x00000000); } -static void test_sha512_sg(const char *machine, const uint32_t base, - const uint32_t src_addr) +static void test_sha512_ast1030(void) { - QTestState *s = qtest_init(machine); - - const uint32_t src_addr_1 = src_addr + 0x1000000; - const uint32_t src_addr_2 = src_addr + 0x2000000; - const uint32_t src_addr_3 = src_addr + 0x3000000; - const uint32_t digest_addr = src_addr + 0x4000000; - uint8_t digest[64] = {0}; - struct AspeedSgList array[] = { - { cpu_to_le32(sizeof(test_vector_sg1)), - cpu_to_le32(src_addr_1) }, - { cpu_to_le32(sizeof(test_vector_sg2)), - cpu_to_le32(src_addr_2) }, - { cpu_to_le32(sizeof(test_vector_sg3) | SG_LIST_LEN_LAST), - cpu_to_le32(src_addr_3) }, - }; - - /* Check engine is idle, no busy or irq bits set */ - g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0); - - /* Write test vector into memory */ - qtest_memwrite(s, src_addr_1, test_vector_sg1, sizeof(test_vector_sg1)); - qtest_memwrite(s, src_addr_2, test_vector_sg2, sizeof(test_vector_sg2)); - qtest_memwrite(s, src_addr_3, test_vector_sg3, sizeof(test_vector_sg3)); - qtest_memwrite(s, src_addr, array, sizeof(array)); - - write_regs(s, base, src_addr, - (sizeof(test_vector_sg1) - + sizeof(test_vector_sg2) - + sizeof(test_vector_sg3)), - digest_addr, HACE_ALGO_SHA512 | HACE_SG_EN); - - /* Check hash IRQ status is asserted */ - g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0x00000200); - - /* Clear IRQ status and check status is deasserted */ - qtest_writel(s, base + HACE_STS, 0x00000200); - g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0); - - /* Read computed digest from memory */ - qtest_memread(s, digest_addr, digest, sizeof(digest)); - - /* Check result of computation */ - g_assert_cmpmem(digest, sizeof(digest), - test_result_sg_sha512, sizeof(digest)); - - qtest_quit(s); + aspeed_test_sha512("-machine ast1030-evb", 0x7e6d0000, 0x00000000); } -static void test_sha256_accum(const char *machine, const uint32_t base, - const uint32_t src_addr) +static void test_sha512_sg_ast1030(void) { - QTestState *s = qtest_init(machine); - - const uint32_t buffer_addr = src_addr + 0x1000000; - const uint32_t digest_addr = src_addr + 0x4000000; - uint8_t digest[32] = {0}; - struct AspeedSgList array[] = { - { cpu_to_le32(sizeof(test_vector_accum_256) | SG_LIST_LEN_LAST), - cpu_to_le32(buffer_addr) }, - }; - - /* Check engine is idle, no busy or irq bits set */ - g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0); - - /* Write test vector into memory */ - qtest_memwrite(s, buffer_addr, test_vector_accum_256, - sizeof(test_vector_accum_256)); - qtest_memwrite(s, src_addr, array, sizeof(array)); - - write_regs(s, base, src_addr, sizeof(test_vector_accum_256), - digest_addr, HACE_ALGO_SHA256 | HACE_SG_EN | HACE_ACCUM_EN); - - /* Check hash IRQ status is asserted */ - g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0x00000200); - - /* Clear IRQ status and check status is deasserted */ - qtest_writel(s, base + HACE_STS, 0x00000200); - g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0); - - /* Read computed digest from memory */ - qtest_memread(s, digest_addr, digest, sizeof(digest)); - - /* Check result of computation */ - g_assert_cmpmem(digest, sizeof(digest), - test_result_accum_sha256, sizeof(digest)); - - qtest_quit(s); + aspeed_test_sha512_sg("-machine ast1030-evb", 0x7e6d0000, 0x00000000); } -static void test_sha512_accum(const char *machine, const uint32_t base, - const uint32_t src_addr) +static void test_sha256_accum_ast1030(void) { - QTestState *s = qtest_init(machine); - - const uint32_t buffer_addr = src_addr + 0x1000000; - const uint32_t digest_addr = src_addr + 0x4000000; - uint8_t digest[64] = {0}; - struct AspeedSgList array[] = { - { cpu_to_le32(sizeof(test_vector_accum_512) | SG_LIST_LEN_LAST), - cpu_to_le32(buffer_addr) }, - }; - - /* Check engine is idle, no busy or irq bits set */ - g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0); - - /* Write test vector into memory */ - qtest_memwrite(s, buffer_addr, test_vector_accum_512, - sizeof(test_vector_accum_512)); - qtest_memwrite(s, src_addr, array, sizeof(array)); - - write_regs(s, base, src_addr, sizeof(test_vector_accum_512), - digest_addr, HACE_ALGO_SHA512 | HACE_SG_EN | HACE_ACCUM_EN); - - /* Check hash IRQ status is asserted */ - g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0x00000200); - - /* Clear IRQ status and check status is deasserted */ - qtest_writel(s, base + HACE_STS, 0x00000200); - g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0); - - /* Read computed digest from memory */ - qtest_memread(s, digest_addr, digest, sizeof(digest)); - - /* Check result of computation */ - g_assert_cmpmem(digest, sizeof(digest), - test_result_accum_sha512, sizeof(digest)); - - qtest_quit(s); + aspeed_test_sha256_accum("-machine ast1030-evb", 0x7e6d0000, 0x00000000); } -struct masks { - uint32_t src; - uint32_t dest; - uint32_t len; -}; - -static const struct masks ast2600_masks = { - .src = 0x7fffffff, - .dest = 0x7ffffff8, - .len = 0x0fffffff, -}; - -static const struct masks ast2500_masks = { - .src = 0x3fffffff, - .dest = 0x3ffffff8, - .len = 0x0fffffff, -}; - -static const struct masks ast2400_masks = { - .src = 0x0fffffff, - .dest = 0x0ffffff8, - .len = 0x0fffffff, -}; - -static void test_addresses(const char *machine, const uint32_t base, - const struct masks *expected) +static void test_sha384_accum_ast1030(void) { - QTestState *s = qtest_init(machine); - - /* - * Check command mode is zero, meaning engine is in direct access mode, - * as this affects the masking behavior of the HASH_SRC register. - */ - g_assert_cmphex(qtest_readl(s, base + HACE_CMD), ==, 0); - g_assert_cmphex(qtest_readl(s, base + HACE_HASH_SRC), ==, 0); - g_assert_cmphex(qtest_readl(s, base + HACE_HASH_DIGEST), ==, 0); - g_assert_cmphex(qtest_readl(s, base + HACE_HASH_DATA_LEN), ==, 0); - - - /* Check that the address masking is correct */ - qtest_writel(s, base + HACE_HASH_SRC, 0xffffffff); - g_assert_cmphex(qtest_readl(s, base + HACE_HASH_SRC), ==, expected->src); - - qtest_writel(s, base + HACE_HASH_DIGEST, 0xffffffff); - g_assert_cmphex(qtest_readl(s, base + HACE_HASH_DIGEST), ==, expected->dest); - - qtest_writel(s, base + HACE_HASH_DATA_LEN, 0xffffffff); - g_assert_cmphex(qtest_readl(s, base + HACE_HASH_DATA_LEN), ==, expected->len); - - /* Reset to zero */ - qtest_writel(s, base + HACE_HASH_SRC, 0); - qtest_writel(s, base + HACE_HASH_DIGEST, 0); - qtest_writel(s, base + HACE_HASH_DATA_LEN, 0); + aspeed_test_sha384_accum("-machine ast1030-evb", 0x7e6d0000, 0x00000000); +} - /* Check that all bits are now zero */ - g_assert_cmphex(qtest_readl(s, base + HACE_HASH_SRC), ==, 0); - g_assert_cmphex(qtest_readl(s, base + HACE_HASH_DIGEST), ==, 0); - g_assert_cmphex(qtest_readl(s, base + HACE_HASH_DATA_LEN), ==, 0); +static void test_sha512_accum_ast1030(void) +{ + aspeed_test_sha512_accum("-machine ast1030-evb", 0x7e6d0000, 0x00000000); +} - qtest_quit(s); +static void test_addresses_ast1030(void) +{ + aspeed_test_addresses("-machine ast1030-evb", 0x7e6d0000, &ast1030_masks); } /* ast2600 */ static void test_md5_ast2600(void) { - test_md5("-machine ast2600-evb", 0x1e6d0000, 0x80000000); + aspeed_test_md5("-machine ast2600-evb", 0x1e6d0000, 0x80000000); } static void test_sha256_ast2600(void) { - test_sha256("-machine ast2600-evb", 0x1e6d0000, 0x80000000); + aspeed_test_sha256("-machine ast2600-evb", 0x1e6d0000, 0x80000000); } static void test_sha256_sg_ast2600(void) { - test_sha256_sg("-machine ast2600-evb", 0x1e6d0000, 0x80000000); + aspeed_test_sha256_sg("-machine ast2600-evb", 0x1e6d0000, 0x80000000); +} + +static void test_sha384_ast2600(void) +{ + aspeed_test_sha384("-machine ast2600-evb", 0x1e6d0000, 0x80000000); +} + +static void test_sha384_sg_ast2600(void) +{ + aspeed_test_sha384_sg("-machine ast2600-evb", 0x1e6d0000, 0x80000000); } static void test_sha512_ast2600(void) { - test_sha512("-machine ast2600-evb", 0x1e6d0000, 0x80000000); + aspeed_test_sha512("-machine ast2600-evb", 0x1e6d0000, 0x80000000); } static void test_sha512_sg_ast2600(void) { - test_sha512_sg("-machine ast2600-evb", 0x1e6d0000, 0x80000000); + aspeed_test_sha512_sg("-machine ast2600-evb", 0x1e6d0000, 0x80000000); } static void test_sha256_accum_ast2600(void) { - test_sha256_accum("-machine ast2600-evb", 0x1e6d0000, 0x80000000); + aspeed_test_sha256_accum("-machine ast2600-evb", 0x1e6d0000, 0x80000000); +} + +static void test_sha384_accum_ast2600(void) +{ + aspeed_test_sha384_accum("-machine ast2600-evb", 0x1e6d0000, 0x80000000); } static void test_sha512_accum_ast2600(void) { - test_sha512_accum("-machine ast2600-evb", 0x1e6d0000, 0x80000000); + aspeed_test_sha512_accum("-machine ast2600-evb", 0x1e6d0000, 0x80000000); } static void test_addresses_ast2600(void) { - test_addresses("-machine ast2600-evb", 0x1e6d0000, &ast2600_masks); + aspeed_test_addresses("-machine ast2600-evb", 0x1e6d0000, &ast2600_masks); } /* ast2500 */ static void test_md5_ast2500(void) { - test_md5("-machine ast2500-evb", 0x1e6e3000, 0x80000000); + aspeed_test_md5("-machine ast2500-evb", 0x1e6e3000, 0x80000000); } static void test_sha256_ast2500(void) { - test_sha256("-machine ast2500-evb", 0x1e6e3000, 0x80000000); + aspeed_test_sha256("-machine ast2500-evb", 0x1e6e3000, 0x80000000); } static void test_sha512_ast2500(void) { - test_sha512("-machine ast2500-evb", 0x1e6e3000, 0x80000000); + aspeed_test_sha512("-machine ast2500-evb", 0x1e6e3000, 0x80000000); } static void test_addresses_ast2500(void) { - test_addresses("-machine ast2500-evb", 0x1e6e3000, &ast2500_masks); + aspeed_test_addresses("-machine ast2500-evb", 0x1e6e3000, &ast2500_masks); } /* ast2400 */ static void test_md5_ast2400(void) { - test_md5("-machine palmetto-bmc", 0x1e6e3000, 0x40000000); + aspeed_test_md5("-machine palmetto-bmc", 0x1e6e3000, 0x40000000); } static void test_sha256_ast2400(void) { - test_sha256("-machine palmetto-bmc", 0x1e6e3000, 0x40000000); + aspeed_test_sha256("-machine palmetto-bmc", 0x1e6e3000, 0x40000000); } static void test_sha512_ast2400(void) { - test_sha512("-machine palmetto-bmc", 0x1e6e3000, 0x40000000); + aspeed_test_sha512("-machine palmetto-bmc", 0x1e6e3000, 0x40000000); } static void test_addresses_ast2400(void) { - test_addresses("-machine palmetto-bmc", 0x1e6e3000, &ast2400_masks); + aspeed_test_addresses("-machine palmetto-bmc", 0x1e6e3000, &ast2400_masks); } int main(int argc, char **argv) { g_test_init(&argc, &argv, NULL); + qtest_add_func("ast1030/hace/addresses", test_addresses_ast1030); + qtest_add_func("ast1030/hace/sha512", test_sha512_ast1030); + qtest_add_func("ast1030/hace/sha384", test_sha384_ast1030); + qtest_add_func("ast1030/hace/sha256", test_sha256_ast1030); + qtest_add_func("ast1030/hace/md5", test_md5_ast1030); + + qtest_add_func("ast1030/hace/sha512_sg", test_sha512_sg_ast1030); + qtest_add_func("ast1030/hace/sha384_sg", test_sha384_sg_ast1030); + qtest_add_func("ast1030/hace/sha256_sg", test_sha256_sg_ast1030); + + qtest_add_func("ast1030/hace/sha512_accum", test_sha512_accum_ast1030); + qtest_add_func("ast1030/hace/sha384_accum", test_sha384_accum_ast1030); + qtest_add_func("ast1030/hace/sha256_accum", test_sha256_accum_ast1030); + qtest_add_func("ast2600/hace/addresses", test_addresses_ast2600); qtest_add_func("ast2600/hace/sha512", test_sha512_ast2600); + qtest_add_func("ast2600/hace/sha384", test_sha384_ast2600); qtest_add_func("ast2600/hace/sha256", test_sha256_ast2600); qtest_add_func("ast2600/hace/md5", test_md5_ast2600); qtest_add_func("ast2600/hace/sha512_sg", test_sha512_sg_ast2600); + qtest_add_func("ast2600/hace/sha384_sg", test_sha384_sg_ast2600); qtest_add_func("ast2600/hace/sha256_sg", test_sha256_sg_ast2600); qtest_add_func("ast2600/hace/sha512_accum", test_sha512_accum_ast2600); + qtest_add_func("ast2600/hace/sha384_accum", test_sha384_accum_ast2600); qtest_add_func("ast2600/hace/sha256_accum", test_sha256_accum_ast2600); qtest_add_func("ast2500/hace/addresses", test_addresses_ast2500); diff --git a/tests/qtest/aspeed_smc-test.c b/tests/qtest/aspeed_smc-test.c index c713a37..52a00e6 100644 --- a/tests/qtest/aspeed_smc-test.c +++ b/tests/qtest/aspeed_smc-test.c @@ -27,623 +27,211 @@ #include "qemu/bswap.h" #include "libqtest-single.h" #include "qemu/bitops.h" +#include "aspeed-smc-utils.h" -/* - * ASPEED SPI Controller registers - */ -#define R_CONF 0x00 -#define CONF_ENABLE_W0 (1 << 16) -#define R_CE_CTRL 0x04 -#define CRTL_EXTENDED0 0 /* 32 bit addressing for SPI */ -#define R_CTRL0 0x10 -#define CTRL_CE_STOP_ACTIVE (1 << 2) -#define CTRL_READMODE 0x0 -#define CTRL_FREADMODE 0x1 -#define CTRL_WRITEMODE 0x2 -#define CTRL_USERMODE 0x3 -#define SR_WEL BIT(1) - -#define ASPEED_FMC_BASE 0x1E620000 -#define ASPEED_FLASH_BASE 0x20000000 - -/* - * Flash commands - */ -enum { - JEDEC_READ = 0x9f, - RDSR = 0x5, - WRDI = 0x4, - BULK_ERASE = 0xc7, - READ = 0x03, - PP = 0x02, - WRSR = 0x1, - WREN = 0x6, - SRWD = 0x80, - RESET_ENABLE = 0x66, - RESET_MEMORY = 0x99, - EN_4BYTE_ADDR = 0xB7, - ERASE_SECTOR = 0xd8, -}; - -#define FLASH_JEDEC 0x20ba19 /* n25q256a */ -#define FLASH_SIZE (32 * 1024 * 1024) - -#define FLASH_PAGE_SIZE 256 - -/* - * Use an explicit bswap for the values read/wrote to the flash region - * as they are BE and the Aspeed CPU is LE. - */ -static inline uint32_t make_be32(uint32_t data) +static void test_palmetto_bmc(AspeedSMCTestData *data) { - return bswap32(data); -} - -static void spi_conf(uint32_t value) -{ - uint32_t conf = readl(ASPEED_FMC_BASE + R_CONF); - - conf |= value; - writel(ASPEED_FMC_BASE + R_CONF, conf); -} - -static void spi_conf_remove(uint32_t value) -{ - uint32_t conf = readl(ASPEED_FMC_BASE + R_CONF); - - conf &= ~value; - writel(ASPEED_FMC_BASE + R_CONF, conf); -} - -static void spi_ce_ctrl(uint32_t value) -{ - uint32_t conf = readl(ASPEED_FMC_BASE + R_CE_CTRL); - - conf |= value; - writel(ASPEED_FMC_BASE + R_CE_CTRL, conf); -} - -static void spi_ctrl_setmode(uint8_t mode, uint8_t cmd) -{ - uint32_t ctrl = readl(ASPEED_FMC_BASE + R_CTRL0); - ctrl &= ~(CTRL_USERMODE | 0xff << 16); - ctrl |= mode | (cmd << 16); - writel(ASPEED_FMC_BASE + R_CTRL0, ctrl); -} - -static void spi_ctrl_start_user(void) -{ - uint32_t ctrl = readl(ASPEED_FMC_BASE + R_CTRL0); - - ctrl |= CTRL_USERMODE | CTRL_CE_STOP_ACTIVE; - writel(ASPEED_FMC_BASE + R_CTRL0, ctrl); - - ctrl &= ~CTRL_CE_STOP_ACTIVE; - writel(ASPEED_FMC_BASE + R_CTRL0, ctrl); -} - -static void spi_ctrl_stop_user(void) -{ - uint32_t ctrl = readl(ASPEED_FMC_BASE + R_CTRL0); - - ctrl |= CTRL_USERMODE | CTRL_CE_STOP_ACTIVE; - writel(ASPEED_FMC_BASE + R_CTRL0, ctrl); -} - -static void flash_reset(void) -{ - spi_conf(CONF_ENABLE_W0); - - spi_ctrl_start_user(); - writeb(ASPEED_FLASH_BASE, RESET_ENABLE); - writeb(ASPEED_FLASH_BASE, RESET_MEMORY); - writeb(ASPEED_FLASH_BASE, WREN); - writeb(ASPEED_FLASH_BASE, BULK_ERASE); - writeb(ASPEED_FLASH_BASE, WRDI); - spi_ctrl_stop_user(); - - spi_conf_remove(CONF_ENABLE_W0); -} - -static void test_read_jedec(void) -{ - uint32_t jedec = 0x0; - - spi_conf(CONF_ENABLE_W0); - - spi_ctrl_start_user(); - writeb(ASPEED_FLASH_BASE, JEDEC_READ); - jedec |= readb(ASPEED_FLASH_BASE) << 16; - jedec |= readb(ASPEED_FLASH_BASE) << 8; - jedec |= readb(ASPEED_FLASH_BASE); - spi_ctrl_stop_user(); - - flash_reset(); - - g_assert_cmphex(jedec, ==, FLASH_JEDEC); -} - -static void read_page(uint32_t addr, uint32_t *page) -{ - int i; - - spi_ctrl_start_user(); - - writeb(ASPEED_FLASH_BASE, EN_4BYTE_ADDR); - writeb(ASPEED_FLASH_BASE, READ); - writel(ASPEED_FLASH_BASE, make_be32(addr)); - - /* Continuous read are supported */ - for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { - page[i] = make_be32(readl(ASPEED_FLASH_BASE)); - } - spi_ctrl_stop_user(); -} - -static void read_page_mem(uint32_t addr, uint32_t *page) -{ - int i; - - /* move out USER mode to use direct reads from the AHB bus */ - spi_ctrl_setmode(CTRL_READMODE, READ); - - for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { - page[i] = make_be32(readl(ASPEED_FLASH_BASE + addr + i * 4)); - } -} - -static void write_page_mem(uint32_t addr, uint32_t write_value) -{ - spi_ctrl_setmode(CTRL_WRITEMODE, PP); - - for (int i = 0; i < FLASH_PAGE_SIZE / 4; i++) { - writel(ASPEED_FLASH_BASE + addr + i * 4, write_value); - } -} - -static void assert_page_mem(uint32_t addr, uint32_t expected_value) -{ - uint32_t page[FLASH_PAGE_SIZE / 4]; - read_page_mem(addr, page); - for (int i = 0; i < FLASH_PAGE_SIZE / 4; i++) { - g_assert_cmphex(page[i], ==, expected_value); - } -} - -static void test_erase_sector(void) -{ - uint32_t some_page_addr = 0x600 * FLASH_PAGE_SIZE; - uint32_t page[FLASH_PAGE_SIZE / 4]; - int i; - - spi_conf(CONF_ENABLE_W0); - - /* - * Previous page should be full of 0xffs after backend is - * initialized - */ - read_page(some_page_addr - FLASH_PAGE_SIZE, page); - for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { - g_assert_cmphex(page[i], ==, 0xffffffff); - } - - spi_ctrl_start_user(); - writeb(ASPEED_FLASH_BASE, EN_4BYTE_ADDR); - writeb(ASPEED_FLASH_BASE, WREN); - writeb(ASPEED_FLASH_BASE, PP); - writel(ASPEED_FLASH_BASE, make_be32(some_page_addr)); - - /* Fill the page with its own addresses */ - for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { - writel(ASPEED_FLASH_BASE, make_be32(some_page_addr + i * 4)); - } - spi_ctrl_stop_user(); - - /* Check the page is correctly written */ - read_page(some_page_addr, page); - for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { - g_assert_cmphex(page[i], ==, some_page_addr + i * 4); - } - - spi_ctrl_start_user(); - writeb(ASPEED_FLASH_BASE, WREN); - writeb(ASPEED_FLASH_BASE, EN_4BYTE_ADDR); - writeb(ASPEED_FLASH_BASE, ERASE_SECTOR); - writel(ASPEED_FLASH_BASE, make_be32(some_page_addr)); - spi_ctrl_stop_user(); - - /* Check the page is erased */ - read_page(some_page_addr, page); - for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { - g_assert_cmphex(page[i], ==, 0xffffffff); - } - - flash_reset(); -} - -static void test_erase_all(void) -{ - uint32_t some_page_addr = 0x15000 * FLASH_PAGE_SIZE; - uint32_t page[FLASH_PAGE_SIZE / 4]; - int i; - - spi_conf(CONF_ENABLE_W0); - - /* - * Previous page should be full of 0xffs after backend is - * initialized - */ - read_page(some_page_addr - FLASH_PAGE_SIZE, page); - for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { - g_assert_cmphex(page[i], ==, 0xffffffff); - } - - spi_ctrl_start_user(); - writeb(ASPEED_FLASH_BASE, EN_4BYTE_ADDR); - writeb(ASPEED_FLASH_BASE, WREN); - writeb(ASPEED_FLASH_BASE, PP); - writel(ASPEED_FLASH_BASE, make_be32(some_page_addr)); - - /* Fill the page with its own addresses */ - for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { - writel(ASPEED_FLASH_BASE, make_be32(some_page_addr + i * 4)); - } - spi_ctrl_stop_user(); - - /* Check the page is correctly written */ - read_page(some_page_addr, page); - for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { - g_assert_cmphex(page[i], ==, some_page_addr + i * 4); - } - - spi_ctrl_start_user(); - writeb(ASPEED_FLASH_BASE, WREN); - writeb(ASPEED_FLASH_BASE, BULK_ERASE); - spi_ctrl_stop_user(); - - /* Check the page is erased */ - read_page(some_page_addr, page); - for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { - g_assert_cmphex(page[i], ==, 0xffffffff); - } - - flash_reset(); -} - -static void test_write_page(void) -{ - uint32_t my_page_addr = 0x14000 * FLASH_PAGE_SIZE; /* beyond 16MB */ - uint32_t some_page_addr = 0x15000 * FLASH_PAGE_SIZE; - uint32_t page[FLASH_PAGE_SIZE / 4]; - int i; - - spi_conf(CONF_ENABLE_W0); - - spi_ctrl_start_user(); - writeb(ASPEED_FLASH_BASE, EN_4BYTE_ADDR); - writeb(ASPEED_FLASH_BASE, WREN); - writeb(ASPEED_FLASH_BASE, PP); - writel(ASPEED_FLASH_BASE, make_be32(my_page_addr)); - - /* Fill the page with its own addresses */ - for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { - writel(ASPEED_FLASH_BASE, make_be32(my_page_addr + i * 4)); - } - spi_ctrl_stop_user(); - - /* Check what was written */ - read_page(my_page_addr, page); - for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { - g_assert_cmphex(page[i], ==, my_page_addr + i * 4); - } - - /* Check some other page. It should be full of 0xff */ - read_page(some_page_addr, page); - for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { - g_assert_cmphex(page[i], ==, 0xffffffff); - } - - flash_reset(); -} - -static void test_read_page_mem(void) -{ - uint32_t my_page_addr = 0x14000 * FLASH_PAGE_SIZE; /* beyond 16MB */ - uint32_t some_page_addr = 0x15000 * FLASH_PAGE_SIZE; - uint32_t page[FLASH_PAGE_SIZE / 4]; - int i; - - /* Enable 4BYTE mode for controller. This is should be strapped by - * HW for CE0 anyhow. - */ - spi_ce_ctrl(1 << CRTL_EXTENDED0); - - /* Enable 4BYTE mode for flash. */ - spi_conf(CONF_ENABLE_W0); - spi_ctrl_start_user(); - writeb(ASPEED_FLASH_BASE, EN_4BYTE_ADDR); - writeb(ASPEED_FLASH_BASE, WREN); - writeb(ASPEED_FLASH_BASE, PP); - writel(ASPEED_FLASH_BASE, make_be32(my_page_addr)); - - /* Fill the page with its own addresses */ - for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { - writel(ASPEED_FLASH_BASE, make_be32(my_page_addr + i * 4)); - } - spi_ctrl_stop_user(); - spi_conf_remove(CONF_ENABLE_W0); - - /* Check what was written */ - read_page_mem(my_page_addr, page); - for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { - g_assert_cmphex(page[i], ==, my_page_addr + i * 4); - } - - /* Check some other page. It should be full of 0xff */ - read_page_mem(some_page_addr, page); - for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { - g_assert_cmphex(page[i], ==, 0xffffffff); - } - - flash_reset(); -} - -static void test_write_page_mem(void) -{ - uint32_t my_page_addr = 0x15000 * FLASH_PAGE_SIZE; - uint32_t page[FLASH_PAGE_SIZE / 4]; - int i; - - /* Enable 4BYTE mode for controller. This is should be strapped by - * HW for CE0 anyhow. - */ - spi_ce_ctrl(1 << CRTL_EXTENDED0); - - /* Enable 4BYTE mode for flash. */ - spi_conf(CONF_ENABLE_W0); - spi_ctrl_start_user(); - writeb(ASPEED_FLASH_BASE, EN_4BYTE_ADDR); - writeb(ASPEED_FLASH_BASE, WREN); - spi_ctrl_stop_user(); - - /* move out USER mode to use direct writes to the AHB bus */ - spi_ctrl_setmode(CTRL_WRITEMODE, PP); - - for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { - writel(ASPEED_FLASH_BASE + my_page_addr + i * 4, - make_be32(my_page_addr + i * 4)); - } - - /* Check what was written */ - read_page_mem(my_page_addr, page); - for (i = 0; i < FLASH_PAGE_SIZE / 4; i++) { - g_assert_cmphex(page[i], ==, my_page_addr + i * 4); - } - - flash_reset(); -} - -static void test_read_status_reg(void) -{ - uint8_t r; - - spi_conf(CONF_ENABLE_W0); - - spi_ctrl_start_user(); - writeb(ASPEED_FLASH_BASE, RDSR); - r = readb(ASPEED_FLASH_BASE); - spi_ctrl_stop_user(); - - g_assert_cmphex(r & SR_WEL, ==, 0); - g_assert(!qtest_qom_get_bool - (global_qtest, "/machine/soc/fmc/ssi.0/child[0]", "write-enable")); - - spi_ctrl_start_user(); - writeb(ASPEED_FLASH_BASE, WREN); - writeb(ASPEED_FLASH_BASE, RDSR); - r = readb(ASPEED_FLASH_BASE); - spi_ctrl_stop_user(); - - g_assert_cmphex(r & SR_WEL, ==, SR_WEL); - g_assert(qtest_qom_get_bool - (global_qtest, "/machine/soc/fmc/ssi.0/child[0]", "write-enable")); - - spi_ctrl_start_user(); - writeb(ASPEED_FLASH_BASE, WRDI); - writeb(ASPEED_FLASH_BASE, RDSR); - r = readb(ASPEED_FLASH_BASE); - spi_ctrl_stop_user(); - - g_assert_cmphex(r & SR_WEL, ==, 0); - g_assert(!qtest_qom_get_bool - (global_qtest, "/machine/soc/fmc/ssi.0/child[0]", "write-enable")); + int ret; + int fd; - flash_reset(); -} + fd = g_file_open_tmp("qtest.m25p80.n25q256a.XXXXXX", &data->tmp_path, NULL); + g_assert(fd >= 0); + ret = ftruncate(fd, 32 * 1024 * 1024); + g_assert(ret == 0); + close(fd); -static void test_status_reg_write_protection(void) + data->s = qtest_initf("-m 256 -machine palmetto-bmc " + "-drive file=%s,format=raw,if=mtd", + data->tmp_path); + + /* fmc cs0 with n25q256a flash */ + data->flash_base = 0x20000000; + data->spi_base = 0x1E620000; + data->jedec_id = 0x20ba19; + data->cs = 0; + data->node = "/machine/soc/fmc/ssi.0/child[0]"; + /* beyond 16MB */ + data->page_addr = 0x14000 * FLASH_PAGE_SIZE; + + qtest_add_data_func("/ast2400/smc/read_jedec", + data, aspeed_smc_test_read_jedec); + qtest_add_data_func("/ast2400/smc/erase_sector", + data, aspeed_smc_test_erase_sector); + qtest_add_data_func("/ast2400/smc/erase_all", + data, aspeed_smc_test_erase_all); + qtest_add_data_func("/ast2400/smc/write_page", + data, aspeed_smc_test_write_page); + qtest_add_data_func("/ast2400/smc/read_page_mem", + data, aspeed_smc_test_read_page_mem); + qtest_add_data_func("/ast2400/smc/write_page_mem", + data, aspeed_smc_test_write_page_mem); + qtest_add_data_func("/ast2400/smc/read_status_reg", + data, aspeed_smc_test_read_status_reg); + qtest_add_data_func("/ast2400/smc/status_reg_write_protection", + data, aspeed_smc_test_status_reg_write_protection); + qtest_add_data_func("/ast2400/smc/write_block_protect", + data, aspeed_smc_test_write_block_protect); + qtest_add_data_func("/ast2400/smc/write_block_protect_bottom_bit", + data, aspeed_smc_test_write_block_protect_bottom_bit); +} + +static void test_ast2500_evb(AspeedSMCTestData *data) { - uint8_t r; - - spi_conf(CONF_ENABLE_W0); - - /* default case: WP# is high and SRWD is low -> status register writable */ - spi_ctrl_start_user(); - writeb(ASPEED_FLASH_BASE, WREN); - /* test ability to write SRWD */ - writeb(ASPEED_FLASH_BASE, WRSR); - writeb(ASPEED_FLASH_BASE, SRWD); - writeb(ASPEED_FLASH_BASE, RDSR); - r = readb(ASPEED_FLASH_BASE); - spi_ctrl_stop_user(); - g_assert_cmphex(r & SRWD, ==, SRWD); - - /* WP# high and SRWD high -> status register writable */ - spi_ctrl_start_user(); - writeb(ASPEED_FLASH_BASE, WREN); - /* test ability to write SRWD */ - writeb(ASPEED_FLASH_BASE, WRSR); - writeb(ASPEED_FLASH_BASE, 0); - writeb(ASPEED_FLASH_BASE, RDSR); - r = readb(ASPEED_FLASH_BASE); - spi_ctrl_stop_user(); - g_assert_cmphex(r & SRWD, ==, 0); - - /* WP# low and SRWD low -> status register writable */ - qtest_set_irq_in(global_qtest, - "/machine/soc/fmc/ssi.0/child[0]", "WP#", 0, 0); - spi_ctrl_start_user(); - writeb(ASPEED_FLASH_BASE, WREN); - /* test ability to write SRWD */ - writeb(ASPEED_FLASH_BASE, WRSR); - writeb(ASPEED_FLASH_BASE, SRWD); - writeb(ASPEED_FLASH_BASE, RDSR); - r = readb(ASPEED_FLASH_BASE); - spi_ctrl_stop_user(); - g_assert_cmphex(r & SRWD, ==, SRWD); - - /* WP# low and SRWD high -> status register NOT writable */ - spi_ctrl_start_user(); - writeb(ASPEED_FLASH_BASE, WREN); - /* test ability to write SRWD */ - writeb(ASPEED_FLASH_BASE, WRSR); - writeb(ASPEED_FLASH_BASE, 0); - writeb(ASPEED_FLASH_BASE, RDSR); - r = readb(ASPEED_FLASH_BASE); - spi_ctrl_stop_user(); - /* write is not successful */ - g_assert_cmphex(r & SRWD, ==, SRWD); + int ret; + int fd; - qtest_set_irq_in(global_qtest, - "/machine/soc/fmc/ssi.0/child[0]", "WP#", 0, 1); - flash_reset(); -} + fd = g_file_open_tmp("qtest.m25p80.mx25l25635e.XXXXXX", + &data->tmp_path, NULL); + g_assert(fd >= 0); + ret = ftruncate(fd, 32 * 1024 * 1024); + g_assert(ret == 0); + close(fd); -static void test_write_block_protect(void) + data->s = qtest_initf("-machine ast2500-evb " + "-drive file=%s,format=raw,if=mtd", + data->tmp_path); + + /* fmc cs0 with mx25l25635e flash */ + data->flash_base = 0x20000000; + data->spi_base = 0x1E620000; + data->jedec_id = 0xc22019; + data->cs = 0; + data->node = "/machine/soc/fmc/ssi.0/child[0]"; + /* beyond 16MB */ + data->page_addr = 0x14000 * FLASH_PAGE_SIZE; + + qtest_add_data_func("/ast2500/smc/read_jedec", + data, aspeed_smc_test_read_jedec); + qtest_add_data_func("/ast2500/smc/erase_sector", + data, aspeed_smc_test_erase_sector); + qtest_add_data_func("/ast2500/smc/erase_all", + data, aspeed_smc_test_erase_all); + qtest_add_data_func("/ast2500/smc/write_page", + data, aspeed_smc_test_write_page); + qtest_add_data_func("/ast2500/smc/read_page_mem", + data, aspeed_smc_test_read_page_mem); + qtest_add_data_func("/ast2500/smc/write_page_mem", + data, aspeed_smc_test_write_page_mem); + qtest_add_data_func("/ast2500/smc/read_status_reg", + data, aspeed_smc_test_read_status_reg); + qtest_add_data_func("/ast2500/smc/write_page_qpi", + data, aspeed_smc_test_write_page_qpi); +} + +static void test_ast2600_evb(AspeedSMCTestData *data) { - uint32_t sector_size = 65536; - uint32_t n_sectors = 512; - - spi_ce_ctrl(1 << CRTL_EXTENDED0); - spi_conf(CONF_ENABLE_W0); - - uint32_t bp_bits = 0b0; - - for (int i = 0; i < 16; i++) { - bp_bits = ((i & 0b1000) << 3) | ((i & 0b0111) << 2); - - spi_ctrl_start_user(); - writeb(ASPEED_FLASH_BASE, WREN); - writeb(ASPEED_FLASH_BASE, BULK_ERASE); - writeb(ASPEED_FLASH_BASE, WREN); - writeb(ASPEED_FLASH_BASE, WRSR); - writeb(ASPEED_FLASH_BASE, bp_bits); - writeb(ASPEED_FLASH_BASE, EN_4BYTE_ADDR); - writeb(ASPEED_FLASH_BASE, WREN); - spi_ctrl_stop_user(); - - uint32_t num_protected_sectors = i ? MIN(1 << (i - 1), n_sectors) : 0; - uint32_t protection_start = n_sectors - num_protected_sectors; - uint32_t protection_end = n_sectors; - - for (int sector = 0; sector < n_sectors; sector++) { - uint32_t addr = sector * sector_size; - - assert_page_mem(addr, 0xffffffff); - write_page_mem(addr, make_be32(0xabcdef12)); - - uint32_t expected_value = protection_start <= sector - && sector < protection_end - ? 0xffffffff : 0xabcdef12; - - assert_page_mem(addr, expected_value); - } - } + int ret; + int fd; - flash_reset(); -} + fd = g_file_open_tmp("qtest.m25p80.mx66u51235f.XXXXXX", + &data->tmp_path, NULL); + g_assert(fd >= 0); + ret = ftruncate(fd, 64 * 1024 * 1024); + g_assert(ret == 0); + close(fd); -static void test_write_block_protect_bottom_bit(void) + data->s = qtest_initf("-machine ast2600-evb " + "-drive file=%s,format=raw,if=mtd", + data->tmp_path); + + /* fmc cs0 with mx66u51235f flash */ + data->flash_base = 0x20000000; + data->spi_base = 0x1E620000; + data->jedec_id = 0xc2253a; + data->cs = 0; + data->node = "/machine/soc/fmc/ssi.0/child[0]"; + /* beyond 16MB */ + data->page_addr = 0x14000 * FLASH_PAGE_SIZE; + + qtest_add_data_func("/ast2600/smc/read_jedec", + data, aspeed_smc_test_read_jedec); + qtest_add_data_func("/ast2600/smc/erase_sector", + data, aspeed_smc_test_erase_sector); + qtest_add_data_func("/ast2600/smc/erase_all", + data, aspeed_smc_test_erase_all); + qtest_add_data_func("/ast2600/smc/write_page", + data, aspeed_smc_test_write_page); + qtest_add_data_func("/ast2600/smc/read_page_mem", + data, aspeed_smc_test_read_page_mem); + qtest_add_data_func("/ast2600/smc/write_page_mem", + data, aspeed_smc_test_write_page_mem); + qtest_add_data_func("/ast2600/smc/read_status_reg", + data, aspeed_smc_test_read_status_reg); + qtest_add_data_func("/ast2600/smc/write_page_qpi", + data, aspeed_smc_test_write_page_qpi); +} + +static void test_ast1030_evb(AspeedSMCTestData *data) { - uint32_t sector_size = 65536; - uint32_t n_sectors = 512; - - spi_ce_ctrl(1 << CRTL_EXTENDED0); - spi_conf(CONF_ENABLE_W0); - - /* top bottom bit is enabled */ - uint32_t bp_bits = 0b00100 << 3; - - for (int i = 0; i < 16; i++) { - bp_bits = (((i & 0b1000) | 0b0100) << 3) | ((i & 0b0111) << 2); - - spi_ctrl_start_user(); - writeb(ASPEED_FLASH_BASE, WREN); - writeb(ASPEED_FLASH_BASE, BULK_ERASE); - writeb(ASPEED_FLASH_BASE, WREN); - writeb(ASPEED_FLASH_BASE, WRSR); - writeb(ASPEED_FLASH_BASE, bp_bits); - writeb(ASPEED_FLASH_BASE, EN_4BYTE_ADDR); - writeb(ASPEED_FLASH_BASE, WREN); - spi_ctrl_stop_user(); - - uint32_t num_protected_sectors = i ? MIN(1 << (i - 1), n_sectors) : 0; - uint32_t protection_start = 0; - uint32_t protection_end = num_protected_sectors; - - for (int sector = 0; sector < n_sectors; sector++) { - uint32_t addr = sector * sector_size; - - assert_page_mem(addr, 0xffffffff); - write_page_mem(addr, make_be32(0xabcdef12)); - - uint32_t expected_value = protection_start <= sector - && sector < protection_end - ? 0xffffffff : 0xabcdef12; + int ret; + int fd; - assert_page_mem(addr, expected_value); - } - } + fd = g_file_open_tmp("qtest.m25p80.w25q80bl.XXXXXX", + &data->tmp_path, NULL); + g_assert(fd >= 0); + ret = ftruncate(fd, 1 * 1024 * 1024); + g_assert(ret == 0); + close(fd); - flash_reset(); + data->s = qtest_initf("-machine ast1030-evb " + "-drive file=%s,format=raw,if=mtd", + data->tmp_path); + + /* fmc cs0 with w25q80bl flash */ + data->flash_base = 0x80000000; + data->spi_base = 0x7E620000; + data->jedec_id = 0xef4014; + data->cs = 0; + data->node = "/machine/soc/fmc/ssi.0/child[0]"; + /* beyond 512KB */ + data->page_addr = 0x800 * FLASH_PAGE_SIZE; + + qtest_add_data_func("/ast1030/smc/read_jedec", + data, aspeed_smc_test_read_jedec); + qtest_add_data_func("/ast1030/smc/erase_sector", + data, aspeed_smc_test_erase_sector); + qtest_add_data_func("/ast1030/smc/erase_all", + data, aspeed_smc_test_erase_all); + qtest_add_data_func("/ast1030/smc/write_page", + data, aspeed_smc_test_write_page); + qtest_add_data_func("/ast1030/smc/read_page_mem", + data, aspeed_smc_test_read_page_mem); + qtest_add_data_func("/ast1030/smc/write_page_mem", + data, aspeed_smc_test_write_page_mem); + qtest_add_data_func("/ast1030/smc/read_status_reg", + data, aspeed_smc_test_read_status_reg); + qtest_add_data_func("/ast1030/smc/write_page_qpi", + data, aspeed_smc_test_write_page_qpi); } int main(int argc, char **argv) { - g_autofree char *tmp_path = NULL; + AspeedSMCTestData palmetto_data; + AspeedSMCTestData ast2500_evb_data; + AspeedSMCTestData ast2600_evb_data; + AspeedSMCTestData ast1030_evb_data; int ret; - int fd; g_test_init(&argc, &argv, NULL); - fd = g_file_open_tmp("qtest.m25p80.XXXXXX", &tmp_path, NULL); - g_assert(fd >= 0); - ret = ftruncate(fd, FLASH_SIZE); - g_assert(ret == 0); - close(fd); - - global_qtest = qtest_initf("-m 256 -machine palmetto-bmc " - "-drive file=%s,format=raw,if=mtd", - tmp_path); - - qtest_add_func("/ast2400/smc/read_jedec", test_read_jedec); - qtest_add_func("/ast2400/smc/erase_sector", test_erase_sector); - qtest_add_func("/ast2400/smc/erase_all", test_erase_all); - qtest_add_func("/ast2400/smc/write_page", test_write_page); - qtest_add_func("/ast2400/smc/read_page_mem", test_read_page_mem); - qtest_add_func("/ast2400/smc/write_page_mem", test_write_page_mem); - qtest_add_func("/ast2400/smc/read_status_reg", test_read_status_reg); - qtest_add_func("/ast2400/smc/status_reg_write_protection", - test_status_reg_write_protection); - qtest_add_func("/ast2400/smc/write_block_protect", - test_write_block_protect); - qtest_add_func("/ast2400/smc/write_block_protect_bottom_bit", - test_write_block_protect_bottom_bit); - - flash_reset(); + test_palmetto_bmc(&palmetto_data); + test_ast2500_evb(&ast2500_evb_data); + test_ast2600_evb(&ast2600_evb_data); + test_ast1030_evb(&ast1030_evb_data); ret = g_test_run(); - qtest_quit(global_qtest); - unlink(tmp_path); + qtest_quit(palmetto_data.s); + qtest_quit(ast2500_evb_data.s); + qtest_quit(ast2600_evb_data.s); + qtest_quit(ast1030_evb_data.s); + unlink(palmetto_data.tmp_path); + unlink(ast2500_evb_data.tmp_path); + unlink(ast2600_evb_data.tmp_path); + unlink(ast1030_evb_data.tmp_path); + g_free(palmetto_data.tmp_path); + g_free(ast2500_evb_data.tmp_path); + g_free(ast2600_evb_data.tmp_path); + g_free(ast1030_evb_data.tmp_path); + return ret; } diff --git a/tests/qtest/ast2700-gpio-test.c b/tests/qtest/ast2700-gpio-test.c new file mode 100644 index 0000000..eeae9bf --- /dev/null +++ b/tests/qtest/ast2700-gpio-test.c @@ -0,0 +1,95 @@ +/* + * QTest testcase for the ASPEED AST2700 GPIO Controller. + * + * SPDX-License-Identifier: GPL-2.0-or-later + * Copyright (C) 2024 ASPEED Technology Inc. + */ + +#include "qemu/osdep.h" +#include "qemu/bitops.h" +#include "qemu/timer.h" +#include "qobject/qdict.h" +#include "libqtest-single.h" + +#define AST2700_GPIO_BASE 0x14C0B000 +#define GPIOA0_CONTROL 0x180 + +static void test_output_pins(const char *machine, const uint32_t base) +{ + QTestState *s = qtest_init(machine); + uint32_t offset = 0; + uint32_t value = 0; + uint32_t pin = 0; + + for (char c = 'A'; c <= 'D'; c++) { + for (int i = 0; i < 8; i++) { + offset = base + (pin * 4); + + /* output direction and output hi */ + qtest_writel(s, offset, 0x00000003); + value = qtest_readl(s, offset); + g_assert_cmphex(value, ==, 0x00000003); + + /* output direction and output low */ + qtest_writel(s, offset, 0x00000002); + value = qtest_readl(s, offset); + g_assert_cmphex(value, ==, 0x00000002); + pin++; + } + } + + qtest_quit(s); +} + +static void test_input_pins(const char *machine, const uint32_t base) +{ + QTestState *s = qtest_init(machine); + char name[16]; + uint32_t offset = 0; + uint32_t value = 0; + uint32_t pin = 0; + + for (char c = 'A'; c <= 'D'; c++) { + for (int i = 0; i < 8; i++) { + sprintf(name, "gpio%c%d", c, i); + offset = base + (pin * 4); + /* input direction */ + qtest_writel(s, offset, 0); + + /* set input */ + qtest_qom_set_bool(s, "/machine/soc/gpio", name, true); + value = qtest_readl(s, offset); + g_assert_cmphex(value, ==, 0x00002000); + + /* clear input */ + qtest_qom_set_bool(s, "/machine/soc/gpio", name, false); + value = qtest_readl(s, offset); + g_assert_cmphex(value, ==, 0); + pin++; + } + } + + qtest_quit(s); +} + +static void test_2700_input_pins(void) +{ + test_input_pins("-machine ast2700-evb", + AST2700_GPIO_BASE + GPIOA0_CONTROL); +} + +static void test_2700_output_pins(void) +{ + test_output_pins("-machine ast2700-evb", + AST2700_GPIO_BASE + GPIOA0_CONTROL); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + qtest_add_func("/ast2700/gpio/input_pins", test_2700_input_pins); + qtest_add_func("/ast2700/gpio/output_pins", test_2700_output_pins); + + return g_test_run(); +} diff --git a/tests/qtest/ast2700-hace-test.c b/tests/qtest/ast2700-hace-test.c new file mode 100644 index 0000000..a400e29 --- /dev/null +++ b/tests/qtest/ast2700-hace-test.c @@ -0,0 +1,98 @@ +/* + * QTest testcase for the ASPEED Hash and Crypto Engine + * + * SPDX-License-Identifier: GPL-2.0-or-later + * Copyright (C) 2025 ASPEED Technology Inc. + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "qemu/bitops.h" +#include "aspeed-hace-utils.h" + +static const struct AspeedMasks as2700_masks = { + .src = 0x7fffffff, + .dest = 0x7ffffff8, + .key = 0x7ffffff8, + .len = 0x0fffffff, + .src_hi = 0x00000003, + .dest_hi = 0x00000003, + .key_hi = 0x00000003, +}; + +/* ast2700 */ +static void test_md5_ast2700(void) +{ + aspeed_test_md5("-machine ast2700a1-evb", 0x12070000, 0x400000000); +} + +static void test_sha256_ast2700(void) +{ + aspeed_test_sha256("-machine ast2700a1-evb", 0x12070000, 0x400000000); +} + +static void test_sha256_sg_ast2700(void) +{ + aspeed_test_sha256_sg("-machine ast2700a1-evb", 0x12070000, 0x400000000); +} + +static void test_sha384_ast2700(void) +{ + aspeed_test_sha384("-machine ast2700a1-evb", 0x12070000, 0x400000000); +} + +static void test_sha384_sg_ast2700(void) +{ + aspeed_test_sha384_sg("-machine ast2700a1-evb", 0x12070000, 0x400000000); +} + +static void test_sha512_ast2700(void) +{ + aspeed_test_sha512("-machine ast2700a1-evb", 0x12070000, 0x400000000); +} + +static void test_sha512_sg_ast2700(void) +{ + aspeed_test_sha512_sg("-machine ast2700a1-evb", 0x12070000, 0x400000000); +} + +static void test_sha256_accum_ast2700(void) +{ + aspeed_test_sha256_accum("-machine ast2700a1-evb", 0x12070000, 0x400000000); +} + +static void test_sha384_accum_ast2700(void) +{ + aspeed_test_sha384_accum("-machine ast2700a1-evb", 0x12070000, 0x400000000); +} + +static void test_sha512_accum_ast2700(void) +{ + aspeed_test_sha512_accum("-machine ast2700a1-evb", 0x12070000, 0x400000000); +} + +static void test_addresses_ast2700(void) +{ + aspeed_test_addresses("-machine ast2700a1-evb", 0x12070000, &as2700_masks); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + qtest_add_func("ast2700/hace/addresses", test_addresses_ast2700); + qtest_add_func("ast2700/hace/sha512", test_sha512_ast2700); + qtest_add_func("ast2700/hace/sha384", test_sha384_ast2700); + qtest_add_func("ast2700/hace/sha256", test_sha256_ast2700); + qtest_add_func("ast2700/hace/md5", test_md5_ast2700); + + qtest_add_func("ast2700/hace/sha512_sg", test_sha512_sg_ast2700); + qtest_add_func("ast2700/hace/sha384_sg", test_sha384_sg_ast2700); + qtest_add_func("ast2700/hace/sha256_sg", test_sha256_sg_ast2700); + + qtest_add_func("ast2700/hace/sha512_accum", test_sha512_accum_ast2700); + qtest_add_func("ast2700/hace/sha384_accum", test_sha384_accum_ast2700); + qtest_add_func("ast2700/hace/sha256_accum", test_sha256_accum_ast2700); + + return g_test_run(); +} diff --git a/tests/qtest/ast2700-smc-test.c b/tests/qtest/ast2700-smc-test.c new file mode 100644 index 0000000..62d538d --- /dev/null +++ b/tests/qtest/ast2700-smc-test.c @@ -0,0 +1,72 @@ +/* + * QTest testcase for the M25P80 Flash using the ASPEED SPI Controller since + * AST2700. + * + * SPDX-License-Identifier: GPL-2.0-or-later + * Copyright (C) 2024 ASPEED Technology Inc. + */ + +#include "qemu/osdep.h" +#include "qemu/bswap.h" +#include "libqtest-single.h" +#include "qemu/bitops.h" +#include "aspeed-smc-utils.h" + +static void test_ast2700_evb(AspeedSMCTestData *data) +{ + int ret; + int fd; + + fd = g_file_open_tmp("qtest.m25p80.w25q01jvq.XXXXXX", + &data->tmp_path, NULL); + g_assert(fd >= 0); + ret = ftruncate(fd, 128 * 1024 * 1024); + g_assert(ret == 0); + close(fd); + + data->s = qtest_initf("-machine ast2700-evb " + "-drive file=%s,format=raw,if=mtd", + data->tmp_path); + + /* fmc cs0 with w25q01jvq flash */ + data->flash_base = 0x100000000; + data->spi_base = 0x14000000; + data->jedec_id = 0xef4021; + data->cs = 0; + data->node = "/machine/soc/fmc/ssi.0/child[0]"; + /* beyond 64MB */ + data->page_addr = 0x40000 * FLASH_PAGE_SIZE; + + qtest_add_data_func("/ast2700/smc/read_jedec", + data, aspeed_smc_test_read_jedec); + qtest_add_data_func("/ast2700/smc/erase_sector", + data, aspeed_smc_test_erase_sector); + qtest_add_data_func("/ast2700/smc/erase_all", + data, aspeed_smc_test_erase_all); + qtest_add_data_func("/ast2700/smc/write_page", + data, aspeed_smc_test_write_page); + qtest_add_data_func("/ast2700/smc/read_page_mem", + data, aspeed_smc_test_read_page_mem); + qtest_add_data_func("/ast2700/smc/write_page_mem", + data, aspeed_smc_test_write_page_mem); + qtest_add_data_func("/ast2700/smc/read_status_reg", + data, aspeed_smc_test_read_status_reg); + qtest_add_data_func("/ast2700/smc/write_page_qpi", + data, aspeed_smc_test_write_page_qpi); +} + +int main(int argc, char **argv) +{ + AspeedSMCTestData ast2700_evb_data; + int ret; + + g_test_init(&argc, &argv, NULL); + + test_ast2700_evb(&ast2700_evb_data); + ret = g_test_run(); + + qtest_quit(ast2700_evb_data.s); + unlink(ast2700_evb_data.tmp_path); + g_free(ast2700_evb_data.tmp_path); + return ret; +} diff --git a/tests/qtest/bcm2835-i2c-test.c b/tests/qtest/bcm2835-i2c-test.c index 513ecce..1599194 100644 --- a/tests/qtest/bcm2835-i2c-test.c +++ b/tests/qtest/bcm2835-i2c-test.c @@ -81,7 +81,7 @@ static void test_i2c_read_write(gconstpointer data) g_assert_cmpint(i2cdata, ==, 0xde); i2cdata = readl(base_addr + BCM2835_I2C_FIFO); - g_assert_cmpint(i2cdata, ==, 0xad); + g_assert_cmpint(i2cdata, ==, 0xa0); /* Clear flags */ writel(base_addr + BCM2835_I2C_S, BCM2835_I2C_S_DONE | BCM2835_I2C_S_ERR | diff --git a/tests/qtest/bios-tables-test.c b/tests/qtest/bios-tables-test.c index 36e5c0a..4dbc07e 100644 --- a/tests/qtest/bios-tables-test.c +++ b/tests/qtest/bios-tables-test.c @@ -292,6 +292,7 @@ static void dump_aml_files(test_data *data, bool rebuild) g_free(aml_file); } + free_test_data(&exp_data); } static bool create_tmp_asl(AcpiSdtTable *sdt) @@ -959,7 +960,7 @@ static void test_acpi_piix4_tcg_bridge(void) free_test_data(&data); /* check that reboot/reset doesn't change any ACPI tables */ - qtest_qmp_send(data.qts, "{'execute':'system_reset' }"); + qtest_system_reset(data.qts); process_acpi_tables(&data); free_test_data(&data); } @@ -1216,7 +1217,7 @@ static void test_acpi_q35_multif_bridge(void) free_test_data(&data); /* check that reboot/reset doesn't change any ACPI tables */ - qtest_qmp_send(data.qts, "{'execute':'system_reset' }"); + qtest_system_reset(data.qts); process_acpi_tables(&data); free_test_data(&data); } @@ -1621,7 +1622,7 @@ static void test_acpi_aarch64_virt_tcg_memhp(void) .uefi_fl2 = "pc-bios/edk2-arm-vars.fd", .cd = "tests/data/uefi-boot-images/bios-tables-test.aarch64.iso.qcow2", .ram_start = 0x40000000ULL, - .scan_len = 256ULL * 1024 * 1024, + .scan_len = 256ULL * MiB, }; data.variant = ".memhp"; @@ -1706,6 +1707,32 @@ static void test_acpi_microvm_ioapic2_tcg(void) free_test_data(&data); } +static void test_acpi_riscv64_virt_tcg_numamem(void) +{ + test_data data = { + .machine = "virt", + .arch = "riscv64", + .tcg_only = true, + .uefi_fl1 = "pc-bios/edk2-riscv-code.fd", + .uefi_fl2 = "pc-bios/edk2-riscv-vars.fd", + .cd = "tests/data/uefi-boot-images/bios-tables-test.riscv64.iso.qcow2", + .ram_start = 0x80000000ULL, + .scan_len = 128ULL * MiB, + }; + + data.variant = ".numamem"; + /* + * RHCT will have ISA string encoded. To reduce the effort + * of updating expected AML file for any new default ISA extension, + * use the profile rva22s64. + */ + test_acpi_one(" -cpu rva22s64" + " -object memory-backend-ram,id=ram0,size=128M" + " -numa node,memdev=ram0", + &data); + free_test_data(&data); +} + static void test_acpi_aarch64_virt_tcg_numamem(void) { test_data data = { @@ -1716,7 +1743,7 @@ static void test_acpi_aarch64_virt_tcg_numamem(void) .uefi_fl2 = "pc-bios/edk2-arm-vars.fd", .cd = "tests/data/uefi-boot-images/bios-tables-test.aarch64.iso.qcow2", .ram_start = 0x40000000ULL, - .scan_len = 128ULL * 1024 * 1024, + .scan_len = 128ULL * MiB, }; data.variant = ".numamem"; @@ -1738,7 +1765,7 @@ static void test_acpi_aarch64_virt_tcg_pxb(void) .uefi_fl1 = "pc-bios/edk2-aarch64-code.fd", .uefi_fl2 = "pc-bios/edk2-arm-vars.fd", .ram_start = 0x40000000ULL, - .scan_len = 128ULL * 1024 * 1024, + .scan_len = 128ULL * MiB, }; /* * While using -cdrom, the cdrom would auto plugged into pxb-pcie, @@ -1814,7 +1841,7 @@ static void test_acpi_aarch64_virt_tcg_acpi_hmat(void) .uefi_fl2 = "pc-bios/edk2-arm-vars.fd", .cd = "tests/data/uefi-boot-images/bios-tables-test.aarch64.iso.qcow2", .ram_start = 0x40000000ULL, - .scan_len = 128ULL * 1024 * 1024, + .scan_len = 128ULL * MiB, }; data.variant = ".acpihmatvirt"; @@ -1910,6 +1937,101 @@ static void test_acpi_q35_tcg_acpi_hmat_noinitiator(void) free_test_data(&data); } +/* Test intended to hit corner cases of SRAT and HMAT */ +static void test_acpi_q35_tcg_acpi_hmat_generic_x(void) +{ + test_data data = {}; + + data.machine = MACHINE_Q35; + data.arch = "x86"; + data.variant = ".acpihmat-generic-x"; + test_acpi_one(" -machine hmat=on,cxl=on" + " -smp 3,sockets=3" + " -m 128M,maxmem=384M,slots=2" + " -device pcie-root-port,chassis=1,id=pci.1" + " -device pci-testdev,bus=pci.1," + "multifunction=on,addr=00.0" + " -device pci-testdev,bus=pci.1,addr=00.1" + " -device pci-testdev,bus=pci.1,id=gidev,addr=00.2" + " -device pxb-cxl,bus_nr=64,bus=pcie.0,id=cxl.1" + " -object memory-backend-ram,size=64M,id=ram0" + " -object memory-backend-ram,size=64M,id=ram1" + " -numa node,nodeid=0,cpus=0,memdev=ram0" + " -numa node,nodeid=1" + " -object acpi-generic-initiator,id=gi0,pci-dev=gidev,node=1" + " -numa node,nodeid=2" + " -object acpi-generic-port,id=gp0,pci-bus=cxl.1,node=2" + " -numa node,nodeid=3,cpus=1" + " -numa node,nodeid=4,memdev=ram1" + " -numa node,nodeid=5,cpus=2" + " -numa hmat-lb,initiator=0,target=0,hierarchy=memory," + "data-type=access-latency,latency=10" + " -numa hmat-lb,initiator=0,target=0,hierarchy=memory," + "data-type=access-bandwidth,bandwidth=800M" + " -numa hmat-lb,initiator=0,target=2,hierarchy=memory," + "data-type=access-latency,latency=100" + " -numa hmat-lb,initiator=0,target=2,hierarchy=memory," + "data-type=access-bandwidth,bandwidth=200M" + " -numa hmat-lb,initiator=0,target=4,hierarchy=memory," + "data-type=access-latency,latency=100" + " -numa hmat-lb,initiator=0,target=4,hierarchy=memory," + "data-type=access-bandwidth,bandwidth=200M" + " -numa hmat-lb,initiator=0,target=5,hierarchy=memory," + "data-type=access-latency,latency=200" + " -numa hmat-lb,initiator=0,target=5,hierarchy=memory," + "data-type=access-bandwidth,bandwidth=400M" + " -numa hmat-lb,initiator=1,target=0,hierarchy=memory," + "data-type=access-latency,latency=500" + " -numa hmat-lb,initiator=1,target=0,hierarchy=memory," + "data-type=access-bandwidth,bandwidth=100M" + " -numa hmat-lb,initiator=1,target=2,hierarchy=memory," + "data-type=access-latency,latency=50" + " -numa hmat-lb,initiator=1,target=2,hierarchy=memory," + "data-type=access-bandwidth,bandwidth=400M" + " -numa hmat-lb,initiator=1,target=4,hierarchy=memory," + "data-type=access-latency,latency=50" + " -numa hmat-lb,initiator=1,target=4,hierarchy=memory," + "data-type=access-bandwidth,bandwidth=800M" + " -numa hmat-lb,initiator=1,target=5,hierarchy=memory," + "data-type=access-latency,latency=500" + " -numa hmat-lb,initiator=1,target=5,hierarchy=memory," + "data-type=access-bandwidth,bandwidth=100M" + " -numa hmat-lb,initiator=3,target=0,hierarchy=memory," + "data-type=access-latency,latency=20" + " -numa hmat-lb,initiator=3,target=0,hierarchy=memory," + "data-type=access-bandwidth,bandwidth=400M" + " -numa hmat-lb,initiator=3,target=2,hierarchy=memory," + "data-type=access-latency,latency=80" + " -numa hmat-lb,initiator=3,target=2,hierarchy=memory," + "data-type=access-bandwidth,bandwidth=200M" + " -numa hmat-lb,initiator=3,target=4,hierarchy=memory," + "data-type=access-latency,latency=80" + " -numa hmat-lb,initiator=3,target=4,hierarchy=memory," + "data-type=access-bandwidth,bandwidth=200M" + " -numa hmat-lb,initiator=3,target=5,hierarchy=memory," + "data-type=access-latency,latency=20" + " -numa hmat-lb,initiator=3,target=5,hierarchy=memory," + "data-type=access-bandwidth,bandwidth=400M" + " -numa hmat-lb,initiator=5,target=0,hierarchy=memory," + "data-type=access-latency,latency=20" + " -numa hmat-lb,initiator=5,target=0,hierarchy=memory," + "data-type=access-bandwidth,bandwidth=400M" + " -numa hmat-lb,initiator=5,target=2,hierarchy=memory," + "data-type=access-latency,latency=80" + " -numa hmat-lb,initiator=5,target=4,hierarchy=memory," + "data-type=access-bandwidth,bandwidth=200M" + " -numa hmat-lb,initiator=5,target=4,hierarchy=memory," + "data-type=access-latency,latency=80" + " -numa hmat-lb,initiator=5,target=2,hierarchy=memory," + "data-type=access-bandwidth,bandwidth=200M" + " -numa hmat-lb,initiator=5,target=5,hierarchy=memory," + "data-type=access-latency,latency=10" + " -numa hmat-lb,initiator=5,target=5,hierarchy=memory," + "data-type=access-bandwidth,bandwidth=800M", + &data); + free_test_data(&data); +} + #ifdef CONFIG_POSIX static void test_acpi_erst(const char *machine, const char *arch) { @@ -1973,7 +2095,7 @@ static void test_acpi_riscv64_virt_tcg(void) .uefi_fl2 = "pc-bios/edk2-riscv-vars.fd", .cd = "tests/data/uefi-boot-images/bios-tables-test.riscv64.iso.qcow2", .ram_start = 0x80000000ULL, - .scan_len = 128ULL * 1024 * 1024, + .scan_len = 128ULL * MiB, }; /* @@ -1995,7 +2117,7 @@ static void test_acpi_aarch64_virt_tcg(void) .uefi_fl2 = "pc-bios/edk2-arm-vars.fd", .cd = "tests/data/uefi-boot-images/bios-tables-test.aarch64.iso.qcow2", .ram_start = 0x40000000ULL, - .scan_len = 128ULL * 1024 * 1024, + .scan_len = 128ULL * MiB, }; data.smbios_cpu_max_speed = 2900; @@ -2016,7 +2138,7 @@ static void test_acpi_aarch64_virt_tcg_topology(void) .uefi_fl2 = "pc-bios/edk2-arm-vars.fd", .cd = "tests/data/uefi-boot-images/bios-tables-test.aarch64.iso.qcow2", .ram_start = 0x40000000ULL, - .scan_len = 128ULL * 1024 * 1024, + .scan_len = 128ULL * MiB, }; test_acpi_one("-cpu cortex-a57 " @@ -2024,6 +2146,25 @@ static void test_acpi_aarch64_virt_tcg_topology(void) free_test_data(&data); } +static void test_acpi_aarch64_virt_tcg_its_off(void) +{ + test_data data = { + .machine = "virt", + .arch = "aarch64", + .variant = ".its_off", + .tcg_only = true, + .uefi_fl1 = "pc-bios/edk2-aarch64-code.fd", + .uefi_fl2 = "pc-bios/edk2-arm-vars.fd", + .cd = "tests/data/uefi-boot-images/bios-tables-test.aarch64.iso.qcow2", + .ram_start = 0x40000000ULL, + .scan_len = 128ULL * 1024 * 1024, + }; + + test_acpi_one("-cpu cortex-a57 " + "-M gic-version=3,iommu=smmuv3,its=off", &data); + free_test_data(&data); +} + static void test_acpi_q35_viot(void) { test_data data = { @@ -2101,7 +2242,7 @@ static void test_acpi_aarch64_virt_viot(void) .uefi_fl2 = "pc-bios/edk2-arm-vars.fd", .cd = "tests/data/uefi-boot-images/bios-tables-test.aarch64.iso.qcow2", .ram_start = 0x40000000ULL, - .scan_len = 128ULL * 1024 * 1024, + .scan_len = 128ULL * MiB, }; test_acpi_one("-cpu cortex-a57 " @@ -2285,7 +2426,7 @@ static void test_acpi_aarch64_virt_oem_fields(void) .uefi_fl2 = "pc-bios/edk2-arm-vars.fd", .cd = "tests/data/uefi-boot-images/bios-tables-test.aarch64.iso.qcow2", .ram_start = 0x40000000ULL, - .scan_len = 128ULL * 1024 * 1024, + .scan_len = 128ULL * MiB, }; char *args; @@ -2388,6 +2529,8 @@ int main(int argc, char *argv[]) qtest_add_func("acpi/q35/nohpet", test_acpi_q35_tcg_nohpet); qtest_add_func("acpi/q35/acpihmat-noinitiator", test_acpi_q35_tcg_acpi_hmat_noinitiator); + qtest_add_func("acpi/q35/acpihmat-genericx", + test_acpi_q35_tcg_acpi_hmat_generic_x); /* i386 does not support memory hotplug */ if (strcmp(arch, "i386")) { @@ -2453,6 +2596,8 @@ int main(int argc, char *argv[]) test_acpi_aarch64_virt_tcg_acpi_hmat); qtest_add_func("acpi/virt/topology", test_acpi_aarch64_virt_tcg_topology); + qtest_add_func("acpi/virt/its_off", + test_acpi_aarch64_virt_tcg_its_off); qtest_add_func("acpi/virt/numamem", test_acpi_aarch64_virt_tcg_numamem); qtest_add_func("acpi/virt/memhp", test_acpi_aarch64_virt_tcg_memhp); @@ -2466,6 +2611,8 @@ int main(int argc, char *argv[]) } else if (strcmp(arch, "riscv64") == 0) { if (has_tcg && qtest_has_device("virtio-blk-pci")) { qtest_add_func("acpi/virt", test_acpi_riscv64_virt_tcg); + qtest_add_func("acpi/virt/numamem", + test_acpi_riscv64_virt_tcg_numamem); } } ret = g_test_run(); diff --git a/tests/qtest/boot-order-test.c b/tests/qtest/boot-order-test.c index 8f2b6ef..74d6b82 100644 --- a/tests/qtest/boot-order-test.c +++ b/tests/qtest/boot-order-test.c @@ -13,7 +13,7 @@ #include "qemu/osdep.h" #include "libqos/fw_cfg.h" #include "libqtest.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "standard-headers/linux/qemu_fw_cfg.h" typedef struct { @@ -31,7 +31,7 @@ static void test_a_boot_order(const char *machine, uint64_t actual; QTestState *qts; - if (machine && !qtest_has_machine(machine)) { + if (!qtest_has_machine(machine)) { g_test_skip("Machine is not available"); return; } @@ -40,12 +40,7 @@ static void test_a_boot_order(const char *machine, machine ?: "", test_args); actual = read_boot_order(qts); g_assert_cmphex(actual, ==, expected_boot); - qtest_qmp_assert_success(qts, "{ 'execute': 'system_reset' }"); - /* - * system_reset only requests reset. We get a RESET event after - * the actual reset completes. Need to wait for that. - */ - qtest_qmp_eventwait(qts, "RESET"); + qtest_system_reset(qts); actual = read_boot_order(qts); g_assert_cmphex(actual, ==, expected_reboot); qtest_quit(qts); @@ -107,7 +102,7 @@ static const boot_order_test test_cases_pc[] = { static void test_pc_boot_order(void) { - test_boot_orders(NULL, read_boot_order_pc, test_cases_pc); + test_boot_orders("pc", read_boot_order_pc, test_cases_pc); } static uint64_t read_boot_order_pmac(QTestState *qts) diff --git a/tests/qtest/boot-serial-test.c b/tests/qtest/boot-serial-test.c index 3b92fa5..a05d26e 100644 --- a/tests/qtest/boot-serial-test.c +++ b/tests/qtest/boot-serial-test.c @@ -70,18 +70,23 @@ static const uint8_t kernel_plml605[] = { }; static const uint8_t bios_raspi2[] = { - 0x08, 0x30, 0x9f, 0xe5, /* ldr r3,[pc,#8] Get base */ - 0x54, 0x20, 0xa0, 0xe3, /* mov r2,#'T' */ - 0x00, 0x20, 0xc3, 0xe5, /* strb r2,[r3] */ - 0xfb, 0xff, 0xff, 0xea, /* b loop */ - 0x00, 0x10, 0x20, 0x3f, /* 0x3f201000 = UART0 base addr */ + 0x10, 0x30, 0x9f, 0xe5, /* ldr r3, [pc, #16] Get &UART0 */ + 0x10, 0x20, 0x9f, 0xe5, /* ldr r2, [pc, #16] Get &CR */ + 0xb0, 0x23, 0xc3, 0xe1, /* strh r2, [r3, #48] Set CR */ + 0x54, 0x20, 0xa0, 0xe3, /* mov r2, #'T' */ + 0x00, 0x20, 0xc3, 0xe5, /* loop: strb r2, [r3] *TXDAT = 'T' */ + 0xff, 0xff, 0xff, 0xea, /* b -4 (loop) */ + 0x00, 0x10, 0x20, 0x3f, /* UART0: 0x3f201000 */ + 0x01, 0x01, 0x00, 0x00, /* CR: 0x101 = UARTEN|TXE */ }; static const uint8_t kernel_aarch64[] = { - 0x81, 0x0a, 0x80, 0x52, /* mov w1, #0x54 */ - 0x02, 0x20, 0xa1, 0xd2, /* mov x2, #0x9000000 */ - 0x41, 0x00, 0x00, 0x39, /* strb w1, [x2] */ - 0xfd, 0xff, 0xff, 0x17, /* b -12 (loop) */ + 0x02, 0x20, 0xa1, 0xd2, /* mov x2, #0x9000000 Load UART0 */ + 0x21, 0x20, 0x80, 0x52, /* mov w1, 0x101 CR = UARTEN|TXE */ + 0x41, 0x60, 0x00, 0x79, /* strh w1, [x2, #48] Set CR */ + 0x81, 0x0a, 0x80, 0x52, /* mov w1, #'T' */ + 0x41, 0x00, 0x00, 0x39, /* loop: strb w1, [x2] *TXDAT = 'T' */ + 0xff, 0xff, 0xff, 0x17, /* b -4 (loop) */ }; static const uint8_t kernel_nrf51[] = { @@ -184,8 +189,6 @@ static const testdef_t tests[] = { { "microblazeel", "petalogix-ml605", "", "TT", sizeof(kernel_plml605), kernel_plml605 }, { "arm", "raspi2b", "", "TT", sizeof(bios_raspi2), 0, bios_raspi2 }, - /* For hppa, force bios to output to serial by disabling graphics. */ - { "hppa", "hppa", "-vga none", "SeaBIOS wants SYSTEM HALT" }, { "aarch64", "virt", "-cpu max", "TT", sizeof(kernel_aarch64), kernel_aarch64 }, { "arm", "microbit", "", "T", sizeof(kernel_nrf51), kernel_nrf51 }, diff --git a/tests/qtest/cdrom-test.c b/tests/qtest/cdrom-test.c index 5d89e62..56e2d28 100644 --- a/tests/qtest/cdrom-test.c +++ b/tests/qtest/cdrom-test.c @@ -13,7 +13,7 @@ #include "qemu/osdep.h" #include "libqtest.h" #include "boot-sector.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" static char isoimage[] = "cdrom-boot-iso-XXXXXX"; @@ -135,13 +135,35 @@ static void add_x86_tests(void) return; } - qtest_add_data_func("cdrom/boot/default", "-cdrom ", test_cdboot); - if (qtest_has_device("virtio-scsi-ccw")) { - qtest_add_data_func("cdrom/boot/virtio-scsi", - "-device virtio-scsi -device scsi-cd,drive=cdr " - "-blockdev file,node-name=cdr,filename=", - test_cdboot); + if (qtest_has_machine("pc")) { + qtest_add_data_func("cdrom/boot/default", "-cdrom ", test_cdboot); + if (qtest_has_device("virtio-scsi-ccw")) { + qtest_add_data_func("cdrom/boot/virtio-scsi", + "-device virtio-scsi -device scsi-cd,drive=cdr " + "-blockdev file,node-name=cdr,filename=", + test_cdboot); + } + + if (qtest_has_device("am53c974")) { + qtest_add_data_func("cdrom/boot/am53c974", + "-device am53c974 -device scsi-cd,drive=cd1 " + "-drive if=none,id=cd1,format=raw,file=", + test_cdboot); + } + if (qtest_has_device("dc390")) { + qtest_add_data_func("cdrom/boot/dc390", + "-device dc390 -device scsi-cd,drive=cd1 " + "-blockdev file,node-name=cd1,filename=", + test_cdboot); + } + if (qtest_has_device("lsi53c895a")) { + qtest_add_data_func("cdrom/boot/lsi53c895a", + "-device lsi53c895a -device scsi-cd,drive=cd1 " + "-blockdev file,node-name=cd1,filename=", + test_cdboot); + } } + /* * Unstable CI test under load * See https://lists.gnu.org/archive/html/qemu-devel/2019-02/msg05509.html @@ -150,35 +172,20 @@ static void add_x86_tests(void) qtest_add_data_func("cdrom/boot/isapc", "-M isapc " "-drive if=ide,media=cdrom,file=", test_cdboot); } - if (qtest_has_device("am53c974")) { - qtest_add_data_func("cdrom/boot/am53c974", - "-device am53c974 -device scsi-cd,drive=cd1 " - "-drive if=none,id=cd1,format=raw,file=", - test_cdboot); - } - if (qtest_has_device("dc390")) { - qtest_add_data_func("cdrom/boot/dc390", - "-device dc390 -device scsi-cd,drive=cd1 " - "-blockdev file,node-name=cd1,filename=", - test_cdboot); - } - if (qtest_has_device("lsi53c895a")) { - qtest_add_data_func("cdrom/boot/lsi53c895a", - "-device lsi53c895a -device scsi-cd,drive=cd1 " - "-blockdev file,node-name=cd1,filename=", - test_cdboot); - } - if (qtest_has_device("megasas")) { - qtest_add_data_func("cdrom/boot/megasas", "-M q35 " - "-device megasas -device scsi-cd,drive=cd1 " - "-blockdev file,node-name=cd1,filename=", - test_cdboot); - } - if (qtest_has_device("megasas-gen2")) { - qtest_add_data_func("cdrom/boot/megasas-gen2", "-M q35 " - "-device megasas-gen2 -device scsi-cd,drive=cd1 " - "-blockdev file,node-name=cd1,filename=", - test_cdboot); + + if (qtest_has_machine("q35")) { + if (qtest_has_device("megasas")) { + qtest_add_data_func("cdrom/boot/megasas", "-M q35 " + "-device megasas -device scsi-cd,drive=cd1 " + "-blockdev file,node-name=cd1,filename=", + test_cdboot); + } + if (qtest_has_device("megasas-gen2")) { + qtest_add_data_func("cdrom/boot/megasas-gen2", "-M q35 " + "-device megasas-gen2 -device scsi-cd,drive=cd1 " + "-blockdev file,node-name=cd1,filename=", + test_cdboot); + } } } @@ -206,6 +213,30 @@ static void add_s390x_tests(void) "-drive driver=null-co,read-zeroes=on,if=none,id=d1 " "-device virtio-blk,drive=d2,bootindex=1 " "-drive if=none,id=d2,media=cdrom,file=", test_cdboot); + qtest_add_data_func("cdrom/boot/as-fallback-device", + "-device virtio-serial -device virtio-scsi " + "-device virtio-blk,drive=d1,bootindex=1 " + "-drive driver=null-co,read-zeroes=on,if=none,id=d1 " + "-device virtio-blk,drive=d2,bootindex=2 " + "-drive if=none,id=d2,media=cdrom,file=", test_cdboot); + qtest_add_data_func("cdrom/boot/as-last-option", + "-device virtio-serial -device virtio-scsi " + "-device virtio-blk,drive=d1,bootindex=1 " + "-drive driver=null-co,read-zeroes=on,if=none,id=d1 " + "-device virtio-blk,drive=d2,bootindex=2 " + "-drive driver=null-co,read-zeroes=on,if=none,id=d2 " + "-device virtio-blk,drive=d3,bootindex=3 " + "-drive driver=null-co,read-zeroes=on,if=none,id=d3 " + "-device scsi-hd,drive=d4,bootindex=4 " + "-drive driver=null-co,read-zeroes=on,if=none,id=d4 " + "-device scsi-hd,drive=d5,bootindex=5 " + "-drive driver=null-co,read-zeroes=on,if=none,id=d5 " + "-device virtio-blk,drive=d6,bootindex=6 " + "-drive driver=null-co,read-zeroes=on,if=none,id=d6 " + "-device scsi-hd,drive=d7,bootindex=7 " + "-drive driver=null-co,read-zeroes=on,if=none,id=d7 " + "-device scsi-cd,drive=d8,bootindex=8 " + "-drive if=none,id=d8,media=cdrom,file=", test_cdboot); if (qtest_has_device("x-terminal3270")) { qtest_add_data_func("cdrom/boot/without-bootindex", "-device virtio-scsi -device virtio-serial " diff --git a/tests/qtest/cmsdk-apb-watchdog-test.c b/tests/qtest/cmsdk-apb-watchdog-test.c index 00b5dbb..cd0c602 100644 --- a/tests/qtest/cmsdk-apb-watchdog-test.c +++ b/tests/qtest/cmsdk-apb-watchdog-test.c @@ -15,14 +15,12 @@ */ #include "qemu/osdep.h" +#include "exec/hwaddr.h" #include "qemu/bitops.h" #include "libqtest-single.h" -/* - * lm3s811evb watchdog; at board startup this runs at 200MHz / 16 == 12.5MHz, - * which is 80ns per tick. - */ #define WDOG_BASE 0x40000000 +#define WDOG_BASE_MPS2 0x40008000 #define WDOGLOAD 0 #define WDOGVALUE 4 @@ -37,39 +35,97 @@ #define SYSDIV_SHIFT 23 #define SYSDIV_LENGTH 4 -static void test_watchdog(void) +#define WDOGLOAD_DEFAULT 0xFFFFFFFF +#define WDOGVALUE_DEFAULT 0xFFFFFFFF + +typedef struct CMSDKAPBWatchdogTestArgs { + int64_t tick; + hwaddr wdog_base; + const char *machine; +} CMSDKAPBWatchdogTestArgs; + +enum { + MACHINE_LM3S811EVB, + MACHINE_MPS2_AN385, +}; + +/* + * lm3s811evb watchdog; at board startup this runs at 200MHz / 16 == 12.5MHz, + * which is 80ns per tick. + * + * IoTKit/ARMSSE dualtimer; driven at 25MHz in mps2-an385, so 40ns per tick + */ +static const CMSDKAPBWatchdogTestArgs machine_info[] = { + [MACHINE_LM3S811EVB] = { + .tick = 80, + .wdog_base = WDOG_BASE, + .machine = "lm3s811evb", + }, + [MACHINE_MPS2_AN385] = { + .tick = 40, + .wdog_base = WDOG_BASE_MPS2, + .machine = "mps2-an385", + }, +}; + +static void system_reset(QTestState *qtest) { - g_assert_cmpuint(readl(WDOG_BASE + WDOGRIS), ==, 0); + QDict *resp; - writel(WDOG_BASE + WDOGCONTROL, 1); - writel(WDOG_BASE + WDOGLOAD, 1000); + resp = qtest_qmp(qtest, "{'execute': 'system_reset'}"); + g_assert(qdict_haskey(resp, "return")); + qobject_unref(resp); + qtest_qmp_eventwait(qtest, "RESET"); +} + +static void test_watchdog(const void *ptr) +{ + const CMSDKAPBWatchdogTestArgs *args = ptr; + hwaddr wdog_base = args->wdog_base; + int64_t tick = args->tick; + g_autofree gchar *cmdline = g_strdup_printf("-machine %s", args->machine); + qtest_start(cmdline); + + g_assert_cmpuint(readl(wdog_base + WDOGRIS), ==, 0); + + writel(wdog_base + WDOGCONTROL, 1); + writel(wdog_base + WDOGLOAD, 1000); /* Step to just past the 500th tick */ - clock_step(500 * 80 + 1); - g_assert_cmpuint(readl(WDOG_BASE + WDOGRIS), ==, 0); - g_assert_cmpuint(readl(WDOG_BASE + WDOGVALUE), ==, 500); + clock_step(500 * tick + 1); + g_assert_cmpuint(readl(wdog_base + WDOGRIS), ==, 0); + g_assert_cmpuint(readl(wdog_base + WDOGVALUE), ==, 500); /* Just past the 1000th tick: timer should have fired */ - clock_step(500 * 80); - g_assert_cmpuint(readl(WDOG_BASE + WDOGRIS), ==, 1); - g_assert_cmpuint(readl(WDOG_BASE + WDOGVALUE), ==, 0); + clock_step(500 * tick); + g_assert_cmpuint(readl(wdog_base + WDOGRIS), ==, 1); + g_assert_cmpuint(readl(wdog_base + WDOGVALUE), ==, 0); /* VALUE reloads at following tick */ - clock_step(80); - g_assert_cmpuint(readl(WDOG_BASE + WDOGVALUE), ==, 1000); + clock_step(tick); + g_assert_cmpuint(readl(wdog_base + WDOGVALUE), ==, 1000); /* Writing any value to WDOGINTCLR clears the interrupt and reloads */ - clock_step(500 * 80); - g_assert_cmpuint(readl(WDOG_BASE + WDOGVALUE), ==, 500); - g_assert_cmpuint(readl(WDOG_BASE + WDOGRIS), ==, 1); - writel(WDOG_BASE + WDOGINTCLR, 0); - g_assert_cmpuint(readl(WDOG_BASE + WDOGVALUE), ==, 1000); - g_assert_cmpuint(readl(WDOG_BASE + WDOGRIS), ==, 0); + clock_step(500 * tick); + g_assert_cmpuint(readl(wdog_base + WDOGVALUE), ==, 500); + g_assert_cmpuint(readl(wdog_base + WDOGRIS), ==, 1); + writel(wdog_base + WDOGINTCLR, 0); + g_assert_cmpuint(readl(wdog_base + WDOGVALUE), ==, 1000); + g_assert_cmpuint(readl(wdog_base + WDOGRIS), ==, 0); + + qtest_end(); } -static void test_clock_change(void) +/* + * This test can only be executed in the stellaris board since it relies on a + * component of the board to change the clocking parameters of the watchdog. + */ +static void test_clock_change(const void *ptr) { uint32_t rcc; + const CMSDKAPBWatchdogTestArgs *args = ptr; + g_autofree gchar *cmdline = g_strdup_printf("-machine %s", args->machine); + qtest_start(cmdline); /* * Test that writing to the stellaris board's RCC register to @@ -109,23 +165,231 @@ static void test_clock_change(void) writel(WDOG_BASE + WDOGINTCLR, 0); g_assert_cmpuint(readl(WDOG_BASE + WDOGVALUE), ==, 1000); g_assert_cmpuint(readl(WDOG_BASE + WDOGRIS), ==, 0); + + qtest_end(); } -int main(int argc, char **argv) +/* Tests the counter is not running after reset. */ +static void test_watchdog_reset(const void *ptr) { - int r; + const CMSDKAPBWatchdogTestArgs *args = ptr; + hwaddr wdog_base = args->wdog_base; + int64_t tick = args->tick; + g_autofree gchar *cmdline = g_strdup_printf("-machine %s", args->machine); + qtest_start(cmdline); + g_assert_cmpuint(readl(wdog_base + WDOGRIS), ==, 0); - g_test_init(&argc, &argv, NULL); + g_assert_cmphex(readl(wdog_base + WDOGLOAD), ==, WDOGLOAD_DEFAULT); + g_assert_cmphex(readl(wdog_base + WDOGVALUE), ==, WDOGVALUE_DEFAULT); + + g_assert_cmphex(readl(wdog_base + WDOGCONTROL), ==, 0); + + /* + * The counter should not be running if WDOGCONTROL.INTEN has not been set, + * as it is the case after a cold reset. + */ + clock_step(15 * tick + 1); + g_assert_cmphex(readl(wdog_base + WDOGLOAD), ==, WDOGLOAD_DEFAULT); + g_assert_cmphex(readl(wdog_base + WDOGVALUE), ==, WDOGVALUE_DEFAULT); + + /* Let the counter run before reset */ + writel(wdog_base + WDOGLOAD, 3000); + writel(wdog_base + WDOGCONTROL, 1); + + /* Verify it is running */ + clock_step(1000 * tick + 1); + g_assert_cmpuint(readl(wdog_base + WDOGLOAD), ==, 3000); + g_assert_cmpuint(readl(wdog_base + WDOGVALUE), ==, 2000); + + system_reset(global_qtest); - qtest_start("-machine lm3s811evb"); + /* Check defaults after reset */ + g_assert_cmphex(readl(wdog_base + WDOGLOAD), ==, WDOGLOAD_DEFAULT); + g_assert_cmphex(readl(wdog_base + WDOGVALUE), ==, WDOGVALUE_DEFAULT); - qtest_add_func("/cmsdk-apb-watchdog/watchdog", test_watchdog); - qtest_add_func("/cmsdk-apb-watchdog/watchdog_clock_change", - test_clock_change); + /* The counter should not be running after reset. */ + clock_step(1000 * tick + 1); + g_assert_cmphex(readl(wdog_base + WDOGLOAD), ==, WDOGLOAD_DEFAULT); + g_assert_cmphex(readl(wdog_base + WDOGVALUE), ==, WDOGVALUE_DEFAULT); - r = g_test_run(); + qtest_end(); +} + +/* + * Tests inten works as the counter enable based on this description: + * + * Enable the interrupt event, WDOGINT. Set HIGH to enable the counter and the + * interrupt, or LOW to disable the counter and interrupt. Reloads the counter + * from the value in WDOGLOAD when the interrupt is enabled, after previously + * being disabled. + */ +static void test_watchdog_inten(const void *ptr) +{ + const CMSDKAPBWatchdogTestArgs *args = ptr; + hwaddr wdog_base = args->wdog_base; + int64_t tick = args->tick; + g_autofree gchar *cmdline = g_strdup_printf("-machine %s", args->machine); + qtest_start(cmdline); + g_assert_cmpuint(readl(wdog_base + WDOGRIS), ==, 0); + + g_assert_cmphex(readl(wdog_base + WDOGLOAD), ==, WDOGLOAD_DEFAULT); + g_assert_cmphex(readl(wdog_base + WDOGVALUE), ==, WDOGVALUE_DEFAULT); + + /* + * When WDOGLOAD is written to, the count is immediately restarted from the + * new value. + * + * Note: the counter should not be running as long as WDOGCONTROL.INTEN is + * not set + */ + writel(wdog_base + WDOGLOAD, 4000); + g_assert_cmpuint(readl(wdog_base + WDOGLOAD), ==, 4000); + g_assert_cmpuint(readl(wdog_base + WDOGVALUE), ==, 4000); + clock_step(500 * tick + 1); + g_assert_cmpuint(readl(wdog_base + WDOGLOAD), ==, 4000); + g_assert_cmpuint(readl(wdog_base + WDOGVALUE), ==, 4000); + + /* Set HIGH WDOGCONTROL.INTEN to enable the counter and the interrupt */ + writel(wdog_base + WDOGCONTROL, 1); + clock_step(500 * tick + 1); + g_assert_cmpuint(readl(wdog_base + WDOGLOAD), ==, 4000); + g_assert_cmpuint(readl(wdog_base + WDOGVALUE), ==, 3500); + + /* or LOW to disable the counter and interrupt. */ + writel(wdog_base + WDOGCONTROL, 0); + clock_step(100 * tick); + g_assert_cmpuint(readl(wdog_base + WDOGLOAD), ==, 4000); + g_assert_cmpuint(readl(wdog_base + WDOGVALUE), ==, 3500); + + /* + * Reloads the counter from the value in WDOGLOAD when the interrupt is + * enabled, after previously being disabled. + */ + writel(wdog_base + WDOGCONTROL, 1); + g_assert_cmpuint(readl(wdog_base + WDOGLOAD), ==, 4000); + g_assert_cmpuint(readl(wdog_base + WDOGVALUE), ==, 4000); + + /* Test counter is still on */ + clock_step(50 * tick + 1); + g_assert_cmpuint(readl(wdog_base + WDOGLOAD), ==, 4000); + g_assert_cmpuint(readl(wdog_base + WDOGVALUE), ==, 3950); + + /* + * When WDOGLOAD is written to, the count is immediately restarted from the + * new value. + * + * Note: the counter should be running since WDOGCONTROL.INTEN is set + */ + writel(wdog_base + WDOGLOAD, 5000); + g_assert_cmpuint(readl(wdog_base + WDOGLOAD), ==, 5000); + g_assert_cmpuint(readl(wdog_base + WDOGVALUE), ==, 5000); + clock_step(4999 * tick + 1); + g_assert_cmpuint(readl(wdog_base + WDOGLOAD), ==, 5000); + g_assert_cmpuint(readl(wdog_base + WDOGVALUE), ==, 1); + g_assert_cmpuint(readl(wdog_base + WDOGRIS), ==, 0); + + /* Finally disable and check the conditions don't change */ + writel(wdog_base + WDOGCONTROL, 0); + clock_step(10 * tick); + g_assert_cmpuint(readl(wdog_base + WDOGLOAD), ==, 5000); + g_assert_cmpuint(readl(wdog_base + WDOGVALUE), ==, 1); + g_assert_cmpuint(readl(wdog_base + WDOGRIS), ==, 0); + + qtest_end(); +} + +/* + * Tests the following custom behavior: + * + * The Luminary version of this device ignores writes to this register after the + * guest has enabled interrupts (so they can only be disabled again via reset). + */ +static void test_watchdog_inten_luminary(const void *ptr) +{ + const CMSDKAPBWatchdogTestArgs *args = ptr; + hwaddr wdog_base = args->wdog_base; + int64_t tick = args->tick; + g_autofree gchar *cmdline = g_strdup_printf("-machine %s", args->machine); + qtest_start(cmdline); + g_assert_cmpuint(readl(wdog_base + WDOGRIS), ==, 0); + + g_assert_cmphex(readl(wdog_base + WDOGLOAD), ==, WDOGLOAD_DEFAULT); + g_assert_cmphex(readl(wdog_base + WDOGVALUE), ==, WDOGVALUE_DEFAULT); + + /* + * When WDOGLOAD is written to, the count is immediately restarted from the + * new value. + * + * Note: the counter should not be running as long as WDOGCONTROL.INTEN is + * not set + */ + writel(wdog_base + WDOGLOAD, 4000); + g_assert_cmpuint(readl(wdog_base + WDOGLOAD), ==, 4000); + g_assert_cmpuint(readl(wdog_base + WDOGVALUE), ==, 4000); + clock_step(500 * tick + 1); + g_assert_cmpuint(readl(wdog_base + WDOGLOAD), ==, 4000); + g_assert_cmpuint(readl(wdog_base + WDOGVALUE), ==, 4000); + + /* Set HIGH WDOGCONTROL.INTEN to enable the counter and the interrupt */ + writel(wdog_base + WDOGCONTROL, 1); + clock_step(500 * tick + 1); + g_assert_cmpuint(readl(wdog_base + WDOGLOAD), ==, 4000); + g_assert_cmpuint(readl(wdog_base + WDOGVALUE), ==, 3500); + + /* + * The Luminary version of this device ignores writes to this register after + * the guest has enabled interrupts + */ + writel(wdog_base + WDOGCONTROL, 0); + clock_step(100 * tick); + g_assert_cmpuint(readl(wdog_base + WDOGLOAD), ==, 4000); + g_assert_cmpuint(readl(wdog_base + WDOGVALUE), ==, 3400); + g_assert_cmphex(readl(wdog_base + WDOGCONTROL), ==, 0x1); + + /* They can only be disabled again via reset */ + system_reset(global_qtest); + + /* Check defaults after reset */ + g_assert_cmphex(readl(wdog_base + WDOGLOAD), ==, WDOGLOAD_DEFAULT); + g_assert_cmphex(readl(wdog_base + WDOGVALUE), ==, WDOGVALUE_DEFAULT); + g_assert_cmphex(readl(wdog_base + WDOGCONTROL), ==, 0); + + /* The counter should not be running after reset. */ + clock_step(1000 * tick + 1); + g_assert_cmphex(readl(wdog_base + WDOGLOAD), ==, WDOGLOAD_DEFAULT); + g_assert_cmphex(readl(wdog_base + WDOGVALUE), ==, WDOGVALUE_DEFAULT); qtest_end(); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + g_test_set_nonfatal_assertions(); + + if (qtest_has_machine(machine_info[MACHINE_LM3S811EVB].machine)) { + qtest_add_data_func("/cmsdk-apb-watchdog/watchdog", + &machine_info[MACHINE_LM3S811EVB], test_watchdog); + qtest_add_data_func("/cmsdk-apb-watchdog/watchdog_clock_change", + &machine_info[MACHINE_LM3S811EVB], + test_clock_change); + qtest_add_data_func("/cmsdk-apb-watchdog/watchdog_reset", + &machine_info[MACHINE_LM3S811EVB], + test_watchdog_reset); + qtest_add_data_func("/cmsdk-apb-watchdog/watchdog_inten_luminary", + &machine_info[MACHINE_LM3S811EVB], + test_watchdog_inten_luminary); + } + if (qtest_has_machine(machine_info[MACHINE_MPS2_AN385].machine)) { + qtest_add_data_func("/cmsdk-apb-watchdog/watchdog_mps2", + &machine_info[MACHINE_MPS2_AN385], test_watchdog); + qtest_add_data_func("/cmsdk-apb-watchdog/watchdog_reset_mps2", + &machine_info[MACHINE_MPS2_AN385], + test_watchdog_reset); + qtest_add_data_func("/cmsdk-apb-watchdog/watchdog_inten", + &machine_info[MACHINE_MPS2_AN385], + test_watchdog_inten); + } - return r; + return g_test_run(); } diff --git a/tests/qtest/cpu-plug-test.c b/tests/qtest/cpu-plug-test.c index 7f5dd5f..44d7046 100644 --- a/tests/qtest/cpu-plug-test.c +++ b/tests/qtest/cpu-plug-test.c @@ -10,8 +10,8 @@ #include "qemu/osdep.h" #include "libqtest-single.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qlist.h" +#include "qobject/qdict.h" +#include "qobject/qlist.h" struct PlugTestData { char *machine; @@ -156,6 +156,28 @@ static void add_s390x_test_case(const char *mname) g_free(path); } +static void add_loongarch_test_case(const char *mname) +{ + char *path; + PlugTestData *data; + + data = g_new(PlugTestData, 1); + data->machine = g_strdup(mname); + data->cpu_model = "la464"; + data->device_model = g_strdup("la464-loongarch-cpu"); + data->sockets = 1; + data->cores = 3; + data->threads = 1; + data->maxcpus = data->sockets * data->cores * data->threads; + + path = g_strdup_printf("cpu-plug/%s/device-add/%ux%ux%u&maxcpus=%u", + mname, data->sockets, data->cores, + data->threads, data->maxcpus); + qtest_add_data_func_full(path, data, test_plug_with_device_add, + test_data_free); + g_free(path); +} + int main(int argc, char **argv) { const char *arch = qtest_get_arch(); @@ -168,6 +190,8 @@ int main(int argc, char **argv) qtest_cb_for_every_machine(add_pseries_test_case, g_test_quick()); } else if (g_str_equal(arch, "s390x")) { qtest_cb_for_every_machine(add_s390x_test_case, g_test_quick()); + } else if (g_str_equal(arch, "loongarch64")) { + add_loongarch_test_case("virt"); } return g_test_run(); diff --git a/tests/qtest/dbus-display-test.c b/tests/qtest/dbus-display-test.c index 0390bdc..f7fc873 100644 --- a/tests/qtest/dbus-display-test.c +++ b/tests/qtest/dbus-display-test.c @@ -2,9 +2,14 @@ #include "qemu/sockets.h" #include "qemu/dbus.h" #include "qemu/sockets.h" +#include "glib.h" +#include "glibconfig.h" #include <gio/gio.h> #include <gio/gunixfdlist.h> #include "libqtest.h" +#ifndef WIN32 +#include <sys/mman.h> +#endif #include "ui/dbus-display1.h" static GDBusConnection* @@ -82,6 +87,7 @@ typedef struct TestDBusConsoleRegister { GThread *thread; GDBusConnection *listener_conn; GDBusObjectManagerServer *server; + bool with_map; } TestDBusConsoleRegister; static gboolean listener_handle_scanout( @@ -94,13 +100,49 @@ static gboolean listener_handle_scanout( GVariant *arg_data, TestDBusConsoleRegister *test) { + if (!test->with_map) { + g_main_loop_quit(test->loop); + } + + return DBUS_METHOD_INVOCATION_HANDLED; +} + +#ifndef WIN32 +static gboolean listener_handle_scanout_map( + QemuDBusDisplay1ListenerUnixMap *object, + GDBusMethodInvocation *invocation, + GUnixFDList *fd_list, + GVariant *arg_handle, + guint arg_offset, + guint arg_width, + guint arg_height, + guint arg_stride, + guint arg_pixman_format, + TestDBusConsoleRegister *test) +{ + int fd = -1; + gint32 handle = g_variant_get_handle(arg_handle); + g_autoptr(GError) error = NULL; + void *addr = NULL; + size_t len = arg_height * arg_stride; + + g_assert_cmpuint(g_unix_fd_list_get_length(fd_list), ==, 1); + fd = g_unix_fd_list_get(fd_list, handle, &error); + g_assert_no_error(error); + + addr = mmap(NULL, len, PROT_READ, MAP_PRIVATE, fd, arg_offset); + g_assert_no_errno(addr == MAP_FAILED ? -1 : 0); + g_assert_no_errno(munmap(addr, len)); + g_main_loop_quit(test->loop); + close(fd); return DBUS_METHOD_INVOCATION_HANDLED; } +#endif static void -test_dbus_console_setup_listener(TestDBusConsoleRegister *test) +test_dbus_console_setup_listener(TestDBusConsoleRegister *test, bool with_map) { g_autoptr(GDBusObjectSkeleton) listener = NULL; g_autoptr(QemuDBusDisplay1ListenerSkeleton) iface = NULL; @@ -114,6 +156,25 @@ test_dbus_console_setup_listener(TestDBusConsoleRegister *test) NULL); g_dbus_object_skeleton_add_interface(listener, G_DBUS_INTERFACE_SKELETON(iface)); + if (with_map) { +#ifdef WIN32 + g_test_skip("map test lacking on win32"); + return; +#else + g_autoptr(QemuDBusDisplay1ListenerUnixMapSkeleton) iface_map = + QEMU_DBUS_DISPLAY1_LISTENER_UNIX_MAP_SKELETON( + qemu_dbus_display1_listener_unix_map_skeleton_new()); + + g_object_connect(iface_map, + "signal::handle-scanout-map", listener_handle_scanout_map, test, + NULL); + g_dbus_object_skeleton_add_interface(listener, + G_DBUS_INTERFACE_SKELETON(iface_map)); + g_object_set(iface, "interfaces", + (const gchar *[]) { "org.qemu.Display1.Listener.Unix.Map", NULL }, + NULL); +#endif + } g_dbus_object_manager_server_export(test->server, listener); g_dbus_object_manager_server_set_connection(test->server, test->listener_conn); @@ -145,7 +206,7 @@ test_dbus_console_registered(GObject *source_object, g_assert_no_error(err); test->listener_conn = g_thread_join(test->thread); - test_dbus_console_setup_listener(test); + test_dbus_console_setup_listener(test, test->with_map); } static gpointer @@ -155,7 +216,7 @@ test_dbus_p2p_server_setup_thread(gpointer data) } static void -test_dbus_display_console(void) +test_dbus_display_console(const void* data) { g_autoptr(GError) err = NULL; g_autoptr(GDBusConnection) conn = NULL; @@ -163,7 +224,7 @@ test_dbus_display_console(void) g_autoptr(GMainLoop) loop = NULL; QTestState *qts = NULL; int pair[2]; - TestDBusConsoleRegister test = { 0, }; + TestDBusConsoleRegister test = { 0, .with_map = GPOINTER_TO_INT(data) }; #ifdef WIN32 WSAPROTOCOL_INFOW info; g_autoptr(GVariant) listener = NULL; @@ -299,7 +360,8 @@ main(int argc, char **argv) g_test_init(&argc, &argv, NULL); qtest_add_func("/dbus-display/vm", test_dbus_display_vm); - qtest_add_func("/dbus-display/console", test_dbus_display_console); + qtest_add_data_func("/dbus-display/console", GINT_TO_POINTER(false), test_dbus_display_console); + qtest_add_data_func("/dbus-display/console/map", GINT_TO_POINTER(true), test_dbus_display_console); qtest_add_func("/dbus-display/keyboard", test_dbus_display_keyboard); return g_test_run(); diff --git a/tests/qtest/device-introspect-test.c b/tests/qtest/device-introspect-test.c index 587da59..f84cec5 100644 --- a/tests/qtest/device-introspect-test.c +++ b/tests/qtest/device-introspect-test.c @@ -18,9 +18,9 @@ */ #include "qemu/osdep.h" -#include "qapi/qmp/qstring.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qlist.h" +#include "qobject/qstring.h" +#include "qobject/qdict.h" +#include "qobject/qlist.h" #include "libqtest.h" const char common_args[] = "-nodefaults -machine none"; diff --git a/tests/qtest/device-plug-test.c b/tests/qtest/device-plug-test.c index c6f3315..2707ee5 100644 --- a/tests/qtest/device-plug-test.c +++ b/tests/qtest/device-plug-test.c @@ -12,17 +12,8 @@ #include "qemu/osdep.h" #include "libqtest.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qstring.h" - -static void system_reset(QTestState *qtest) -{ - QDict *resp; - - resp = qtest_qmp(qtest, "{'execute': 'system_reset'}"); - g_assert(qdict_haskey(resp, "return")); - qobject_unref(resp); -} +#include "qobject/qdict.h" +#include "qobject/qstring.h" static void wait_device_deleted_event(QTestState *qtest, const char *id) { @@ -58,7 +49,7 @@ static void process_device_remove(QTestState *qtest, const char *id) * handled, removing the device. */ qtest_qmp_device_del_send(qtest, id); - system_reset(qtest); + qtest_system_reset_nowait(qtest); wait_device_deleted_event(qtest, id); } diff --git a/tests/qtest/drive_del-test.c b/tests/qtest/drive_del-test.c index 7b67a4b..30d9451 100644 --- a/tests/qtest/drive_del-test.c +++ b/tests/qtest/drive_del-test.c @@ -13,8 +13,8 @@ #include "qemu/osdep.h" #include "libqtest.h" #include "libqos/virtio.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qlist.h" +#include "qobject/qdict.h" +#include "qobject/qlist.h" static const char *qvirtio_get_dev_type(void); @@ -154,15 +154,10 @@ static void device_add(QTestState *qts) static void device_del(QTestState *qts, bool and_reset) { - QDict *response; - qtest_qmp_device_del_send(qts, "dev0"); if (and_reset) { - response = qtest_qmp(qts, "{'execute': 'system_reset' }"); - g_assert(response); - g_assert(qdict_haskey(response, "return")); - qobject_unref(response); + qtest_system_reset_nowait(qts); } qtest_qmp_eventwait(qts, "DEVICE_DELETED"); diff --git a/tests/qtest/emc141x-test.c b/tests/qtest/emc141x-test.c index 8c86694..a24103e 100644 --- a/tests/qtest/emc141x-test.c +++ b/tests/qtest/emc141x-test.c @@ -10,7 +10,7 @@ #include "libqtest-single.h" #include "libqos/qgraph.h" #include "libqos/i2c.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "hw/sensor/emc141x_regs.h" #define EMC1414_TEST_ID "emc1414-test" diff --git a/tests/qtest/fdc-test.c b/tests/qtest/fdc-test.c index 5e8fbda..1b37a8a 100644 --- a/tests/qtest/fdc-test.c +++ b/tests/qtest/fdc-test.c @@ -26,7 +26,7 @@ #include "libqtest-single.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #define DRIVE_FLOPPY_BLANK \ "-drive if=floppy,file=null-co://,file.read-zeroes=on,format=raw,size=1440k" @@ -552,7 +552,7 @@ static bool qtest_check_clang_sanitizer(void) #ifdef QEMU_SANITIZE_ADDRESS return true; #else - g_test_skip("QEMU not configured using --enable-sanitizers"); + g_test_skip("QEMU not configured using --enable-asan"); return false; #endif } diff --git a/tests/qtest/fuzz/fuzz.c b/tests/qtest/fuzz/fuzz.c index 9b9c9f9..ca248a5 100644 --- a/tests/qtest/fuzz/fuzz.c +++ b/tests/qtest/fuzz/fuzz.c @@ -17,9 +17,9 @@ #include "qemu/cutils.h" #include "qemu/datadir.h" -#include "sysemu/sysemu.h" -#include "sysemu/qtest.h" -#include "sysemu/runstate.h" +#include "system/system.h" +#include "system/qtest.h" +#include "system/runstate.h" #include "qemu/main-loop.h" #include "qemu/rcu.h" #include "tests/qtest/libqtest.h" @@ -41,6 +41,7 @@ static FuzzTargetList *fuzz_target_list; static FuzzTarget *fuzz_target; static QTestState *fuzz_qts; +int (*qemu_main)(void); void flush_events(QTestState *s) diff --git a/tests/qtest/fuzz/generic_fuzz.c b/tests/qtest/fuzz/generic_fuzz.c index d107a49..f12080e 100644 --- a/tests/qtest/fuzz/generic_fuzz.c +++ b/tests/qtest/fuzz/generic_fuzz.c @@ -20,8 +20,8 @@ #include "tests/qtest/libqos/pci-pc.h" #include "fuzz.h" #include "string.h" -#include "exec/memory.h" -#include "exec/ramblock.h" +#include "system/memory.h" +#include "system/ramblock.h" #include "hw/qdev-core.h" #include "hw/pci/pci.h" #include "hw/pci/pci_device.h" @@ -572,7 +572,6 @@ static void op_add_dma_pattern(QTestState *s, pattern p = {a.index, a.stride, len - sizeof(a), data + sizeof(a)}; p.index = a.index % p.len; g_array_append_val(dma_patterns, p); - return; } static void op_clear_dma_patterns(QTestState *s, diff --git a/tests/qtest/fuzz/qos_fuzz.c b/tests/qtest/fuzz/qos_fuzz.c index d3839bf..9afe8bf 100644 --- a/tests/qtest/fuzz/qos_fuzz.c +++ b/tests/qtest/fuzz/qos_fuzz.c @@ -19,7 +19,7 @@ #include "qemu/osdep.h" #include "qemu/units.h" #include "qapi/error.h" -#include "exec/memory.h" +#include "system/memory.h" #include "qemu/main-loop.h" #include "tests/qtest/libqtest.h" diff --git a/tests/qtest/fuzz/qtest_wrappers.c b/tests/qtest/fuzz/qtest_wrappers.c index 0580f8d..d7adcbe 100644 --- a/tests/qtest/fuzz/qtest_wrappers.c +++ b/tests/qtest/fuzz/qtest_wrappers.c @@ -13,7 +13,7 @@ #include "qemu/osdep.h" #include "hw/core/cpu.h" -#include "exec/ioport.h" +#include "system/ioport.h" #include "fuzz.h" diff --git a/tests/qtest/fw_cfg-test.c b/tests/qtest/fw_cfg-test.c index 5dc807b..e48b34a 100644 --- a/tests/qtest/fw_cfg-test.c +++ b/tests/qtest/fw_cfg-test.c @@ -243,12 +243,6 @@ int main(int argc, char **argv) qtest_add_func("fw_cfg/ram_size", test_fw_cfg_ram_size); qtest_add_func("fw_cfg/nographic", test_fw_cfg_nographic); qtest_add_func("fw_cfg/nb_cpus", test_fw_cfg_nb_cpus); -#if 0 - qtest_add_func("fw_cfg/machine_id", test_fw_cfg_machine_id); - qtest_add_func("fw_cfg/kernel", test_fw_cfg_kernel); - qtest_add_func("fw_cfg/initrd", test_fw_cfg_initrd); - qtest_add_func("fw_cfg/boot_device", test_fw_cfg_boot_device); -#endif qtest_add_func("fw_cfg/max_cpus", test_fw_cfg_max_cpus); qtest_add_func("fw_cfg/numa", test_fw_cfg_numa); qtest_add_func("fw_cfg/boot_menu", test_fw_cfg_boot_menu); diff --git a/tests/qtest/hd-geo-test.c b/tests/qtest/hd-geo-test.c index d08bffa..41481a5 100644 --- a/tests/qtest/hd-geo-test.c +++ b/tests/qtest/hd-geo-test.c @@ -17,7 +17,7 @@ #include "qemu/osdep.h" #include "qemu/bswap.h" -#include "qapi/qmp/qlist.h" +#include "qobject/qlist.h" #include "libqtest.h" #include "libqos/fw_cfg.h" #include "libqos/libqos.h" @@ -900,7 +900,6 @@ static void test_override_hot_unplug(TestArgs *args, const char *devid, QTestState *qts; char *joined_args; QFWCFG *fw_cfg; - QDict *response; int i; joined_args = g_strjoinv(" ", args->argv); @@ -913,13 +912,7 @@ static void test_override_hot_unplug(TestArgs *args, const char *devid, /* unplug device an restart */ qtest_qmp_device_del_send(qts, devid); - response = qtest_qmp(qts, - "{ 'execute': 'system_reset', 'arguments': { }}"); - g_assert(response); - g_assert(!qdict_haskey(response, "error")); - qobject_unref(response); - - qtest_qmp_eventwait(qts, "RESET"); + qtest_system_reset(qts); read_bootdevices(fw_cfg, expected2); @@ -1074,17 +1067,26 @@ int main(int argc, char **argv) } } - qtest_add_func("hd-geo/ide/none", test_ide_none); - qtest_add_func("hd-geo/ide/drive/mbr/blank", test_ide_drive_mbr_blank); - qtest_add_func("hd-geo/ide/drive/mbr/lba", test_ide_drive_mbr_lba); - qtest_add_func("hd-geo/ide/drive/mbr/chs", test_ide_drive_mbr_chs); - qtest_add_func("hd-geo/ide/drive/cd_0", test_ide_drive_cd_0); - qtest_add_func("hd-geo/ide/device/mbr/blank", test_ide_device_mbr_blank); - qtest_add_func("hd-geo/ide/device/mbr/lba", test_ide_device_mbr_lba); - qtest_add_func("hd-geo/ide/device/mbr/chs", test_ide_device_mbr_chs); - qtest_add_func("hd-geo/ide/device/user/chs", test_ide_device_user_chs); - qtest_add_func("hd-geo/ide/device/user/chst", test_ide_device_user_chst); - if (have_qemu_img()) { + if (qtest_has_machine("pc")) { + qtest_add_func("hd-geo/ide/none", test_ide_none); + qtest_add_func("hd-geo/ide/drive/mbr/blank", test_ide_drive_mbr_blank); + qtest_add_func("hd-geo/ide/drive/mbr/lba", test_ide_drive_mbr_lba); + qtest_add_func("hd-geo/ide/drive/mbr/chs", test_ide_drive_mbr_chs); + qtest_add_func("hd-geo/ide/drive/cd_0", test_ide_drive_cd_0); + qtest_add_func("hd-geo/ide/device/mbr/blank", test_ide_device_mbr_blank); + qtest_add_func("hd-geo/ide/device/mbr/lba", test_ide_device_mbr_lba); + qtest_add_func("hd-geo/ide/device/mbr/chs", test_ide_device_mbr_chs); + qtest_add_func("hd-geo/ide/device/user/chs", test_ide_device_user_chs); + qtest_add_func("hd-geo/ide/device/user/chst", test_ide_device_user_chst); + } + + if (!have_qemu_img()) { + g_test_message("QTEST_QEMU_IMG not set or qemu-img missing; " + "skipping hd-geo/override/* tests"); + goto test_add_done; + } + + if (qtest_has_machine("pc")) { qtest_add_func("hd-geo/override/ide", test_override_ide); if (qtest_has_device("lsi53c895a")) { qtest_add_func("hd-geo/override/scsi", test_override_scsi); @@ -1104,30 +1106,26 @@ int main(int argc, char **argv) qtest_add_func("hd-geo/override/virtio_blk", test_override_virtio_blk); } + } - if (qtest_has_machine("q35")) { - qtest_add_func("hd-geo/override/sata", test_override_sata); - qtest_add_func("hd-geo/override/zero_chs_q35", - test_override_zero_chs_q35); - if (qtest_has_device("lsi53c895a")) { - qtest_add_func("hd-geo/override/scsi_q35", - test_override_scsi_q35); - } - if (qtest_has_device("virtio-scsi-pci")) { - qtest_add_func("hd-geo/override/scsi_hot_unplug_q35", - test_override_scsi_hot_unplug_q35); - } - if (qtest_has_device("virtio-blk-pci")) { - qtest_add_func("hd-geo/override/virtio_hot_unplug_q35", - test_override_virtio_hot_unplug_q35); - qtest_add_func("hd-geo/override/virtio_blk_q35", - test_override_virtio_blk_q35); - } - + if (qtest_has_machine("q35")) { + qtest_add_func("hd-geo/override/sata", test_override_sata); + qtest_add_func("hd-geo/override/zero_chs_q35", + test_override_zero_chs_q35); + if (qtest_has_device("lsi53c895a")) { + qtest_add_func("hd-geo/override/scsi_q35", + test_override_scsi_q35); + } + if (qtest_has_device("virtio-scsi-pci")) { + qtest_add_func("hd-geo/override/scsi_hot_unplug_q35", + test_override_scsi_hot_unplug_q35); + } + if (qtest_has_device("virtio-blk-pci")) { + qtest_add_func("hd-geo/override/virtio_hot_unplug_q35", + test_override_virtio_hot_unplug_q35); + qtest_add_func("hd-geo/override/virtio_blk_q35", + test_override_virtio_blk_q35); } - } else { - g_test_message("QTEST_QEMU_IMG not set or qemu-img missing; " - "skipping hd-geo/override/* tests"); } test_add_done: diff --git a/tests/qtest/ide-test.c b/tests/qtest/ide-test.c index 90ba6b2..ceee444 100644 --- a/tests/qtest/ide-test.c +++ b/tests/qtest/ide-test.c @@ -29,7 +29,7 @@ #include "libqos/libqos.h" #include "libqos/pci-pc.h" #include "libqos/malloc-pc.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "qemu/bswap.h" #include "hw/pci/pci_ids.h" #include "hw/pci/pci_regs.h" diff --git a/tests/qtest/intel-iommu-test.c b/tests/qtest/intel-iommu-test.c new file mode 100644 index 0000000..c521b37 --- /dev/null +++ b/tests/qtest/intel-iommu-test.c @@ -0,0 +1,64 @@ +/* + * QTest testcase for intel-iommu + * + * Copyright (c) 2024 Intel, Inc. + * + * Author: Zhenzhong Duan <zhenzhong.duan@intel.com> + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "hw/i386/intel_iommu_internal.h" + +#define CAP_STAGE_1_FIXED1 (VTD_CAP_FRO | VTD_CAP_NFR | VTD_CAP_ND | \ + VTD_CAP_MAMV | VTD_CAP_PSI | VTD_CAP_SLLPS) +#define ECAP_STAGE_1_FIXED1 (VTD_ECAP_QI | VTD_ECAP_IR | VTD_ECAP_IRO | \ + VTD_ECAP_MHMV | VTD_ECAP_SMTS | VTD_ECAP_FLTS) + +static inline uint64_t vtd_reg_readq(QTestState *s, uint64_t offset) +{ + return qtest_readq(s, Q35_HOST_BRIDGE_IOMMU_ADDR + offset); +} + +static void test_intel_iommu_stage_1(void) +{ + uint8_t init_csr[DMAR_REG_SIZE]; /* register values */ + uint8_t post_reset_csr[DMAR_REG_SIZE]; /* register values */ + uint64_t cap, ecap, tmp; + QTestState *s; + + s = qtest_init("-M q35 -device intel-iommu,x-scalable-mode=on,x-flts=on"); + + cap = vtd_reg_readq(s, DMAR_CAP_REG); + g_assert((cap & CAP_STAGE_1_FIXED1) == CAP_STAGE_1_FIXED1); + + tmp = cap & VTD_CAP_SAGAW_MASK; + g_assert(tmp == (VTD_CAP_SAGAW_39bit | VTD_CAP_SAGAW_48bit)); + + tmp = VTD_MGAW_FROM_CAP(cap); + g_assert(tmp == VTD_HOST_AW_48BIT - 1); + + ecap = vtd_reg_readq(s, DMAR_ECAP_REG); + g_assert((ecap & ECAP_STAGE_1_FIXED1) == ECAP_STAGE_1_FIXED1); + + qtest_memread(s, Q35_HOST_BRIDGE_IOMMU_ADDR, init_csr, DMAR_REG_SIZE); + + qobject_unref(qtest_qmp(s, "{ 'execute': 'system_reset' }")); + qtest_qmp_eventwait(s, "RESET"); + + qtest_memread(s, Q35_HOST_BRIDGE_IOMMU_ADDR, post_reset_csr, DMAR_REG_SIZE); + /* Ensure registers are consistent after hard reset */ + g_assert(!memcmp(init_csr, post_reset_csr, DMAR_REG_SIZE)); + + qtest_quit(s); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + qtest_add_func("/q35/intel-iommu/stage-1", test_intel_iommu_stage_1); + + return g_test_run(); +} diff --git a/tests/qtest/ipmi-bt-test.c b/tests/qtest/ipmi-bt-test.c index 383239b..637732f 100644 --- a/tests/qtest/ipmi-bt-test.c +++ b/tests/qtest/ipmi-bt-test.c @@ -251,7 +251,7 @@ static void emu_msg_handler(void) msg[msg_len++] = 0xa0; write_emu_msg(msg, msg_len); } else { - g_assert(0); + g_assert_not_reached(); } } @@ -411,7 +411,7 @@ int main(int argc, char **argv) g_test_init(&argc, &argv, NULL); global_qtest = qtest_initf( - " -chardev socket,id=ipmi0,host=127.0.0.1,port=%d,reconnect=10" + " -chardev socket,id=ipmi0,host=127.0.0.1,port=%d,reconnect-ms=10000" " -device ipmi-bmc-extern,chardev=ipmi0,id=bmc0" " -device isa-ipmi-bt,bmc=bmc0", emu_port); qtest_irq_intercept_in(global_qtest, "ioapic"); diff --git a/tests/qtest/ipmi-kcs-test.c b/tests/qtest/ipmi-kcs-test.c index afc24dd..3186c6a 100644 --- a/tests/qtest/ipmi-kcs-test.c +++ b/tests/qtest/ipmi-kcs-test.c @@ -145,7 +145,7 @@ static void kcs_cmd(uint8_t *cmd, unsigned int cmd_len, break; default: - g_assert(0); + g_assert_not_reached(); } *rsp_len = j; } @@ -184,7 +184,7 @@ static void kcs_abort(uint8_t *cmd, unsigned int cmd_len, break; default: - g_assert(0); + g_assert_not_reached(); } /* Start the abort here */ diff --git a/tests/qtest/isl_pmbus_vr-test.c b/tests/qtest/isl_pmbus_vr-test.c index 5553ea4..1ff840c 100644 --- a/tests/qtest/isl_pmbus_vr-test.c +++ b/tests/qtest/isl_pmbus_vr-test.c @@ -21,8 +21,8 @@ #include "libqtest-single.h" #include "libqos/qgraph.h" #include "libqos/i2c.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qnum.h" +#include "qobject/qdict.h" +#include "qobject/qnum.h" #include "qemu/bitops.h" #define TEST_ID "isl_pmbus_vr-test" diff --git a/tests/qtest/libqmp.c b/tests/qtest/libqmp.c index a89cab0..16fe546 100644 --- a/tests/qtest/libqmp.c +++ b/tests/qtest/libqmp.c @@ -25,8 +25,8 @@ #include "qemu/cutils.h" #include "qemu/sockets.h" #include "qapi/error.h" -#include "qapi/qmp/json-parser.h" -#include "qapi/qmp/qjson.h" +#include "qobject/json-parser.h" +#include "qobject/qjson.h" #define SOCKET_MAX_FDS 16 diff --git a/tests/qtest/libqmp.h b/tests/qtest/libqmp.h index 3445b75..4a931c9 100644 --- a/tests/qtest/libqmp.h +++ b/tests/qtest/libqmp.h @@ -18,7 +18,7 @@ #ifndef LIBQMP_H #define LIBQMP_H -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" QDict *qmp_fd_receive(int fd); #ifndef _WIN32 diff --git a/tests/qtest/libqos/arm-imx25-pdk-machine.c b/tests/qtest/libqos/arm-imx25-pdk-machine.c index 8fe128f..2d8b754 100644 --- a/tests/qtest/libqos/arm-imx25-pdk-machine.c +++ b/tests/qtest/libqos/arm-imx25-pdk-machine.c @@ -23,6 +23,7 @@ #include "libqos-malloc.h" #include "qgraph.h" #include "i2c.h" +#include "hw/i2c/imx_i2c.h" #define ARM_PAGE_SIZE 4096 #define IMX25_PDK_RAM_START 0x80000000 @@ -50,7 +51,7 @@ static void *imx25_pdk_get_driver(void *object, const char *interface) static QOSGraphObject *imx25_pdk_get_device(void *obj, const char *device) { QIMX25PDKMachine *machine = obj; - if (!g_strcmp0(device, "imx.i2c")) { + if (!g_strcmp0(device, TYPE_IMX_I2C)) { return &machine->i2c_1.obj; } @@ -86,7 +87,7 @@ static void imx25_pdk_register_nodes(void) .extra_device_opts = "bus=i2c-bus.0" }; qos_node_create_machine("arm/imx25-pdk", qos_create_machine_arm_imx25_pdk); - qos_node_contains("arm/imx25-pdk", "imx.i2c", &edge, NULL); + qos_node_contains("arm/imx25-pdk", TYPE_IMX_I2C, &edge, NULL); } libqos_init(imx25_pdk_register_nodes); diff --git a/tests/qtest/libqos/arm-n800-machine.c b/tests/qtest/libqos/arm-n800-machine.c deleted file mode 100644 index 4e5afe0..0000000 --- a/tests/qtest/libqos/arm-n800-machine.c +++ /dev/null @@ -1,92 +0,0 @@ -/* - * libqos driver framework - * - * Copyright (c) 2019 Red Hat, Inc. - * - * Author: Paolo Bonzini <pbonzini@redhat.com> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License version 2.1 as published by the Free Software Foundation. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see <http://www.gnu.org/licenses/> - */ - -#include "qemu/osdep.h" -#include "../libqtest.h" -#include "libqos-malloc.h" -#include "qgraph.h" -#include "i2c.h" - -#define ARM_PAGE_SIZE 4096 -#define N800_RAM_START 0x80000000 -#define N800_RAM_END 0x88000000 - -typedef struct QN800Machine QN800Machine; - -struct QN800Machine { - QOSGraphObject obj; - QGuestAllocator alloc; - OMAPI2C i2c_1; -}; - -static void *n800_get_driver(void *object, const char *interface) -{ - QN800Machine *machine = object; - if (!g_strcmp0(interface, "memory")) { - return &machine->alloc; - } - - fprintf(stderr, "%s not present in arm/n800\n", interface); - g_assert_not_reached(); -} - -static QOSGraphObject *n800_get_device(void *obj, const char *device) -{ - QN800Machine *machine = obj; - if (!g_strcmp0(device, "omap_i2c")) { - return &machine->i2c_1.obj; - } - - fprintf(stderr, "%s not present in arm/n800\n", device); - g_assert_not_reached(); -} - -static void n800_destructor(QOSGraphObject *obj) -{ - QN800Machine *machine = (QN800Machine *) obj; - alloc_destroy(&machine->alloc); -} - -static void *qos_create_machine_arm_n800(QTestState *qts) -{ - QN800Machine *machine = g_new0(QN800Machine, 1); - - alloc_init(&machine->alloc, 0, - N800_RAM_START, - N800_RAM_END, - ARM_PAGE_SIZE); - machine->obj.get_device = n800_get_device; - machine->obj.get_driver = n800_get_driver; - machine->obj.destructor = n800_destructor; - - omap_i2c_init(&machine->i2c_1, qts, 0x48070000); - return &machine->obj; -} - -static void n800_register_nodes(void) -{ - QOSGraphEdgeOptions edge = { - .extra_device_opts = "bus=i2c-bus.0" - }; - qos_node_create_machine("arm/n800", qos_create_machine_arm_n800); - qos_node_contains("arm/n800", "omap_i2c", &edge, NULL); -} - -libqos_init(n800_register_nodes); diff --git a/tests/qtest/libqos/fw_cfg.c b/tests/qtest/libqos/fw_cfg.c index 89f053c..0ab3959 100644 --- a/tests/qtest/libqos/fw_cfg.c +++ b/tests/qtest/libqos/fw_cfg.c @@ -14,6 +14,8 @@ #include "qemu/osdep.h" #include "fw_cfg.h" +#include "malloc-pc.h" +#include "libqos-malloc.h" #include "../libqtest.h" #include "qemu/bswap.h" #include "hw/nvram/fw_cfg.h" @@ -60,6 +62,91 @@ static void mm_fw_cfg_select(QFWCFG *fw_cfg, uint16_t key) qtest_writew(fw_cfg->qts, fw_cfg->base, key); } +static void qfw_cfg_dma_transfer(QFWCFG *fw_cfg, QOSState *qs, void *address, + uint32_t length, uint32_t control) +{ + FWCfgDmaAccess access; + uint32_t addr; + uint64_t guest_access_addr; + uint64_t gaddr; + + /* create a data buffer in guest memory */ + gaddr = guest_alloc(&qs->alloc, length); + + if (control & FW_CFG_DMA_CTL_WRITE) { + qtest_bufwrite(fw_cfg->qts, gaddr, address, length); + } + access.address = cpu_to_be64(gaddr); + access.length = cpu_to_be32(length); + access.control = cpu_to_be32(control); + + /* now create a separate buffer in guest memory for 'access' */ + guest_access_addr = guest_alloc(&qs->alloc, sizeof(access)); + qtest_bufwrite(fw_cfg->qts, guest_access_addr, &access, sizeof(access)); + + /* write lower 32 bits of address */ + addr = cpu_to_be32((uint32_t)(uintptr_t)guest_access_addr); + qtest_outl(fw_cfg->qts, fw_cfg->base + 8, addr); + + /* write upper 32 bits of address */ + addr = cpu_to_be32((uint32_t)(uintptr_t)(guest_access_addr >> 32)); + qtest_outl(fw_cfg->qts, fw_cfg->base + 4, addr); + + g_assert(!(be32_to_cpu(access.control) & FW_CFG_DMA_CTL_ERROR)); + + if (control & FW_CFG_DMA_CTL_READ) { + qtest_bufread(fw_cfg->qts, gaddr, address, length); + } + + guest_free(&qs->alloc, guest_access_addr); + guest_free(&qs->alloc, gaddr); +} + +static void qfw_cfg_write_entry(QFWCFG *fw_cfg, QOSState *qs, uint16_t key, + void *buf, uint32_t len) +{ + qfw_cfg_select(fw_cfg, key); + qfw_cfg_dma_transfer(fw_cfg, qs, buf, len, FW_CFG_DMA_CTL_WRITE); +} + +static void qfw_cfg_read_entry(QFWCFG *fw_cfg, QOSState *qs, uint16_t key, + void *buf, uint32_t len) +{ + qfw_cfg_select(fw_cfg, key); + qfw_cfg_dma_transfer(fw_cfg, qs, buf, len, FW_CFG_DMA_CTL_READ); +} + +static bool find_pdir_entry(QFWCFG *fw_cfg, const char *filename, + uint16_t *sel, uint32_t *size) +{ + g_autofree unsigned char *filesbuf = NULL; + uint32_t count; + size_t dsize; + FWCfgFile *pdir_entry; + uint32_t i; + bool found = false; + + *size = 0; + *sel = 0; + + qfw_cfg_get(fw_cfg, FW_CFG_FILE_DIR, &count, sizeof(count)); + count = be32_to_cpu(count); + dsize = sizeof(uint32_t) + count * sizeof(struct fw_cfg_file); + filesbuf = g_malloc(dsize); + qfw_cfg_get(fw_cfg, FW_CFG_FILE_DIR, filesbuf, dsize); + pdir_entry = (FWCfgFile *)(filesbuf + sizeof(uint32_t)); + for (i = 0; i < count; ++i, ++pdir_entry) { + if (!strcmp(pdir_entry->name, filename)) { + *size = be32_to_cpu(pdir_entry->size); + *sel = be16_to_cpu(pdir_entry->select); + found = true; + break; + } + } + + return found; +} + /* * The caller need check the return value. When the return value is * nonzero, it means that some bytes have been transferred. @@ -73,37 +160,106 @@ static void mm_fw_cfg_select(QFWCFG *fw_cfg, uint16_t key) * populated, it has received only a starting slice of the fw_cfg file. */ size_t qfw_cfg_get_file(QFWCFG *fw_cfg, const char *filename, - void *data, size_t buflen) + void *data, size_t buflen) { - uint32_t count; - uint32_t i; - unsigned char *filesbuf = NULL; - size_t dsize; - FWCfgFile *pdir_entry; size_t filesize = 0; + uint32_t len; + uint16_t sel; - qfw_cfg_get(fw_cfg, FW_CFG_FILE_DIR, &count, sizeof(count)); - count = be32_to_cpu(count); - dsize = sizeof(uint32_t) + count * sizeof(struct fw_cfg_file); - filesbuf = g_malloc(dsize); - qfw_cfg_get(fw_cfg, FW_CFG_FILE_DIR, filesbuf, dsize); - pdir_entry = (FWCfgFile *)(filesbuf + sizeof(uint32_t)); - for (i = 0; i < count; ++i, ++pdir_entry) { - if (!strcmp(pdir_entry->name, filename)) { - uint32_t len = be32_to_cpu(pdir_entry->size); - uint16_t sel = be16_to_cpu(pdir_entry->select); - filesize = len; - if (len > buflen) { - len = buflen; - } - qfw_cfg_get(fw_cfg, sel, data, len); - break; + if (find_pdir_entry(fw_cfg, filename, &sel, &len)) { + filesize = len; + if (len > buflen) { + len = buflen; } + qfw_cfg_get(fw_cfg, sel, data, len); } - g_free(filesbuf); + return filesize; } +/* + * The caller need check the return value. When the return value is + * nonzero, it means that some bytes have been transferred. + * + * If the fw_cfg file in question is smaller than the allocated & passed-in + * buffer, then the first len bytes were read. + * + * If the fw_cfg file in question is larger than the passed-in + * buffer, then the return value explains how much was actually read. + * + * It is illegal to call this function if fw_cfg does not support DMA + * interface. The caller should ensure that DMA is supported before + * calling this function. + * + * Passed QOSState pointer qs must be initialized. qs->alloc must also be + * properly initialized. + */ +size_t qfw_cfg_read_file(QFWCFG *fw_cfg, QOSState *qs, const char *filename, + void *data, size_t buflen) +{ + uint32_t len = 0; + uint16_t sel; + uint32_t id; + + g_assert(qs); + g_assert(filename); + g_assert(data); + g_assert(buflen); + /* check if DMA is supported since we use DMA for read */ + id = qfw_cfg_get_u32(fw_cfg, FW_CFG_ID); + g_assert(id & FW_CFG_VERSION_DMA); + + if (find_pdir_entry(fw_cfg, filename, &sel, &len)) { + if (len > buflen) { + len = buflen; + } + qfw_cfg_read_entry(fw_cfg, qs, sel, data, len); + } + + return len; +} + +/* + * The caller need check the return value. When the return value is + * nonzero, it means that some bytes have been transferred. + * + * If the fw_cfg file in question is smaller than the allocated & passed-in + * buffer, then the buffer has been partially written. + * + * If the fw_cfg file in question is larger than the passed-in + * buffer, then the return value explains how much was actually written. + * + * It is illegal to call this function if fw_cfg does not support DMA + * interface. The caller should ensure that DMA is supported before + * calling this function. + * + * Passed QOSState pointer qs must be initialized. qs->alloc must also be + * properly initialized. + */ +size_t qfw_cfg_write_file(QFWCFG *fw_cfg, QOSState *qs, const char *filename, + void *data, size_t buflen) +{ + uint32_t len = 0; + uint16_t sel; + uint32_t id; + + g_assert(qs); + g_assert(filename); + g_assert(data); + g_assert(buflen); + /* write operation is only valid if DMA is supported */ + id = qfw_cfg_get_u32(fw_cfg, FW_CFG_ID); + g_assert(id & FW_CFG_VERSION_DMA); + + if (find_pdir_entry(fw_cfg, filename, &sel, &len)) { + if (len > buflen) { + len = buflen; + } + qfw_cfg_write_entry(fw_cfg, qs, sel, data, len); + } + return len; +} + static void mm_fw_cfg_read(QFWCFG *fw_cfg, void *data, size_t len) { uint8_t *ptr = data; diff --git a/tests/qtest/libqos/fw_cfg.h b/tests/qtest/libqos/fw_cfg.h index b0456a1..6d6ff09 100644 --- a/tests/qtest/libqos/fw_cfg.h +++ b/tests/qtest/libqos/fw_cfg.h @@ -14,6 +14,7 @@ #define LIBQOS_FW_CFG_H #include "../libqtest.h" +#include "libqos.h" typedef struct QFWCFG QFWCFG; @@ -33,7 +34,10 @@ uint32_t qfw_cfg_get_u32(QFWCFG *fw_cfg, uint16_t key); uint64_t qfw_cfg_get_u64(QFWCFG *fw_cfg, uint16_t key); size_t qfw_cfg_get_file(QFWCFG *fw_cfg, const char *filename, void *data, size_t buflen); - +size_t qfw_cfg_write_file(QFWCFG *fw_cfg, QOSState *qs, const char *filename, + void *data, size_t buflen); +size_t qfw_cfg_read_file(QFWCFG *fw_cfg, QOSState *qs, const char *filename, + void *data, size_t buflen); QFWCFG *mm_fw_cfg_init(QTestState *qts, uint64_t base); void mm_fw_cfg_uninit(QFWCFG *fw_cfg); QFWCFG *io_fw_cfg_init(QTestState *qts, uint16_t base); diff --git a/tests/qtest/libqos/generic-pcihost.c b/tests/qtest/libqos/generic-pcihost.c index 3124b0e..4bbeb5f 100644 --- a/tests/qtest/libqos/generic-pcihost.c +++ b/tests/qtest/libqos/generic-pcihost.c @@ -13,7 +13,7 @@ #include "qemu/osdep.h" #include "../libqtest.h" #include "generic-pcihost.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "hw/pci/pci_regs.h" #include "qemu/host-utils.h" diff --git a/tests/qtest/libqos/i2c-imx.c b/tests/qtest/libqos/i2c-imx.c index 710cb92..6d868e4 100644 --- a/tests/qtest/libqos/i2c-imx.c +++ b/tests/qtest/libqos/i2c-imx.c @@ -209,8 +209,8 @@ void imx_i2c_init(IMXI2C *s, QTestState *qts, uint64_t addr) static void imx_i2c_register_nodes(void) { - qos_node_create_driver("imx.i2c", NULL); - qos_node_produces("imx.i2c", "i2c-bus"); + qos_node_create_driver(TYPE_IMX_I2C, NULL); + qos_node_produces(TYPE_IMX_I2C, "i2c-bus"); } libqos_init(imx_i2c_register_nodes); diff --git a/tests/qtest/libqos/igb.c b/tests/qtest/libqos/igb.c index f40c4ec..ab3ef6f 100644 --- a/tests/qtest/libqos/igb.c +++ b/tests/qtest/libqos/igb.c @@ -104,10 +104,10 @@ static void igb_pci_start_hw(QOSGraphObject *obj) e1000e_macreg_write(&d->e1000e, E1000_RDT(0), 0); e1000e_macreg_write(&d->e1000e, E1000_RDH(0), 0); e1000e_macreg_write(&d->e1000e, E1000_RA, - le32_to_cpu(*(uint32_t *)address)); + ldl_le_p(address)); e1000e_macreg_write(&d->e1000e, E1000_RA + 4, E1000_RAH_AV | E1000_RAH_POOL_1 | - le16_to_cpu(*(uint16_t *)(address + 4))); + lduw_le_p(address + 4)); /* Set supported receive descriptor mode */ e1000e_macreg_write(&d->e1000e, diff --git a/tests/qtest/libqos/libqos-malloc.c b/tests/qtest/libqos/libqos-malloc.c index d756697..c90f8f0 100644 --- a/tests/qtest/libqos/libqos-malloc.c +++ b/tests/qtest/libqos/libqos-malloc.c @@ -342,5 +342,4 @@ void migrate_allocator(QGuestAllocator *src, QTAILQ_INIT(src->free); node = mlist_new(src->start, src->end - src->start); QTAILQ_INSERT_HEAD(src->free, node, MLIST_ENTNAME); - return; } diff --git a/tests/qtest/libqos/libqos.c b/tests/qtest/libqos/libqos.c index 5c0fa1f..9b49d0d 100644 --- a/tests/qtest/libqos/libqos.c +++ b/tests/qtest/libqos/libqos.c @@ -2,7 +2,7 @@ #include "../libqtest.h" #include "libqos.h" #include "pci.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" /*** Test Setup & Teardown ***/ @@ -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/meson.build b/tests/qtest/libqos/meson.build index 1b2b2db..1ddaf7b 100644 --- a/tests/qtest/libqos/meson.build +++ b/tests/qtest/libqos/meson.build @@ -32,7 +32,6 @@ libqos_srcs = files( 'i2c-omap.c', 'igb.c', 'sdhci.c', - 'tpci200.c', 'virtio.c', 'virtio-balloon.c', 'virtio-blk.c', @@ -52,7 +51,6 @@ libqos_srcs = files( # qgraph machines: 'aarch64-xlnx-zcu102-machine.c', 'arm-imx25-pdk-machine.c', - 'arm-n800-machine.c', 'arm-raspi2-machine.c', 'arm-sabrelite-machine.c', 'arm-smdkc210-machine.c', @@ -68,6 +66,13 @@ if have_virtfs libqos_srcs += files('virtio-9p.c', 'virtio-9p-client.c') endif +if config_all_devices.has_key('CONFIG_RISCV_IOMMU') + libqos_srcs += files('riscv-iommu.c') +endif +if config_all_devices.has_key('CONFIG_TPCI200') + libqos_srcs += files('tpci200.c') +endif + libqos = static_library('qos', libqos_srcs + genh, build_by_default: false) diff --git a/tests/qtest/libqos/pci-pc.c b/tests/qtest/libqos/pci-pc.c index 9604628..147009f 100644 --- a/tests/qtest/libqos/pci-pc.c +++ b/tests/qtest/libqos/pci-pc.c @@ -13,7 +13,7 @@ #include "qemu/osdep.h" #include "../libqtest.h" #include "pci-pc.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "hw/pci/pci_regs.h" #include "qemu/module.h" diff --git a/tests/qtest/libqos/pci.c b/tests/qtest/libqos/pci.c index b23d723..a59197b 100644 --- a/tests/qtest/libqos/pci.c +++ b/tests/qtest/libqos/pci.c @@ -328,8 +328,6 @@ bool qpci_msix_pending(QPCIDevice *dev, uint16_t entry) g_assert(dev->msix_enabled); pba_entry = qpci_io_readl(dev, dev->msix_pba_bar, dev->msix_pba_off + off); - qpci_io_writel(dev, dev->msix_pba_bar, dev->msix_pba_off + off, - pba_entry & ~(1 << bit_n)); return (pba_entry & (1 << bit_n)) != 0; } diff --git a/tests/qtest/libqos/qgraph.h b/tests/qtest/libqos/qgraph.h index 1b5de02..81fbfdd 100644 --- a/tests/qtest/libqos/qgraph.h +++ b/tests/qtest/libqos/qgraph.h @@ -355,7 +355,7 @@ void qos_object_start_hw(QOSGraphObject *obj); QOSGraphObject *qos_machine_new(QOSGraphNode *node, QTestState *qts); /** - * qos_machine_new(): instantiate a new driver node + * qos_driver_new(): instantiate a new driver node * @node: A driver node to be instantiated * @parent: A #QOSGraphObject to be consumed by the new driver node * @alloc: An allocator to be used by the new driver node. diff --git a/tests/qtest/libqos/qos_external.c b/tests/qtest/libqos/qos_external.c index c6bb8bf..493ab74 100644 --- a/tests/qtest/libqos/qos_external.c +++ b/tests/qtest/libqos/qos_external.c @@ -19,11 +19,11 @@ #include "qemu/osdep.h" #include <getopt.h> #include "../libqtest.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qbool.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qdict.h" +#include "qobject/qbool.h" +#include "qobject/qstring.h" #include "qemu/module.h" -#include "qapi/qmp/qlist.h" +#include "qobject/qlist.h" #include "libqos-malloc.h" #include "qgraph.h" #include "qgraph_internal.h" diff --git a/tests/qtest/libqos/riscv-iommu.c b/tests/qtest/libqos/riscv-iommu.c new file mode 100644 index 0000000..01e3b31 --- /dev/null +++ b/tests/qtest/libqos/riscv-iommu.c @@ -0,0 +1,76 @@ +/* + * libqos driver riscv-iommu-pci framework + * + * Copyright (c) 2024 Ventana Micro Systems Inc. + * + * This work is licensed under the terms of the GNU GPL, version 2 or (at your + * option) any later version. See the COPYING file in the top-level directory. + * + */ + +#include "qemu/osdep.h" +#include "../libqtest.h" +#include "qemu/module.h" +#include "qgraph.h" +#include "pci.h" +#include "riscv-iommu.h" + +static void *riscv_iommu_pci_get_driver(void *obj, const char *interface) +{ + QRISCVIOMMU *r_iommu_pci = obj; + + if (!g_strcmp0(interface, "pci-device")) { + return &r_iommu_pci->dev; + } + + fprintf(stderr, "%s not present in riscv_iommu_pci\n", interface); + g_assert_not_reached(); +} + +static void riscv_iommu_pci_start_hw(QOSGraphObject *obj) +{ + QRISCVIOMMU *pci = (QRISCVIOMMU *)obj; + qpci_device_enable(&pci->dev); +} + +static void riscv_iommu_pci_destructor(QOSGraphObject *obj) +{ + QRISCVIOMMU *pci = (QRISCVIOMMU *)obj; + qpci_iounmap(&pci->dev, pci->reg_bar); +} + +static void *riscv_iommu_pci_create(void *pci_bus, QGuestAllocator *alloc, + void *addr) +{ + QRISCVIOMMU *r_iommu_pci = g_new0(QRISCVIOMMU, 1); + QPCIBus *bus = pci_bus; + + qpci_device_init(&r_iommu_pci->dev, bus, addr); + r_iommu_pci->reg_bar = qpci_iomap(&r_iommu_pci->dev, 0, NULL); + + r_iommu_pci->obj.get_driver = riscv_iommu_pci_get_driver; + r_iommu_pci->obj.start_hw = riscv_iommu_pci_start_hw; + r_iommu_pci->obj.destructor = riscv_iommu_pci_destructor; + return &r_iommu_pci->obj; +} + +static void riscv_iommu_pci_register_nodes(void) +{ + QPCIAddress addr = { + .vendor_id = RISCV_IOMMU_PCI_VENDOR_ID, + .device_id = RISCV_IOMMU_PCI_DEVICE_ID, + .devfn = QPCI_DEVFN(1, 0), + }; + + QOSGraphEdgeOptions opts = { + .extra_device_opts = "addr=01.0", + }; + + add_qpci_address(&opts, &addr); + + qos_node_create_driver("riscv-iommu-pci", riscv_iommu_pci_create); + qos_node_produces("riscv-iommu-pci", "pci-device"); + qos_node_consumes("riscv-iommu-pci", "pci-bus", &opts); +} + +libqos_init(riscv_iommu_pci_register_nodes); diff --git a/tests/qtest/libqos/riscv-iommu.h b/tests/qtest/libqos/riscv-iommu.h new file mode 100644 index 0000000..318db13 --- /dev/null +++ b/tests/qtest/libqos/riscv-iommu.h @@ -0,0 +1,101 @@ +/* + * libqos driver riscv-iommu-pci framework + * + * Copyright (c) 2024 Ventana Micro Systems Inc. + * + * This work is licensed under the terms of the GNU GPL, version 2 or (at your + * option) any later version. See the COPYING file in the top-level directory. + * + */ + +#ifndef TESTS_LIBQOS_RISCV_IOMMU_H +#define TESTS_LIBQOS_RISCV_IOMMU_H + +#include "qgraph.h" +#include "pci.h" +#include "qemu/bitops.h" + +#ifndef GENMASK_ULL +#define GENMASK_ULL(h, l) (((~0ULL) >> (63 - (h) + (l))) << (l)) +#endif + +/* + * RISC-V IOMMU uses PCI_VENDOR_ID_REDHAT 0x1b36 and + * PCI_DEVICE_ID_REDHAT_RISCV_IOMMU 0x0014. + */ +#define RISCV_IOMMU_PCI_VENDOR_ID 0x1b36 +#define RISCV_IOMMU_PCI_DEVICE_ID 0x0014 +#define RISCV_IOMMU_PCI_DEVICE_CLASS 0x0806 + +/* Common field positions */ +#define RISCV_IOMMU_QUEUE_ENABLE BIT(0) +#define RISCV_IOMMU_QUEUE_INTR_ENABLE BIT(1) +#define RISCV_IOMMU_QUEUE_MEM_FAULT BIT(8) +#define RISCV_IOMMU_QUEUE_ACTIVE BIT(16) +#define RISCV_IOMMU_QUEUE_BUSY BIT(17) + +#define RISCV_IOMMU_REG_CAP 0x0000 +#define RISCV_IOMMU_CAP_VERSION GENMASK_ULL(7, 0) + +#define RISCV_IOMMU_REG_DDTP 0x0010 +#define RISCV_IOMMU_DDTP_BUSY BIT_ULL(4) +#define RISCV_IOMMU_DDTP_MODE GENMASK_ULL(3, 0) +#define RISCV_IOMMU_DDTP_MODE_OFF 0 + +#define RISCV_IOMMU_REG_CQCSR 0x0048 +#define RISCV_IOMMU_CQCSR_CQEN RISCV_IOMMU_QUEUE_ENABLE +#define RISCV_IOMMU_CQCSR_CIE RISCV_IOMMU_QUEUE_INTR_ENABLE +#define RISCV_IOMMU_CQCSR_CQON RISCV_IOMMU_QUEUE_ACTIVE +#define RISCV_IOMMU_CQCSR_BUSY RISCV_IOMMU_QUEUE_BUSY + +#define RISCV_IOMMU_REG_FQCSR 0x004C +#define RISCV_IOMMU_FQCSR_FQEN RISCV_IOMMU_QUEUE_ENABLE +#define RISCV_IOMMU_FQCSR_FIE RISCV_IOMMU_QUEUE_INTR_ENABLE +#define RISCV_IOMMU_FQCSR_FQON RISCV_IOMMU_QUEUE_ACTIVE +#define RISCV_IOMMU_FQCSR_BUSY RISCV_IOMMU_QUEUE_BUSY + +#define RISCV_IOMMU_REG_PQCSR 0x0050 +#define RISCV_IOMMU_PQCSR_PQEN RISCV_IOMMU_QUEUE_ENABLE +#define RISCV_IOMMU_PQCSR_PIE RISCV_IOMMU_QUEUE_INTR_ENABLE +#define RISCV_IOMMU_PQCSR_PQON RISCV_IOMMU_QUEUE_ACTIVE +#define RISCV_IOMMU_PQCSR_BUSY RISCV_IOMMU_QUEUE_BUSY + +#define RISCV_IOMMU_REG_IPSR 0x0054 + +#define RISCV_IOMMU_REG_IVEC 0x02F8 +#define RISCV_IOMMU_REG_IVEC_CIV GENMASK_ULL(3, 0) +#define RISCV_IOMMU_REG_IVEC_FIV GENMASK_ULL(7, 4) +#define RISCV_IOMMU_REG_IVEC_PMIV GENMASK_ULL(11, 8) +#define RISCV_IOMMU_REG_IVEC_PIV GENMASK_ULL(15, 12) + +#define RISCV_IOMMU_REG_CQB 0x0018 +#define RISCV_IOMMU_CQB_PPN_START 10 +#define RISCV_IOMMU_CQB_PPN_LEN 44 +#define RISCV_IOMMU_CQB_LOG2SZ_START 0 +#define RISCV_IOMMU_CQB_LOG2SZ_LEN 5 + +#define RISCV_IOMMU_REG_CQT 0x0024 + +#define RISCV_IOMMU_REG_FQB 0x0028 +#define RISCV_IOMMU_FQB_PPN_START 10 +#define RISCV_IOMMU_FQB_PPN_LEN 44 +#define RISCV_IOMMU_FQB_LOG2SZ_START 0 +#define RISCV_IOMMU_FQB_LOG2SZ_LEN 5 + +#define RISCV_IOMMU_REG_FQT 0x0034 + +#define RISCV_IOMMU_REG_PQB 0x0038 +#define RISCV_IOMMU_PQB_PPN_START 10 +#define RISCV_IOMMU_PQB_PPN_LEN 44 +#define RISCV_IOMMU_PQB_LOG2SZ_START 0 +#define RISCV_IOMMU_PQB_LOG2SZ_LEN 5 + +#define RISCV_IOMMU_REG_PQT 0x0044 + +typedef struct QRISCVIOMMU { + QOSGraphObject obj; + QPCIDevice dev; + QPCIBar reg_bar; +} QRISCVIOMMU; + +#endif diff --git a/tests/qtest/libqos/virtio-9p-client.c b/tests/qtest/libqos/virtio-9p-client.c index b8adc8d..6ab4501 100644 --- a/tests/qtest/libqos/virtio-9p-client.c +++ b/tests/qtest/libqos/virtio-9p-client.c @@ -235,10 +235,11 @@ static const char *rmessage_name(uint8_t id) id == P9_RMKDIR ? "RMKDIR" : id == P9_RLCREATE ? "RLCREATE" : id == P9_RSYMLINK ? "RSYMLINK" : + id == P9_RGETATTR ? "RGETATTR" : id == P9_RLINK ? "RLINK" : id == P9_RUNLINKAT ? "RUNLINKAT" : id == P9_RFLUSH ? "RFLUSH" : - id == P9_RREADDIR ? "READDIR" : + id == P9_RREADDIR ? "RREADDIR" : "<unknown>"; } @@ -556,6 +557,55 @@ void v9fs_rgetattr(P9Req *req, v9fs_attr *attr) v9fs_req_free(req); } +/* + * size[4] Tsetattr tag[2] fid[4] valid[4] mode[4] uid[4] gid[4] size[8] + * atime_sec[8] atime_nsec[8] mtime_sec[8] mtime_nsec[8] + */ +TSetAttrRes v9fs_tsetattr(TSetAttrOpt opt) +{ + P9Req *req; + uint32_t err; + + g_assert(opt.client); + + req = v9fs_req_init( + opt.client, 4/*fid*/ + 4/*valid*/ + 4/*mode*/ + 4/*uid*/ + 4/*gid*/ + + 8/*size*/ + 8/*atime_sec*/ + 8/*atime_nsec*/ + 8/*mtime_sec*/ + + 8/*mtime_nsec*/, P9_TSETATTR, opt.tag + ); + v9fs_uint32_write(req, opt.fid); + v9fs_uint32_write(req, (uint32_t) opt.attr.valid); + v9fs_uint32_write(req, opt.attr.mode); + v9fs_uint32_write(req, opt.attr.uid); + v9fs_uint32_write(req, opt.attr.gid); + v9fs_uint64_write(req, opt.attr.size); + v9fs_uint64_write(req, opt.attr.atime_sec); + v9fs_uint64_write(req, opt.attr.atime_nsec); + v9fs_uint64_write(req, opt.attr.mtime_sec); + v9fs_uint64_write(req, opt.attr.mtime_nsec); + v9fs_req_send(req); + + if (!opt.requestOnly) { + v9fs_req_wait_for_reply(req, NULL); + if (opt.expectErr) { + v9fs_rlerror(req, &err); + g_assert_cmpint(err, ==, opt.expectErr); + } else { + v9fs_rsetattr(req); + } + req = NULL; /* request was freed */ + } + + return (TSetAttrRes) { .req = req }; +} + +/* size[4] Rsetattr tag[2] */ +void v9fs_rsetattr(P9Req *req) +{ + v9fs_req_recv(req, P9_RSETATTR); + v9fs_req_free(req); +} + /* size[4] Treaddir tag[2] fid[4] offset[8] count[4] */ TReadDirRes v9fs_treaddir(TReadDirOpt opt) { diff --git a/tests/qtest/libqos/virtio-9p-client.h b/tests/qtest/libqos/virtio-9p-client.h index 78228eb..e3221a3 100644 --- a/tests/qtest/libqos/virtio-9p-client.h +++ b/tests/qtest/libqos/virtio-9p-client.h @@ -65,6 +65,16 @@ typedef struct v9fs_attr { #define P9_GETATTR_BASIC 0x000007ffULL /* Mask for fields up to BLOCKS */ #define P9_GETATTR_ALL 0x00003fffULL /* Mask for ALL fields */ +#define P9_SETATTR_MODE 0x00000001UL +#define P9_SETATTR_UID 0x00000002UL +#define P9_SETATTR_GID 0x00000004UL +#define P9_SETATTR_SIZE 0x00000008UL +#define P9_SETATTR_ATIME 0x00000010UL +#define P9_SETATTR_MTIME 0x00000020UL +#define P9_SETATTR_CTIME 0x00000040UL +#define P9_SETATTR_ATIME_SET 0x00000080UL +#define P9_SETATTR_MTIME_SET 0x00000100UL + struct V9fsDirent { v9fs_qid qid; uint64_t offset; @@ -182,6 +192,28 @@ typedef struct TGetAttrRes { P9Req *req; } TGetAttrRes; +/* options for 'Tsetattr' 9p request */ +typedef struct TSetAttrOpt { + /* 9P client being used (mandatory) */ + QVirtio9P *client; + /* user supplied tag number being returned with response (optional) */ + uint16_t tag; + /* file ID of file/dir whose attributes shall be modified (required) */ + uint32_t fid; + /* new attribute values to be set by 9p server */ + v9fs_attr attr; + /* only send Tsetattr request but not wait for a reply? (optional) */ + bool requestOnly; + /* do we expect an Rlerror response, if yes which error code? (optional) */ + uint32_t expectErr; +} TSetAttrOpt; + +/* result of 'Tsetattr' 9p request */ +typedef struct TSetAttrRes { + /* if requestOnly was set: request object for further processing */ + P9Req *req; +} TSetAttrRes; + /* options for 'Treaddir' 9p request */ typedef struct TReadDirOpt { /* 9P client being used (mandatory) */ @@ -470,6 +502,8 @@ TWalkRes v9fs_twalk(TWalkOpt opt); void v9fs_rwalk(P9Req *req, uint16_t *nwqid, v9fs_qid **wqid); TGetAttrRes v9fs_tgetattr(TGetAttrOpt); void v9fs_rgetattr(P9Req *req, v9fs_attr *attr); +TSetAttrRes v9fs_tsetattr(TSetAttrOpt opt); +void v9fs_rsetattr(P9Req *req); TReadDirRes v9fs_treaddir(TReadDirOpt); void v9fs_rreaddir(P9Req *req, uint32_t *count, uint32_t *nentries, struct V9fsDirent **entries); diff --git a/tests/qtest/libqos/virtio-pci-modern.c b/tests/qtest/libqos/virtio-pci-modern.c index 18d1188..4e67fcb 100644 --- a/tests/qtest/libqos/virtio-pci-modern.c +++ b/tests/qtest/libqos/virtio-pci-modern.c @@ -173,13 +173,11 @@ static bool get_config_isr_status(QVirtioDevice *d) static void wait_config_isr_status(QVirtioDevice *d, gint64 timeout_us) { - QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev); gint64 start_time = g_get_monotonic_time(); - do { + while (!get_config_isr_status(d)) { g_assert(g_get_monotonic_time() - start_time <= timeout_us); - qtest_clock_step(dev->pdev->bus->qts, 100); - } while (!get_config_isr_status(d)); + } } static void queue_select(QVirtioDevice *d, uint16_t index) diff --git a/tests/qtest/libqos/virtio-pci.c b/tests/qtest/libqos/virtio-pci.c index 485b8f6..002bf8b 100644 --- a/tests/qtest/libqos/virtio-pci.c +++ b/tests/qtest/libqos/virtio-pci.c @@ -171,13 +171,11 @@ static bool qvirtio_pci_get_config_isr_status(QVirtioDevice *d) static void qvirtio_pci_wait_config_isr_status(QVirtioDevice *d, gint64 timeout_us) { - QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev); gint64 start_time = g_get_monotonic_time(); - do { + while (!qvirtio_pci_get_config_isr_status(d)) { g_assert(g_get_monotonic_time() - start_time <= timeout_us); - qtest_clock_step(dev->pdev->bus->qts, 100); - } while (!qvirtio_pci_get_config_isr_status(d)); + } } static void qvirtio_pci_queue_select(QVirtioDevice *d, uint16_t index) diff --git a/tests/qtest/libqos/virtio-scmi.c b/tests/qtest/libqos/virtio-scmi.c index ce8f4d5..6b5bd4d 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/libqos/virtio.c b/tests/qtest/libqos/virtio.c index a21b6ee..5a709d0 100644 --- a/tests/qtest/libqos/virtio.c +++ b/tests/qtest/libqos/virtio.c @@ -25,49 +25,63 @@ */ static uint16_t qvirtio_readw(QVirtioDevice *d, QTestState *qts, uint64_t addr) { - uint16_t val = qtest_readw(qts, addr); + uint16_t val; - if (d->features & (1ull << VIRTIO_F_VERSION_1) && qtest_big_endian(qts)) { - val = bswap16(val); + if (d->features & (1ull << VIRTIO_F_VERSION_1)) { + qtest_memread(qts, addr, &val, sizeof(val)); + val = le16_to_cpu(val); + } else { + val = qtest_readw(qts, addr); } + return val; } static uint32_t qvirtio_readl(QVirtioDevice *d, QTestState *qts, uint64_t addr) { - uint32_t val = qtest_readl(qts, addr); + uint32_t val; - if (d->features & (1ull << VIRTIO_F_VERSION_1) && qtest_big_endian(qts)) { - val = bswap32(val); + if (d->features & (1ull << VIRTIO_F_VERSION_1)) { + qtest_memread(qts, addr, &val, sizeof(val)); + val = le32_to_cpu(val); + } else { + val = qtest_readl(qts, addr); } + return val; } static void qvirtio_writew(QVirtioDevice *d, QTestState *qts, uint64_t addr, uint16_t val) { - if (d->features & (1ull << VIRTIO_F_VERSION_1) && qtest_big_endian(qts)) { - val = bswap16(val); + if (d->features & (1ull << VIRTIO_F_VERSION_1)) { + val = cpu_to_le16(val); + qtest_memwrite(qts, addr, &val, sizeof(val)); + } else { + qtest_writew(qts, addr, val); } - qtest_writew(qts, addr, val); } static void qvirtio_writel(QVirtioDevice *d, QTestState *qts, uint64_t addr, uint32_t val) { - if (d->features & (1ull << VIRTIO_F_VERSION_1) && qtest_big_endian(qts)) { - val = bswap32(val); + if (d->features & (1ull << VIRTIO_F_VERSION_1)) { + val = cpu_to_le32(val); + qtest_memwrite(qts, addr, &val, sizeof(val)); + } else { + qtest_writel(qts, addr, val); } - qtest_writel(qts, addr, val); } static void qvirtio_writeq(QVirtioDevice *d, QTestState *qts, uint64_t addr, uint64_t val) { - if (d->features & (1ull << VIRTIO_F_VERSION_1) && qtest_big_endian(qts)) { - val = bswap64(val); + if (d->features & (1ull << VIRTIO_F_VERSION_1)) { + val = cpu_to_le64(val); + qtest_memwrite(qts, addr, &val, sizeof(val)); + } else { + qtest_writeq(qts, addr, val); } - qtest_writeq(qts, addr, val); } uint8_t qvirtio_config_readb(QVirtioDevice *d, uint64_t addr) @@ -170,7 +184,6 @@ void qvirtio_wait_queue_isr(QTestState *qts, QVirtioDevice *d, gint64 start_time = g_get_monotonic_time(); for (;;) { - qtest_clock_step(qts, 100); if (d->bus->get_queue_isr_status(d, vq)) { return; } @@ -192,7 +205,6 @@ uint8_t qvirtio_wait_status_byte_no_isr(QTestState *qts, QVirtioDevice *d, uint8_t val; while ((val = qtest_readb(qts, addr)) == 0xff) { - qtest_clock_step(qts, 100); g_assert(!d->bus->get_queue_isr_status(d, vq)); g_assert(g_get_monotonic_time() - start_time <= timeout_us); } @@ -219,14 +231,12 @@ void qvirtio_wait_used_elem(QTestState *qts, QVirtioDevice *d, for (;;) { uint32_t got_desc_idx; - qtest_clock_step(qts, 100); if (d->bus->get_queue_isr_status(d, vq) && qvirtqueue_get_buf(qts, vq, &got_desc_idx, len)) { g_assert_cmpint(got_desc_idx, ==, desc_idx); return; } - g_assert(g_get_monotonic_time() - start_time <= timeout_us); } } diff --git a/tests/qtest/libqtest.c b/tests/qtest/libqtest.c index 9d07de1..94526b7 100644 --- a/tests/qtest/libqtest.c +++ b/tests/qtest/libqtest.c @@ -30,14 +30,15 @@ #include "libqtest.h" #include "libqmp.h" +#include "qemu/accel.h" #include "qemu/ctype.h" #include "qemu/cutils.h" #include "qemu/sockets.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qjson.h" -#include "qapi/qmp/qlist.h" -#include "qapi/qmp/qstring.h" -#include "qapi/qmp/qbool.h" +#include "qobject/qdict.h" +#include "qobject/qjson.h" +#include "qobject/qlist.h" +#include "qobject/qstring.h" +#include "qobject/qbool.h" #define MAX_IRQ 256 @@ -75,6 +76,8 @@ struct QTestState { int fd; int qmp_fd; + int sock; + int qmpsock; pid_t qemu_pid; /* our child QEMU process */ int wstatus; #ifdef _WIN32 @@ -215,6 +218,22 @@ static void qtest_check_status(QTestState *s) #endif } +void qtest_system_reset_nowait(QTestState *s) +{ + /* Request the system reset, but do not wait for it to complete */ + qtest_qmp_assert_success(s, "{'execute': 'system_reset' }"); +} + +void qtest_system_reset(QTestState *s) +{ + qtest_system_reset_nowait(s); + /* + * Wait for the RESET event, which is sent once the system reset + * has actually completed. + */ + qtest_qmp_eventwait(s, "RESET"); +} + void qtest_wait_qemu(QTestState *s) { if (s->qemu_pid != -1) { @@ -442,18 +461,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 @@ -485,22 +505,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. * @@ -515,34 +532,69 @@ 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(const char *var, const char *extra_args) +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); - qobject_unref(qtest_qmp(s, "{ 'execute': 'qmp_capabilities' }")); + if (capabilities) { + qtest_qmp_assert_success(s, + "{ 'execute': 'qmp_capabilities', " + "'arguments': { 'enable': %p } }", + qobject_ref(capabilities)); + } else { + qtest_qmp_assert_success(s, "{ 'execute': 'qmp_capabilities' }"); + } +} + +QTestState *qtest_init_ext(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(const char *extra_args) { - return qtest_init_with_env(NULL, extra_args); + return qtest_init_ext(NULL, extra_args, NULL, true); } QTestState *qtest_vinitf(const char *fmt, va_list ap) @@ -752,6 +804,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); } @@ -779,12 +832,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); } @@ -845,6 +900,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); @@ -948,15 +1004,62 @@ const char *qtest_get_arch(void) return end + 1; } +static bool qtest_qom_has_concrete_type(const char *parent_typename, + const char *child_typename, + QList **cached_list) +{ + QList *list = cached_list ? *cached_list : NULL; + const QListEntry *p; + QObject *qobj; + QString *qstr; + QDict *devinfo; + int idx; + + if (!list) { + QDict *resp; + QDict *args; + QTestState *qts = qtest_init("-machine none"); + + args = qdict_new(); + qdict_put_bool(args, "abstract", false); + qdict_put_str(args, "implements", parent_typename); + + resp = qtest_qmp(qts, "{'execute': 'qom-list-types', 'arguments': %p }", + args); + g_assert(qdict_haskey(resp, "return")); + list = qdict_get_qlist(resp, "return"); + qobject_ref(list); + qobject_unref(resp); + + qtest_quit(qts); + + if (cached_list) { + *cached_list = list; + } + } + + for (p = qlist_first(list), idx = 0; p; p = qlist_next(p), idx++) { + devinfo = qobject_to(QDict, qlist_entry_obj(p)); + g_assert(devinfo); + + qobj = qdict_get(devinfo, "name"); + g_assert(qobj); + qstr = qobject_to(QString, qobj); + g_assert(qstr); + if (g_str_equal(qstring_get_str(qstr), child_typename)) { + return true; + } + } + + return false; +} + bool qtest_has_accel(const char *accel_name) { - if (g_str_equal(accel_name, "tcg")) { -#if defined(CONFIG_TCG) - return true; -#else - return false; -#endif - } else if (g_str_equal(accel_name, "kvm")) { + static QList *list; + g_autofree char *accel_type = NULL; + + if (g_str_equal(accel_name, "kvm")) { int i; const char *arch = qtest_get_arch(); const char *targets[] = { CONFIG_KVM_TARGETS }; @@ -968,11 +1071,12 @@ bool qtest_has_accel(const char *accel_name) } } } - } else { - /* not implemented */ - g_assert_not_reached(); + return false; } - return false; + + accel_type = g_strconcat(accel_name, ACCEL_CLASS_SUFFIX, NULL); + + return qtest_qom_has_concrete_type("accel", accel_type, &list); } bool qtest_get_irq(QTestState *s, int num) @@ -1202,6 +1306,33 @@ uint64_t qtest_rtas_call(QTestState *s, const char *name, return 0; } +static void qtest_rsp_csr(QTestState *s, uint64_t *val) +{ + gchar **args; + uint64_t ret; + int rc; + + args = qtest_rsp_args(s, 3); + + rc = qemu_strtou64(args[1], NULL, 16, &ret); + g_assert(rc == 0); + rc = qemu_strtou64(args[2], NULL, 16, val); + g_assert(rc == 0); + + g_strfreev(args); +} + +uint64_t qtest_csr_call(QTestState *s, const char *name, + uint64_t cpu, int csr, + uint64_t *val) +{ + qtest_sendf(s, "csr %s 0x%"PRIx64" %d 0x%"PRIx64"\n", + name, cpu, csr, *val); + + qtest_rsp_csr(s, val); + return 0; +} + void qtest_add_func(const char *str, void (*fn)(void)) { gchar *path = g_strdup_printf("/%s/%s", qtest_get_arch(), str); @@ -1523,7 +1654,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_ext(qemu_var, "-machine none", NULL, true); response = qtest_qmp(qts, "{ 'execute': 'query-machines' }"); g_assert(response); list = qdict_get_qlist(response, "return"); @@ -1578,7 +1709,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_ext(NULL, "-machine none", NULL, true); response = qtest_qmp(qts, "{ 'execute': 'query-cpu-definitions' }"); g_assert(response); list = qdict_get_qlist(response, "return"); @@ -1648,7 +1779,9 @@ void qtest_cb_for_every_machine(void (*cb)(const char *machine), /* Ignore machines that cannot be used for qtests */ if (!strncmp("xenfv", machines[i].name, 5) || g_str_equal("xenpv", machines[i].name) || - g_str_equal("xenpvh", machines[i].name)) { + g_str_equal("xenpvh", machines[i].name) || + g_str_equal("vmapple", machines[i].name) || + g_str_equal("nitro-enclave", machines[i].name)) { continue; } if (!skip_old_versioned || @@ -1699,45 +1832,8 @@ bool qtest_has_machine(const char *machine) bool qtest_has_device(const char *device) { static QList *list; - const QListEntry *p; - QObject *qobj; - QString *qstr; - QDict *devinfo; - int idx; - if (!list) { - QDict *resp; - QDict *args; - QTestState *qts = qtest_init("-machine none"); - - args = qdict_new(); - qdict_put_bool(args, "abstract", false); - qdict_put_str(args, "implements", "device"); - - resp = qtest_qmp(qts, "{'execute': 'qom-list-types', 'arguments': %p }", - args); - g_assert(qdict_haskey(resp, "return")); - list = qdict_get_qlist(resp, "return"); - qobject_ref(list); - qobject_unref(resp); - - qtest_quit(qts); - } - - for (p = qlist_first(list), idx = 0; p; p = qlist_next(p), idx++) { - devinfo = qobject_to(QDict, qlist_entry_obj(p)); - g_assert(devinfo); - - qobj = qdict_get(devinfo, "name"); - g_assert(qobj); - qstr = qobject_to(QString, qobj); - g_assert(qstr); - if (g_str_equal(qstring_get_str(qstr), device)) { - return true; - } - } - - return false; + return qtest_qom_has_concrete_type("device", device, &list); } /* @@ -1918,7 +2014,6 @@ void qtest_client_inproc_recv(void *opaque, const char *str) qts->rx = g_string_new(NULL); } g_string_append(qts->rx, str); - return; } void qtest_qom_set_bool(QTestState *s, const char *path, const char *property, diff --git a/tests/qtest/libqtest.h b/tests/qtest/libqtest.h index beb96b1..b3f2e7f 100644 --- a/tests/qtest/libqtest.h +++ b/tests/qtest/libqtest.h @@ -17,8 +17,9 @@ #ifndef LIBQTEST_H #define LIBQTEST_H -#include "qapi/qmp/qobject.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qobject.h" +#include "qobject/qdict.h" +#include "qobject/qlist.h" #include "libqmp.h" typedef struct QTestState QTestState; @@ -56,17 +57,21 @@ QTestState *qtest_vinitf(const char *fmt, va_list ap) G_GNUC_PRINTF(1, 0); QTestState *qtest_init(const char *extra_args); /** - * qtest_init_with_env: + * qtest_init_ext: * @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. + * @capabilities: list of QMP capabilities (strings) to enable + * @do_connect: connect to qemu monitor and qtest socket. * * Like qtest_init(), but use a different environment variable for the - * QEMU binary. + * QEMU binary, allow specify capabilities and skip connecting + * to QEMU monitor. * * Returns: #QTestState instance. */ -QTestState *qtest_init_with_env(const char *var, const char *extra_args); +QTestState *qtest_init_ext(const char *var, const char *extra_args, + QList *capabilities, bool do_connect); /** * qtest_init_without_qmp_handshake: @@ -78,6 +83,22 @@ QTestState *qtest_init_with_env(const char *var, const char *extra_args); 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_ext. 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. @@ -89,6 +110,31 @@ QTestState *qtest_init_without_qmp_handshake(const char *extra_args); QTestState *qtest_init_with_serial(const char *extra_args, int *sock_fd); /** + * qtest_system_reset: + * @s: #QTestState instance to operate on. + * + * Send a "system_reset" command to the QEMU under test, and wait for + * the reset to complete before returning. + */ +void qtest_system_reset(QTestState *s); + +/** + * qtest_system_reset_nowait: + * @s: #QTestState instance to operate on. + * + * Send a "system_reset" command to the QEMU under test, but do not + * wait for the reset to complete before returning. The caller is + * responsible for waiting for either the RESET event or some other + * event of interest to them before proceeding. + * + * This function should only be used if you're specifically testing + * for some other event; in that case you can't use qtest_system_reset() + * because it will read and discard any other QMP events that arrive + * before the RESET event. + */ +void qtest_system_reset_nowait(QTestState *s); + +/** * qtest_wait_qemu: * @s: #QTestState instance to operate on. * @@ -340,7 +386,7 @@ QDict *qtest_qmp_event_ref(QTestState *s, const char *event); char *qtest_hmp(QTestState *s, const char *fmt, ...) G_GNUC_PRINTF(2, 3); /** - * qtest_hmpv: + * qtest_vhmp: * @s: #QTestState instance to operate on. * @fmt: HMP command to send to QEMU, formats arguments like vsprintf(). * @ap: HMP command arguments @@ -576,6 +622,20 @@ uint64_t qtest_rtas_call(QTestState *s, const char *name, uint32_t nret, uint64_t ret); /** + * qtest_csr_call: + * @s: #QTestState instance to operate on. + * @name: name of the command to call. + * @cpu: hart number. + * @csr: CSR number. + * @val: Value for reading/writing. + * + * Call an RISC-V CSR read/write function + */ +uint64_t qtest_csr_call(QTestState *s, const char *name, + uint64_t cpu, int csr, + uint64_t *val); + +/** * qtest_bufread: * @s: #QTestState instance to operate on. * @addr: Guest address to read from. @@ -879,7 +939,7 @@ void qtest_qmp_assert_success(QTestState *qts, const char *fmt, ...) #ifndef _WIN32 /** - * qtest_qmp_fd_assert_success_ref: + * qtest_qmp_fds_assert_success_ref: * @qts: QTestState instance to operate on * @fds: the file descriptors to send * @nfds: number of @fds to send @@ -896,7 +956,7 @@ QDict *qtest_qmp_fds_assert_success_ref(QTestState *qts, int *fds, size_t nfds, G_GNUC_PRINTF(4, 5); /** - * qtest_qmp_fd_assert_success: + * qtest_qmp_fds_assert_success: * @qts: QTestState instance to operate on * @fds: the file descriptors to send * @nfds: number of @fds to send diff --git a/tests/qtest/lsm303dlhc-mag-test.c b/tests/qtest/lsm303dlhc-mag-test.c index 0f64e7f..55ef459 100644 --- a/tests/qtest/lsm303dlhc-mag-test.c +++ b/tests/qtest/lsm303dlhc-mag-test.c @@ -13,7 +13,7 @@ #include "libqtest-single.h" #include "libqos/qgraph.h" #include "libqos/i2c.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #define LSM303DLHC_MAG_TEST_ID "lsm303dlhc_mag-test" #define LSM303DLHC_MAG_REG_CRA 0x00 diff --git a/tests/qtest/m48t59-test.c b/tests/qtest/m48t59-test.c index 605797a..1e39a0e 100644 --- a/tests/qtest/m48t59-test.c +++ b/tests/qtest/m48t59-test.c @@ -247,11 +247,6 @@ static void base_setup(void) base_year = 1968; base_machine = "SS-5"; use_mmio = true; - } else if (g_str_equal(arch, "ppc") || g_str_equal(arch, "ppc64")) { - base = 0xF0000000; - base_year = 1968; - base_machine = "ref405ep"; - use_mmio = true; } else { g_assert_not_reached(); } diff --git a/tests/qtest/machine-none-test.c b/tests/qtest/machine-none-test.c index 05da7bc..b6a87d2 100644 --- a/tests/qtest/machine-none-test.c +++ b/tests/qtest/machine-none-test.c @@ -14,7 +14,7 @@ #include "qemu/cutils.h" #include "libqtest.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" struct arch2cpu { @@ -30,7 +30,6 @@ static struct arch2cpu cpus_map[] = { { "x86_64", "qemu64,apic-id=0" }, { "i386", "qemu32,apic-id=0" }, { "alpha", "ev67" }, - { "cris", "crisv32" }, { "m68k", "m5206" }, { "microblaze", "any" }, { "microblazeel", "any" }, diff --git a/tests/qtest/max34451-test.c b/tests/qtest/max34451-test.c index dbf6ddc..5e0878c 100644 --- a/tests/qtest/max34451-test.c +++ b/tests/qtest/max34451-test.c @@ -11,8 +11,8 @@ #include "libqtest-single.h" #include "libqos/qgraph.h" #include "libqos/i2c.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qnum.h" +#include "qobject/qdict.h" +#include "qobject/qnum.h" #include "qemu/bitops.h" #define TEST_ID "max34451-test" diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index fc852f3..8ad8490 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -4,9 +4,11 @@ slow_qtests = { 'bios-tables-test' : 910, 'cdrom-test' : 610, 'device-introspect-test' : 720, + 'ide-test' : 120, 'migration-test' : 480, 'npcm7xx_pwm-test': 300, 'npcm7xx_watchdog_timer-test': 120, + 'qmp-cmd-test' : 120, 'qom-test' : 900, 'stm32l4x5_usart-test' : 600, 'test-hmp' : 240, @@ -50,7 +52,16 @@ qtests_filter = \ qtests_i386 = \ (slirp.found() ? ['pxe-test'] : []) + \ qtests_filter + \ - (have_tools ? ['ahci-test'] : []) + \ + (config_all_devices.has_key('CONFIG_ACPI_VMGENID') ? ['vmgenid-test'] : []) + \ + (config_all_devices.has_key('CONFIG_AHCI_ICH9') and have_tools ? ['ahci-test'] : []) + \ + (config_all_devices.has_key('CONFIG_AHCI_ICH9') ? ['tco-test'] : []) + \ + (config_all_devices.has_key('CONFIG_FDC_ISA') ? ['fdc-test'] : []) + \ + (config_all_devices.has_key('CONFIG_I440FX') ? ['fw_cfg-test'] : []) + \ + (config_all_devices.has_key('CONFIG_FW_CFG_DMA') ? ['vmcoreinfo-test'] : []) + \ + (config_all_devices.has_key('CONFIG_I440FX') ? ['i440fx-test'] : []) + \ + (config_all_devices.has_key('CONFIG_I440FX') ? ['ide-test'] : []) + \ + (config_all_devices.has_key('CONFIG_I440FX') ? ['numa-test'] : []) + \ + (config_all_devices.has_key('CONFIG_I440FX') ? ['test-x86-cpuid-compat'] : []) + \ (config_all_devices.has_key('CONFIG_ISA_TESTDEV') ? ['endianness-test'] : []) + \ (config_all_devices.has_key('CONFIG_SGA') ? ['boot-serial-test'] : []) + \ (config_all_devices.has_key('CONFIG_ISA_IPMI_KCS') ? ['ipmi-kcs-test'] : []) + \ @@ -64,6 +75,7 @@ qtests_i386 = \ (config_all_devices.has_key('CONFIG_I82801B11') ? ['i82801b11-test'] : []) + \ (config_all_devices.has_key('CONFIG_IOH3420') ? ['ioh3420-test'] : []) + \ (config_all_devices.has_key('CONFIG_LPC_ICH9') ? ['lpc-ich9-test'] : []) + \ + (config_all_devices.has_key('CONFIG_MC146818RTC') ? ['rtc-test'] : []) + \ (config_all_devices.has_key('CONFIG_USB_UHCI') ? ['usb-hcd-uhci-test'] : []) + \ (config_all_devices.has_key('CONFIG_USB_UHCI') and \ config_all_devices.has_key('CONFIG_USB_EHCI') ? ['usb-hcd-ehci-test'] : []) + \ @@ -77,9 +89,12 @@ qtests_i386 = \ (config_all_devices.has_key('CONFIG_MEGASAS_SCSI_PCI') ? ['fuzz-megasas-test'] : []) + \ (config_all_devices.has_key('CONFIG_LSI_SCSI_PCI') ? ['fuzz-lsi53c895a-test'] : []) + \ (config_all_devices.has_key('CONFIG_VIRTIO_SCSI') ? ['fuzz-virtio-scsi-test'] : []) + \ + (config_all_devices.has_key('CONFIG_VIRTIO_BALLOON') ? ['virtio-balloon-test'] : []) + \ + (config_all_devices.has_key('CONFIG_Q35') ? ['q35-test'] : []) + \ (config_all_devices.has_key('CONFIG_SB16') ? ['fuzz-sb16-test'] : []) + \ (config_all_devices.has_key('CONFIG_SDHCI_PCI') ? ['fuzz-sdcard-test'] : []) + \ (config_all_devices.has_key('CONFIG_ESP_PCI') ? ['am53c974-test'] : []) + \ + (config_all_devices.has_key('CONFIG_VTD') ? ['intel-iommu-test'] : []) + \ (host_os != 'windows' and \ config_all_devices.has_key('CONFIG_ACPI_ERST') ? ['erst-test'] : []) + \ (config_all_devices.has_key('CONFIG_PCIE_PORT') and \ @@ -92,25 +107,16 @@ qtests_i386 = \ config_all_devices.has_key('CONFIG_PARALLEL') ? ['bios-tables-test'] : []) + \ qtests_pci + \ qtests_cxl + \ - ['fdc-test', - 'ide-test', + [ 'hd-geo-test', 'boot-order-test', - 'rtc-test', - 'i440fx-test', - 'fw_cfg-test', 'device-plug-test', 'drive_del-test', - 'tco-test', 'cpu-plug-test', - 'q35-test', - 'vmgenid-test', 'migration-test', - 'test-x86-cpuid-compat', - 'numa-test' ] -if dbus_display +if dbus_display and config_all_devices.has_key('CONFIG_VGA') qtests_i386 += ['dbus-display-test'] endif @@ -136,12 +142,14 @@ qtests_alpha = ['boot-serial-test'] + \ qtests_avr = [ 'boot-serial-test' ] -qtests_hppa = ['boot-serial-test'] + \ +qtests_hppa = \ qtests_filter + \ (config_all_devices.has_key('CONFIG_VGA') ? ['display-vga-test'] : []) qtests_loongarch64 = qtests_filter + \ - ['boot-serial-test', 'numa-test'] + (config_all_devices.has_key('CONFIG_LOONGARCH_VIRT') ? ['numa-test'] : []) + \ + ['boot-serial-test', + 'cpu-plug-test'] qtests_m68k = ['boot-serial-test'] + \ qtests_filter @@ -163,7 +171,6 @@ qtests_mips64el = qtests_mips qtests_ppc = \ qtests_filter + \ (config_all_devices.has_key('CONFIG_ISA_TESTDEV') ? ['endianness-test'] : []) + \ - (config_all_devices.has_key('CONFIG_M48T59') ? ['m48t59-test'] : []) + \ (config_all_accel.has_key('CONFIG_TCG') ? ['prom-env-test'] : []) + \ (config_all_accel.has_key('CONFIG_TCG') ? ['boot-serial-test'] : []) + \ ['boot-order-test'] @@ -172,13 +179,15 @@ qtests_ppc64 = \ qtests_ppc + \ (config_all_devices.has_key('CONFIG_PSERIES') ? ['device-plug-test'] : []) + \ (config_all_devices.has_key('CONFIG_POWERNV') ? ['pnv-xscom-test'] : []) + \ + (config_all_devices.has_key('CONFIG_POWERNV') ? ['pnv-xive2-test'] : []) + \ (config_all_devices.has_key('CONFIG_POWERNV') ? ['pnv-spi-seeprom-test'] : []) + \ (config_all_devices.has_key('CONFIG_POWERNV') ? ['pnv-host-i2c-test'] : []) + \ + (config_all_devices.has_key('CONFIG_PSERIES') ? ['numa-test'] : []) + \ (config_all_devices.has_key('CONFIG_PSERIES') ? ['rtas-test'] : []) + \ (slirp.found() ? ['pxe-test'] : []) + \ (config_all_devices.has_key('CONFIG_USB_UHCI') ? ['usb-hcd-uhci-test'] : []) + \ (config_all_devices.has_key('CONFIG_USB_XHCI_NEC') ? ['usb-hcd-xhci-test'] : []) + \ - qtests_pci + ['migration-test', 'numa-test', 'cpu-plug-test', 'drive_del-test'] + qtests_pci + ['migration-test', 'cpu-plug-test', 'drive_del-test'] qtests_sh4 = (config_all_devices.has_key('CONFIG_ISA_TESTDEV') ? ['endianness-test'] : []) qtests_sh4eb = (config_all_devices.has_key('CONFIG_ISA_TESTDEV') ? ['endianness-test'] : []) @@ -199,13 +208,18 @@ qtests_npcm7xx = \ 'npcm7xx_sdhci-test', 'npcm7xx_smbus-test', 'npcm7xx_timer-test', - 'npcm7xx_watchdog_timer-test', - 'npcm_gmac-test'] + \ + 'npcm7xx_watchdog_timer-test'] + \ (slirp.found() ? ['npcm7xx_emc-test'] : []) +qtests_npcm8xx = \ + ['npcm_gmac-test'] qtests_aspeed = \ - ['aspeed_hace-test', - 'aspeed_smc-test', - 'aspeed_gpio-test'] + ['aspeed_gpio-test', + 'aspeed_hace-test', + 'aspeed_smc-test'] +qtests_aspeed64 = \ + ['ast2700-gpio-test', + 'ast2700-hace-test', + 'ast2700-smc-test'] qtests_stm32l4x5 = \ ['stm32l4x5_exti-test', @@ -218,7 +232,8 @@ qtests_arm = \ (config_all_devices.has_key('CONFIG_MPS2') ? ['sse-timer-test'] : []) + \ (config_all_devices.has_key('CONFIG_CMSDK_APB_DUALTIMER') ? ['cmsdk-apb-dualtimer-test'] : []) + \ (config_all_devices.has_key('CONFIG_CMSDK_APB_TIMER') ? ['cmsdk-apb-timer-test'] : []) + \ - (config_all_devices.has_key('CONFIG_CMSDK_APB_WATCHDOG') ? ['cmsdk-apb-watchdog-test'] : []) + \ + (config_all_devices.has_key('CONFIG_STELLARIS') or + config_all_devices.has_key('CONFIG_MPS2') ? ['cmsdk-apb-watchdog-test'] : []) + \ (config_all_devices.has_key('CONFIG_PFLASH_CFI02') and config_all_devices.has_key('CONFIG_MUSICPAL') ? ['pflash-cfi02-test'] : []) + \ (config_all_devices.has_key('CONFIG_ASPEED_SOC') ? qtests_aspeed : []) + \ @@ -244,6 +259,8 @@ qtests_aarch64 = \ (config_all_devices.has_key('CONFIG_RASPI') ? ['bcm2835-dma-test', 'bcm2835-i2c-test'] : []) + \ (config_all_accel.has_key('CONFIG_TCG') and \ config_all_devices.has_key('CONFIG_TPM_TIS_I2C') ? ['tpm-tis-i2c-test'] : []) + \ + (config_all_devices.has_key('CONFIG_ASPEED_SOC') ? qtests_aspeed64 : []) + \ + (config_all_devices.has_key('CONFIG_NPCM8XX') ? qtests_npcm8xx : []) + \ ['arm-cpu-features', 'numa-test', 'boot-serial-test', @@ -261,7 +278,7 @@ qtests_s390x = \ qtests_riscv32 = \ (config_all_devices.has_key('CONFIG_SIFIVE_E_AON') ? ['sifive-e-aon-watchdog-test'] : []) -qtests_riscv64 = \ +qtests_riscv64 = ['riscv-csr-test'] + \ (unpack_edk2_blobs ? ['bios-tables-test'] : []) qos_test_ss = ss.source_set() @@ -273,7 +290,6 @@ qos_test_ss.add( 'e1000-test.c', 'eepro100-test.c', 'es1370-test.c', - 'ipoctal232-test.c', 'lsm303dlhc-mag-test.c', 'isl_pmbus_vr-test.c', 'max34451-test.c', @@ -284,6 +300,7 @@ qos_test_ss.add( 'pca9552-test.c', 'pci-test.c', 'pcnet-test.c', + 'rs5c372-test.c', 'sdhci-test.c', 'spapr-phb-test.c', 'tmp105-test.c', @@ -298,11 +315,15 @@ qos_test_ss.add( 'vmxnet3-test.c', 'igb-test.c', 'ufs-test.c', + 'riscv-iommu-test.c', ) if config_all_devices.has_key('CONFIG_VIRTIO_SERIAL') qos_test_ss.add(files('virtio-serial-test.c')) endif +if config_all_devices.has_key('CONFIG_IP_OCTAL_232') + qos_test_ss.add(files('ipoctal232-test.c')) +endif if host_os != 'windows' qos_test_ss.add(files('e1000e-test.c')) @@ -319,23 +340,44 @@ endif tpmemu_files = ['tpm-emu.c', 'tpm-util.c', 'tpm-tests.c'] -migration_files = [files('migration-helpers.c')] +migration_files = [files( + 'migration/bootfile.c', + 'migration/framework.c', + 'migration/migration-qmp.c', + 'migration/migration-util.c', + 'migration/compression-tests.c', + 'migration/cpr-tests.c', + 'migration/file-tests.c', + 'migration/misc-tests.c', + 'migration/precopy-tests.c', + 'migration/postcopy-tests.c', +)] + +migration_tls_files = [] if gnutls.found() - migration_files += [files('../unit/crypto-tls-psk-helpers.c'), gnutls] + migration_tls_files = [files('migration/tls-tests.c', + '../unit/crypto-tls-psk-helpers.c'), gnutls] if tasn1.found() - migration_files += [files('../unit/crypto-tls-x509-helpers.c'), tasn1] + migration_tls_files += [files('../unit/crypto-tls-x509-helpers.c'), tasn1] endif endif qtests = { + 'aspeed_hace-test': files('aspeed-hace-utils.c', 'aspeed_hace-test.c'), + 'aspeed_smc-test': files('aspeed-smc-utils.c', 'aspeed_smc-test.c'), + 'ast2700-hace-test': files('aspeed-hace-utils.c', 'ast2700-hace-test.c'), + 'ast2700-smc-test': files('aspeed-smc-utils.c', 'ast2700-smc-test.c'), 'bios-tables-test': [io, 'boot-sector.c', 'acpi-utils.c', 'tpm-emu.c'], 'cdrom-test': files('boot-sector.c'), - 'dbus-vmstate-test': files('migration-helpers.c') + dbus_vmstate1, + 'dbus-vmstate-test': files('migration/migration-qmp.c', + 'migration/migration-util.c') + dbus_vmstate1, 'erst-test': files('erst-test.c'), 'ivshmem-test': [rt, '../../contrib/ivshmem-server/ivshmem-server.c'], - 'migration-test': migration_files, + 'migration-test': migration_files + migration_tls_files, 'pxe-test': files('boot-sector.c'), + 'pnv-xive2-test': files('pnv-xive2-common.c', 'pnv-xive2-flush-sync.c', + 'pnv-xive2-nvpg_bar.c'), 'qos-test': [chardev, io, qos_test_ss.apply({}).sources()], 'tpm-crb-swtpm-test': [io, tpmemu_files], 'tpm-crb-test': [io, tpmemu_files], @@ -344,7 +386,7 @@ qtests = { 'tpm-tis-i2c-test': [io, tpmemu_files, 'qtest_aspeed.c'], 'tpm-tis-device-swtpm-test': [io, tpmemu_files, 'tpm-tis-util.c'], 'tpm-tis-device-test': [io, tpmemu_files, 'tpm-tis-util.c'], - 'virtio-net-failover': files('migration-helpers.c'), + 'virtio-net-failover': migration_files, 'vmgenid-test': files('boot-sector.c', 'acpi-utils.c'), 'netdev-socket': files('netdev-socket.c', '../unit/socket-helpers.c'), } @@ -352,7 +394,7 @@ qtests = { if vnc.found() gvnc = dependency('gvnc-1.0', method: 'pkg-config', required: false) if gvnc.found() - qtests += {'vnc-display-test': [gvnc]} + qtests += {'vnc-display-test': [gvnc, keymap_targets]} qtests_generic += [ 'vnc-display-test' ] endif endif @@ -370,6 +412,8 @@ foreach dir : target_dirs target_base = dir.split('-')[0] qtest_emulator = emulators['qemu-system-' + target_base] target_qtests = get_variable('qtests_' + target_base, []) + qtests_generic + has_kvm = ('CONFIG_KVM' in config_all_accel and host_os == 'linux' + and cpu == target_base and fs.exists('/dev/kvm')) test_deps = roms qtest_env = environment() @@ -403,11 +447,18 @@ foreach dir : target_dirs test: executable(test, src, dependencies: deps) } endif + + test_args = ['--tap', '-k'] + + if test == 'migration-test' and has_kvm + test_args += ['--full'] + endif + test('qtest-@0@/@1@'.format(target_base, test), qtest_executables[test], depends: [test_deps, qtest_emulator, emulator_modules], env: qtest_env, - args: ['--tap', '-k'], + args: test_args, protocol: 'tap', timeout: slow_qtests.get(test, 60), priority: slow_qtests.get(test, 60), diff --git a/tests/qtest/migration-helpers.c b/tests/qtest/migration-helpers.c deleted file mode 100644 index a43d180..0000000 --- a/tests/qtest/migration-helpers.c +++ /dev/null @@ -1,531 +0,0 @@ -/* - * QTest migration helpers - * - * Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates - * based on the vhost-user-test.c that is: - * Copyright (c) 2014 Virtual Open Systems Sarl. - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - * - */ - -#include "qemu/osdep.h" -#include "qemu/ctype.h" -#include "qapi/qmp/qjson.h" -#include "qapi/qapi-visit-sockets.h" -#include "qapi/qobject-input-visitor.h" -#include "qapi/error.h" -#include "qapi/qmp/qlist.h" -#include "qemu/cutils.h" -#include "qemu/memalign.h" - -#include "migration-helpers.h" - -/* - * Number of seconds we wait when looking for migration - * status changes, to avoid test suite hanging forever - * when things go wrong. Needs to be higher enough to - * avoid false positives on loaded hosts. - */ -#define MIGRATION_STATUS_WAIT_TIMEOUT 120 - -static char *SocketAddress_to_str(SocketAddress *addr) -{ - switch (addr->type) { - case SOCKET_ADDRESS_TYPE_INET: - return g_strdup_printf("tcp:%s:%s", - addr->u.inet.host, - addr->u.inet.port); - case SOCKET_ADDRESS_TYPE_UNIX: - return g_strdup_printf("unix:%s", - addr->u.q_unix.path); - case SOCKET_ADDRESS_TYPE_FD: - return g_strdup_printf("fd:%s", addr->u.fd.str); - case SOCKET_ADDRESS_TYPE_VSOCK: - return g_strdup_printf("vsock:%s:%s", - addr->u.vsock.cid, - addr->u.vsock.port); - default: - return g_strdup("unknown address type"); - } -} - -static QDict *SocketAddress_to_qdict(SocketAddress *addr) -{ - QDict *dict = qdict_new(); - - switch (addr->type) { - case SOCKET_ADDRESS_TYPE_INET: - qdict_put_str(dict, "type", "inet"); - qdict_put_str(dict, "host", addr->u.inet.host); - qdict_put_str(dict, "port", addr->u.inet.port); - break; - case SOCKET_ADDRESS_TYPE_UNIX: - qdict_put_str(dict, "type", "unix"); - qdict_put_str(dict, "path", addr->u.q_unix.path); - break; - case SOCKET_ADDRESS_TYPE_FD: - qdict_put_str(dict, "type", "fd"); - qdict_put_str(dict, "str", addr->u.fd.str); - break; - case SOCKET_ADDRESS_TYPE_VSOCK: - qdict_put_str(dict, "type", "vsock"); - qdict_put_str(dict, "cid", addr->u.vsock.cid); - qdict_put_str(dict, "port", addr->u.vsock.port); - break; - default: - g_assert_not_reached(); - break; - } - - return dict; -} - -static SocketAddressList *migrate_get_socket_address(QTestState *who) -{ - QDict *rsp; - SocketAddressList *addrs; - Visitor *iv = NULL; - QObject *object; - - rsp = migrate_query(who); - object = qdict_get(rsp, "socket-address"); - - iv = qobject_input_visitor_new(object); - visit_type_SocketAddressList(iv, NULL, &addrs, &error_abort); - visit_free(iv); - - qobject_unref(rsp); - return addrs; -} - -static char * -migrate_get_connect_uri(QTestState *who) -{ - SocketAddressList *addrs; - char *connect_uri; - - addrs = migrate_get_socket_address(who); - connect_uri = SocketAddress_to_str(addrs->value); - - qapi_free_SocketAddressList(addrs); - return connect_uri; -} - -static QDict * -migrate_get_connect_qdict(QTestState *who) -{ - SocketAddressList *addrs; - QDict *connect_qdict; - - addrs = migrate_get_socket_address(who); - connect_qdict = SocketAddress_to_qdict(addrs->value); - - qapi_free_SocketAddressList(addrs); - return connect_qdict; -} - -static void migrate_set_ports(QTestState *to, QList *channel_list) -{ - QDict *addr; - 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)) { - 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, - QDict *event, void *opaque) -{ - QTestMigrationState *state = opaque; - - if (g_str_equal(name, "STOP")) { - state->stop_seen = true; - return true; - } else if (g_str_equal(name, "SUSPEND")) { - state->suspend_seen = true; - return true; - } else if (g_str_equal(name, "RESUME")) { - state->resume_seen = true; - return true; - } - - return false; -} - -void migrate_qmp_fail(QTestState *who, const char *uri, - const char *channels, const char *fmt, ...) -{ - va_list ap; - QDict *args, *err; - - va_start(ap, fmt); - args = qdict_from_vjsonf_nofail(fmt, ap); - va_end(ap); - - g_assert(!qdict_haskey(args, "uri")); - if (uri) { - qdict_put_str(args, "uri", 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); - } - - err = qtest_qmp_assert_failure_ref( - who, "{ 'execute': 'migrate', 'arguments': %p}", args); - - g_assert(qdict_haskey(err, "desc")); - - qobject_unref(err); -} - -/* - * Send QMP command "migrate". - * Arguments are built from @fmt... (formatted like - * 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, ...) -{ - va_list ap; - QDict *args; - g_autofree char *connect_uri = NULL; - - va_start(ap, fmt); - args = qdict_from_vjsonf_nofail(fmt, ap); - va_end(ap); - - g_assert(!qdict_haskey(args, "uri")); - if (uri) { - qdict_put_str(args, "uri", uri); - } else if (!channels) { - connect_uri = migrate_get_connect_uri(to); - qdict_put_str(args, "uri", connect_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); - migrate_set_ports(to, channel_list); - qdict_put_obj(args, "channels", channels_obj); - } - - qtest_qmp_assert_success(who, - "{ 'execute': 'migrate', 'arguments': %p}", args); -} - -void migrate_set_capability(QTestState *who, const char *capability, - bool value) -{ - qtest_qmp_assert_success(who, - "{ 'execute': 'migrate-set-capabilities'," - "'arguments': { " - "'capabilities': [ { " - "'capability': %s, 'state': %i } ] } }", - capability, value); -} - -void migrate_incoming_qmp(QTestState *to, const char *uri, const char *fmt, ...) -{ - va_list ap; - QDict *args, *rsp; - - va_start(ap, fmt); - args = qdict_from_vjsonf_nofail(fmt, ap); - va_end(ap); - - g_assert(!qdict_haskey(args, "uri")); - qdict_put_str(args, "uri", uri); - - /* This function relies on the event to work, make sure it's enabled */ - migrate_set_capability(to, "events", true); - - rsp = qtest_qmp(to, "{ 'execute': 'migrate-incoming', 'arguments': %p}", - args); - - if (!qdict_haskey(rsp, "return")) { - g_autoptr(GString) s = qobject_to_json_pretty(QOBJECT(rsp), true); - g_test_message("%s", s->str); - } - - g_assert(qdict_haskey(rsp, "return")); - qobject_unref(rsp); - - migration_event_wait(to, "setup"); -} - -/* - * Note: caller is responsible to free the returned object via - * qobject_unref() after use - */ -QDict *migrate_query(QTestState *who) -{ - return qtest_qmp_assert_success_ref(who, "{ 'execute': 'query-migrate' }"); -} - -QDict *migrate_query_not_failed(QTestState *who) -{ - const char *status; - QDict *rsp = migrate_query(who); - status = qdict_get_str(rsp, "status"); - if (g_str_equal(status, "failed")) { - g_printerr("query-migrate shows failed migration: %s\n", - qdict_get_str(rsp, "error-desc")); - } - g_assert(!g_str_equal(status, "failed")); - return rsp; -} - -/* - * Note: caller is responsible to free the returned object via - * g_free() after use - */ -static gchar *migrate_query_status(QTestState *who) -{ - QDict *rsp_return = migrate_query(who); - gchar *status = g_strdup(qdict_get_str(rsp_return, "status")); - - g_assert(status); - qobject_unref(rsp_return); - - return status; -} - -static bool check_migration_status(QTestState *who, const char *goal, - const char **ungoals) -{ - bool ready; - char *current_status; - const char **ungoal; - - current_status = migrate_query_status(who); - ready = strcmp(current_status, goal) == 0; - if (!ungoals) { - g_assert_cmpstr(current_status, !=, "failed"); - /* - * If looking for a state other than completed, - * completion of migration would cause the test to - * hang. - */ - if (strcmp(goal, "completed") != 0) { - g_assert_cmpstr(current_status, !=, "completed"); - } - } else { - for (ungoal = ungoals; *ungoal; ungoal++) { - g_assert_cmpstr(current_status, !=, *ungoal); - } - } - g_free(current_status); - return ready; -} - -void wait_for_migration_status(QTestState *who, - const char *goal, const char **ungoals) -{ - g_test_timer_start(); - while (!check_migration_status(who, goal, ungoals)) { - usleep(1000); - - g_assert(g_test_timer_elapsed() < MIGRATION_STATUS_WAIT_TIMEOUT); - } -} - -void wait_for_migration_complete(QTestState *who) -{ - wait_for_migration_status(who, "completed", NULL); -} - -void wait_for_migration_fail(QTestState *from, bool allow_active) -{ - g_test_timer_start(); - QDict *rsp_return; - char *status; - bool failed; - - do { - status = migrate_query_status(from); - bool result = !strcmp(status, "setup") || !strcmp(status, "failed") || - (allow_active && !strcmp(status, "active")); - if (!result) { - fprintf(stderr, "%s: unexpected status status=%s allow_active=%d\n", - __func__, status, allow_active); - } - g_assert(result); - failed = !strcmp(status, "failed"); - g_free(status); - - g_assert(g_test_timer_elapsed() < MIGRATION_STATUS_WAIT_TIMEOUT); - } while (!failed); - - /* Is the machine currently running? */ - rsp_return = qtest_qmp_assert_success_ref(from, - "{ 'execute': 'query-status' }"); - g_assert(qdict_haskey(rsp_return, "running")); - g_assert(qdict_get_bool(rsp_return, "running")); - qobject_unref(rsp_return); -} - -char *find_common_machine_version(const char *mtype, const char *var1, - const char *var2) -{ - g_autofree char *type1 = qtest_resolve_machine_alias(var1, mtype); - g_autofree char *type2 = qtest_resolve_machine_alias(var2, mtype); - - g_assert(type1 && type2); - - if (g_str_equal(type1, type2)) { - /* either can be used */ - return g_strdup(type1); - } - - if (qtest_has_machine_with_env(var2, type1)) { - return g_strdup(type1); - } - - if (qtest_has_machine_with_env(var1, type2)) { - return g_strdup(type2); - } - - g_test_message("No common machine version for machine type '%s' between " - "binaries %s and %s", mtype, getenv(var1), getenv(var2)); - g_assert_not_reached(); -} - -char *resolve_machine_version(const char *alias, const char *var1, - const char *var2) -{ - const char *mname = g_getenv("QTEST_QEMU_MACHINE_TYPE"); - g_autofree char *machine_name = NULL; - - if (mname) { - const char *dash = strrchr(mname, '-'); - const char *dot = strrchr(mname, '.'); - - machine_name = g_strdup(mname); - - if (dash && dot) { - assert(qtest_has_machine(machine_name)); - return g_steal_pointer(&machine_name); - } - /* else: probably an alias, let it be resolved below */ - } else { - /* use the hardcoded alias */ - machine_name = g_strdup(alias); - } - - return find_common_machine_version(machine_name, var1, var2); -} - -typedef struct { - char *name; - void (*func)(void); -} MigrationTest; - -static void migration_test_destroy(gpointer data) -{ - MigrationTest *test = (MigrationTest *)data; - - g_free(test->name); - g_free(test); -} - -static void migration_test_wrapper(const void *data) -{ - MigrationTest *test = (MigrationTest *)data; - - g_test_message("Running /%s%s", qtest_get_arch(), test->name); - test->func(); -} - -void migration_test_add(const char *path, void (*fn)(void)) -{ - MigrationTest *test = g_new0(MigrationTest, 1); - - test->func = fn; - test->name = g_strdup(path); - - qtest_add_data_func_full(path, test, migration_test_wrapper, - migration_test_destroy); -} - -#ifdef O_DIRECT -/* - * Probe for O_DIRECT support on the filesystem. Since this is used - * for tests, be conservative, if anything fails, assume it's - * unsupported. - */ -bool probe_o_direct_support(const char *tmpfs) -{ - g_autofree char *filename = g_strdup_printf("%s/probe-o-direct", tmpfs); - int fd, flags = O_CREAT | O_RDWR | O_TRUNC | O_DIRECT; - void *buf; - ssize_t ret, len; - uint64_t offset; - - fd = open(filename, flags, 0660); - if (fd < 0) { - unlink(filename); - return false; - } - - /* - * Using 1MB alignment as conservative choice to satisfy any - * plausible architecture default page size, and/or filesystem - * alignment restrictions. - */ - len = 0x100000; - offset = 0x100000; - - buf = qemu_try_memalign(len, len); - g_assert(buf); - - ret = pwrite(fd, buf, len, offset); - unlink(filename); - g_free(buf); - - if (ret < 0) { - return false; - } - - return true; -} -#endif - -/* - * Wait for a "MIGRATION" event. This is what Libvirt uses to track - * migration status changes. - */ -void migration_event_wait(QTestState *s, const char *target) -{ - QDict *response, *data; - const char *status; - bool found; - - do { - response = qtest_qmp_eventwait_ref(s, "MIGRATION"); - data = qdict_get_qdict(response, "data"); - g_assert(data); - status = qdict_get_str(data, "status"); - found = (strcmp(status, target) == 0); - qobject_unref(response); - } while (!found); -} diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c index 9d08101..0893687 100644 --- a/tests/qtest/migration-test.c +++ b/tests/qtest/migration-test.c @@ -11,3964 +11,56 @@ */ #include "qemu/osdep.h" - -#include "libqtest.h" -#include "qapi/qmp/qdict.h" +#include "migration/framework.h" #include "qemu/module.h" -#include "qemu/option.h" -#include "qemu/range.h" -#include "qemu/sockets.h" -#include "chardev/char.h" -#include "crypto/tlscredspsk.h" -#include "qapi/qmp/qlist.h" -#include "ppc-util.h" - -#include "migration-helpers.h" -#include "tests/migration/migration-test.h" -#ifdef CONFIG_GNUTLS -# include "tests/unit/crypto-tls-psk-helpers.h" -# ifdef CONFIG_TASN1 -# include "tests/unit/crypto-tls-x509-helpers.h" -# endif /* CONFIG_TASN1 */ -#endif /* CONFIG_GNUTLS */ - -/* For dirty ring test; so far only x86_64 is supported */ -#if defined(__linux__) && defined(HOST_X86_64) -#include "linux/kvm.h" -#endif - -unsigned start_address; -unsigned end_address; -static bool uffd_feature_thread_id; -static QTestMigrationState src_state; -static QTestMigrationState dst_state; - -/* - * An initial 3 MB offset is used as that corresponds - * to ~1 sec of data transfer with our bandwidth setting. - */ -#define MAGIC_OFFSET_BASE (3 * 1024 * 1024) -/* - * A further 1k is added to ensure we're not a multiple - * of TEST_MEM_PAGE_SIZE, thus avoid clash with writes - * from the migration guest workload. - */ -#define MAGIC_OFFSET_SHUFFLE 1024 -#define MAGIC_OFFSET (MAGIC_OFFSET_BASE + MAGIC_OFFSET_SHUFFLE) -#define MAGIC_MARKER 0xFEED12345678CAFEULL - -/* - * Dirtylimit stop working if dirty page rate error - * value less than DIRTYLIMIT_TOLERANCE_RANGE - */ -#define DIRTYLIMIT_TOLERANCE_RANGE 25 /* MB/s */ - -#define ANALYZE_SCRIPT "scripts/analyze-migration.py" - -#define QEMU_VM_FILE_MAGIC 0x5145564d -#define FILE_TEST_FILENAME "migfile" -#define FILE_TEST_OFFSET 0x1000 -#define FILE_TEST_MARKER 'X' -#define QEMU_ENV_SRC "QTEST_QEMU_BINARY_SRC" -#define QEMU_ENV_DST "QTEST_QEMU_BINARY_DST" - -typedef enum PostcopyRecoveryFailStage { - /* - * "no failure" must be 0 as it's the default. OTOH, real failure - * cases must be >0 to make sure they trigger by a "if" test. - */ - POSTCOPY_FAIL_NONE = 0, - POSTCOPY_FAIL_CHANNEL_ESTABLISH, - POSTCOPY_FAIL_RECOVERY, - POSTCOPY_FAIL_MAX -} PostcopyRecoveryFailStage; - -#if defined(__linux__) -#include <sys/syscall.h> -#include <sys/vfs.h> -#endif - -#if defined(__linux__) && defined(__NR_userfaultfd) && defined(CONFIG_EVENTFD) -#include <sys/eventfd.h> -#include <sys/ioctl.h> -#include "qemu/userfaultfd.h" - -static bool ufd_version_check(void) -{ - struct uffdio_api api_struct; - uint64_t ioctl_mask; - - int ufd = uffd_open(O_CLOEXEC); - - if (ufd == -1) { - g_test_message("Skipping test: userfaultfd not available"); - return false; - } - - api_struct.api = UFFD_API; - api_struct.features = 0; - if (ioctl(ufd, UFFDIO_API, &api_struct)) { - g_test_message("Skipping test: UFFDIO_API failed"); - return false; - } - uffd_feature_thread_id = api_struct.features & UFFD_FEATURE_THREAD_ID; - - ioctl_mask = 1ULL << _UFFDIO_REGISTER | - 1ULL << _UFFDIO_UNREGISTER; - if ((api_struct.ioctls & ioctl_mask) != ioctl_mask) { - g_test_message("Skipping test: Missing userfault feature"); - return false; - } - - return true; -} - -#else -static bool ufd_version_check(void) -{ - g_test_message("Skipping test: Userfault not available (builtdtime)"); - return false; -} - -#endif - -static char *tmpfs; -static char *bootpath; - -/* The boot file modifies memory area in [start_address, end_address) - * repeatedly. It outputs a 'B' at a fixed rate while it's still running. - */ -#include "tests/migration/i386/a-b-bootblock.h" -#include "tests/migration/aarch64/a-b-kernel.h" -#include "tests/migration/ppc64/a-b-kernel.h" -#include "tests/migration/s390x/a-b-bios.h" - -static void bootfile_delete(void) -{ - if (!bootpath) { - return; - } - unlink(bootpath); - g_free(bootpath); - bootpath = NULL; -} - -static void bootfile_create(char *dir, bool suspend_me) -{ - const char *arch = qtest_get_arch(); - unsigned char *content; - size_t len; - - bootfile_delete(); - bootpath = g_strdup_printf("%s/bootsect", dir); - if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { - /* the assembled x86 boot sector should be exactly one sector large */ - g_assert(sizeof(x86_bootsect) == 512); - x86_bootsect[SYM_suspend_me - SYM_start] = suspend_me; - content = x86_bootsect; - len = sizeof(x86_bootsect); - } else if (g_str_equal(arch, "s390x")) { - content = s390x_elf; - len = sizeof(s390x_elf); - } else if (strcmp(arch, "ppc64") == 0) { - content = ppc64_kernel; - len = sizeof(ppc64_kernel); - } else if (strcmp(arch, "aarch64") == 0) { - content = aarch64_kernel; - len = sizeof(aarch64_kernel); - g_assert(sizeof(aarch64_kernel) <= ARM_TEST_MAX_KERNEL_SIZE); - } else { - g_assert_not_reached(); - } - - FILE *bootfile = fopen(bootpath, "wb"); - - g_assert_cmpint(fwrite(content, len, 1, bootfile), ==, 1); - fclose(bootfile); -} - -/* - * Wait for some output in the serial output file, - * we get an 'A' followed by an endless string of 'B's - * but on the destination we won't have the A (unless we enabled suspend/resume) - */ -static void wait_for_serial(const char *side) -{ - g_autofree char *serialpath = g_strdup_printf("%s/%s", tmpfs, side); - FILE *serialfile = fopen(serialpath, "r"); - - do { - int readvalue = fgetc(serialfile); - - switch (readvalue) { - case 'A': - /* Fine */ - break; - - case 'B': - /* It's alive! */ - fclose(serialfile); - return; - - case EOF: - fseek(serialfile, 0, SEEK_SET); - usleep(1000); - break; - - default: - fprintf(stderr, "Unexpected %d on %s serial\n", readvalue, side); - g_assert_not_reached(); - } - } while (true); -} - -static void wait_for_stop(QTestState *who, QTestMigrationState *state) -{ - if (!state->stop_seen) { - qtest_qmp_eventwait(who, "STOP"); - } -} - -static void wait_for_resume(QTestState *who, QTestMigrationState *state) -{ - if (!state->resume_seen) { - qtest_qmp_eventwait(who, "RESUME"); - } -} - -static void wait_for_suspend(QTestState *who, QTestMigrationState *state) -{ - if (state->suspend_me && !state->suspend_seen) { - qtest_qmp_eventwait(who, "SUSPEND"); - } -} - -/* - * It's tricky to use qemu's migration event capability with qtest, - * events suddenly appearing confuse the qmp()/hmp() responses. - */ - -static int64_t read_ram_property_int(QTestState *who, const char *property) -{ - QDict *rsp_return, *rsp_ram; - int64_t result; - - rsp_return = migrate_query_not_failed(who); - if (!qdict_haskey(rsp_return, "ram")) { - /* Still in setup */ - result = 0; - } else { - rsp_ram = qdict_get_qdict(rsp_return, "ram"); - result = qdict_get_try_int(rsp_ram, property, 0); - } - qobject_unref(rsp_return); - return result; -} - -static int64_t read_migrate_property_int(QTestState *who, const char *property) -{ - QDict *rsp_return; - int64_t result; - - rsp_return = migrate_query_not_failed(who); - result = qdict_get_try_int(rsp_return, property, 0); - qobject_unref(rsp_return); - return result; -} - -static uint64_t get_migration_pass(QTestState *who) -{ - return read_ram_property_int(who, "dirty-sync-count"); -} - -static void read_blocktime(QTestState *who) -{ - QDict *rsp_return; - - rsp_return = migrate_query_not_failed(who); - g_assert(qdict_haskey(rsp_return, "postcopy-blocktime")); - qobject_unref(rsp_return); -} - -/* - * Wait for two changes in the migration pass count, but bail if we stop. - */ -static void wait_for_migration_pass(QTestState *who) -{ - uint64_t pass, prev_pass = 0, changes = 0; - - while (changes < 2 && !src_state.stop_seen && !src_state.suspend_seen) { - usleep(1000); - pass = get_migration_pass(who); - changes += (pass != prev_pass); - prev_pass = pass; - } -} - -static void check_guests_ram(QTestState *who) -{ - /* Our ASM test will have been incrementing one byte from each page from - * start_address to < end_address in order. This gives us a constraint - * that any page's byte should be equal or less than the previous pages - * byte (mod 256); and they should all be equal except for one transition - * at the point where we meet the incrementer. (We're running this with - * the guest stopped). - */ - unsigned address; - uint8_t first_byte; - uint8_t last_byte; - bool hit_edge = false; - int bad = 0; - - qtest_memread(who, start_address, &first_byte, 1); - last_byte = first_byte; - - for (address = start_address + TEST_MEM_PAGE_SIZE; address < end_address; - address += TEST_MEM_PAGE_SIZE) - { - uint8_t b; - qtest_memread(who, address, &b, 1); - if (b != last_byte) { - if (((b + 1) % 256) == last_byte && !hit_edge) { - /* This is OK, the guest stopped at the point of - * incrementing the previous page but didn't get - * to us yet. - */ - hit_edge = true; - last_byte = b; - } else { - bad++; - if (bad <= 10) { - fprintf(stderr, "Memory content inconsistency at %x" - " first_byte = %x last_byte = %x current = %x" - " hit_edge = %x\n", - address, first_byte, last_byte, b, hit_edge); - } - } - } - } - if (bad >= 10) { - fprintf(stderr, "and in another %d pages", bad - 10); - } - g_assert(bad == 0); -} - -static void cleanup(const char *filename) -{ - g_autofree char *path = g_strdup_printf("%s/%s", tmpfs, filename); - - unlink(path); -} - -static long long migrate_get_parameter_int(QTestState *who, - const char *parameter) -{ - QDict *rsp; - long long result; - - rsp = qtest_qmp_assert_success_ref( - who, "{ 'execute': 'query-migrate-parameters' }"); - result = qdict_get_int(rsp, parameter); - qobject_unref(rsp); - return result; -} - -static void migrate_check_parameter_int(QTestState *who, const char *parameter, - long long value) -{ - long long result; - - result = migrate_get_parameter_int(who, parameter); - g_assert_cmpint(result, ==, value); -} - -static void migrate_set_parameter_int(QTestState *who, const char *parameter, - long long value) -{ - qtest_qmp_assert_success(who, - "{ 'execute': 'migrate-set-parameters'," - "'arguments': { %s: %lld } }", - parameter, value); - migrate_check_parameter_int(who, parameter, value); -} - -static char *migrate_get_parameter_str(QTestState *who, - const char *parameter) -{ - QDict *rsp; - char *result; - - rsp = qtest_qmp_assert_success_ref( - who, "{ 'execute': 'query-migrate-parameters' }"); - result = g_strdup(qdict_get_str(rsp, parameter)); - qobject_unref(rsp); - return result; -} - -static void migrate_check_parameter_str(QTestState *who, const char *parameter, - const char *value) -{ - g_autofree char *result = migrate_get_parameter_str(who, parameter); - g_assert_cmpstr(result, ==, value); -} - -static void migrate_set_parameter_str(QTestState *who, const char *parameter, - const char *value) -{ - qtest_qmp_assert_success(who, - "{ 'execute': 'migrate-set-parameters'," - "'arguments': { %s: %s } }", - parameter, value); - migrate_check_parameter_str(who, parameter, value); -} - -static long long migrate_get_parameter_bool(QTestState *who, - const char *parameter) -{ - QDict *rsp; - int result; - - rsp = qtest_qmp_assert_success_ref( - who, "{ 'execute': 'query-migrate-parameters' }"); - result = qdict_get_bool(rsp, parameter); - qobject_unref(rsp); - return !!result; -} - -static void migrate_check_parameter_bool(QTestState *who, const char *parameter, - int value) -{ - int result; - - result = migrate_get_parameter_bool(who, parameter); - g_assert_cmpint(result, ==, value); -} - -static void migrate_set_parameter_bool(QTestState *who, const char *parameter, - int value) -{ - qtest_qmp_assert_success(who, - "{ 'execute': 'migrate-set-parameters'," - "'arguments': { %s: %i } }", - parameter, value); - migrate_check_parameter_bool(who, parameter, value); -} - -static void migrate_ensure_non_converge(QTestState *who) -{ - /* Can't converge with 1ms downtime + 3 mbs bandwidth limit */ - migrate_set_parameter_int(who, "max-bandwidth", 3 * 1000 * 1000); - migrate_set_parameter_int(who, "downtime-limit", 1); -} - -static void migrate_ensure_converge(QTestState *who) -{ - /* Should converge with 30s downtime + 1 gbs bandwidth limit */ - migrate_set_parameter_int(who, "max-bandwidth", 1 * 1000 * 1000 * 1000); - migrate_set_parameter_int(who, "downtime-limit", 30 * 1000); -} - -/* - * Our goal is to ensure that we run a single full migration - * iteration, and also dirty memory, ensuring that at least - * one further iteration is required. - * - * We can't directly synchronize with the start of a migration - * so we have to apply some tricks monitoring memory that is - * transferred. - * - * Initially we set the migration bandwidth to an insanely - * low value, with tiny max downtime too. This basically - * guarantees migration will never complete. - * - * This will result in a test that is unacceptably slow though, - * so we can't let the entire migration pass run at this speed. - * Our intent is to let it run just long enough that we can - * prove data prior to the marker has been transferred *AND* - * also prove this transferred data is dirty again. - * - * Before migration starts, we write a 64-bit magic marker - * into a fixed location in the src VM RAM. - * - * Then watch dst memory until the marker appears. This is - * proof that start_address -> MAGIC_OFFSET_BASE has been - * transferred. - * - * Finally we go back to the source and read a byte just - * before the marker until we see it flip in value. This - * is proof that start_address -> MAGIC_OFFSET_BASE - * is now dirty again. - * - * IOW, we're guaranteed at least a 2nd migration pass - * at this point. - * - * We can now let migration run at full speed to finish - * the test - */ -static void migrate_prepare_for_dirty_mem(QTestState *from) -{ - /* - * The guest workflow iterates from start_address to - * end_address, writing 1 byte every TEST_MEM_PAGE_SIZE - * bytes. - * - * IOW, if we write to mem at a point which is NOT - * a multiple of TEST_MEM_PAGE_SIZE, our write won't - * conflict with the migration workflow. - * - * We put in a marker here, that we'll use to determine - * when the data has been transferred to the dst. - */ - qtest_writeq(from, start_address + MAGIC_OFFSET, MAGIC_MARKER); -} - -static void migrate_wait_for_dirty_mem(QTestState *from, - QTestState *to) -{ - uint64_t watch_address = start_address + MAGIC_OFFSET_BASE; - uint64_t marker_address = start_address + MAGIC_OFFSET; - uint8_t watch_byte; - - /* - * Wait for the MAGIC_MARKER to get transferred, as an - * indicator that a migration pass has made some known - * amount of progress. - */ - do { - usleep(1000 * 10); - } while (qtest_readq(to, marker_address) != MAGIC_MARKER); - - - /* If suspended, src only iterates once, and watch_byte may never change */ - if (src_state.suspend_me) { - return; - } - - /* - * Now ensure that already transferred bytes are - * dirty again from the guest workload. Note the - * guest byte value will wrap around and by chance - * match the original watch_byte. This is harmless - * as we'll eventually see a different value if we - * keep watching - */ - watch_byte = qtest_readb(from, watch_address); - do { - usleep(1000 * 10); - } while (qtest_readb(from, watch_address) == watch_byte); -} - - -static void migrate_pause(QTestState *who) -{ - qtest_qmp_assert_success(who, "{ 'execute': 'migrate-pause' }"); -} - -static void migrate_continue(QTestState *who, const char *state) -{ - qtest_qmp_assert_success(who, - "{ 'execute': 'migrate-continue'," - " 'arguments': { 'state': %s } }", - state); -} - -static void migrate_recover(QTestState *who, const char *uri) -{ - qtest_qmp_assert_success(who, - "{ 'execute': 'migrate-recover', " - " 'id': 'recover-cmd', " - " 'arguments': { 'uri': %s } }", - uri); -} - -static void migrate_cancel(QTestState *who) -{ - qtest_qmp_assert_success(who, "{ 'execute': 'migrate_cancel' }"); -} - -static void migrate_postcopy_start(QTestState *from, QTestState *to) -{ - qtest_qmp_assert_success(from, "{ 'execute': 'migrate-start-postcopy' }"); - - wait_for_stop(from, &src_state); - qtest_qmp_eventwait(to, "RESUME"); -} - -typedef struct { - /* - * QTEST_LOG=1 may override this. When QTEST_LOG=1, we always dump errors - * unconditionally, because it means the user would like to be verbose. - */ - bool hide_stderr; - bool use_shmem; - /* only launch the target process */ - bool only_target; - /* Use dirty ring if true; dirty logging otherwise */ - bool use_dirty_ring; - const char *opts_source; - const char *opts_target; - /* suspend the src before migrating to dest. */ - bool suspend_me; -} MigrateStart; - -/* - * A hook that runs after the src and dst QEMUs have been - * created, but before the migration is started. This can - * be used to set migration parameters and capabilities. - * - * Returns: NULL, or a pointer to opaque state to be - * later passed to the TestMigrateFinishHook - */ -typedef void * (*TestMigrateStartHook)(QTestState *from, - QTestState *to); - -/* - * A hook that runs after the migration has finished, - * regardless of whether it succeeded or failed, but - * before QEMU has terminated (unless it self-terminated - * due to migration error) - * - * @opaque is a pointer to state previously returned - * by the TestMigrateStartHook if any, or NULL. - */ -typedef void (*TestMigrateFinishHook)(QTestState *from, - QTestState *to, - void *opaque); - -typedef struct { - /* Optional: fine tune start parameters */ - MigrateStart start; - - /* Required: the URI for the dst QEMU to listen on */ - const char *listen_uri; - - /* - * Optional: the URI for the src QEMU to connect to - * If NULL, then it will query the dst QEMU for its actual - * listening address and use that as the connect address. - * This allows for dynamically picking a free TCP port. - */ - const char *connect_uri; - - /* - * Optional: JSON-formatted list of src QEMU URIs. If a port is - * defined as '0' in any QDict key a value of '0' will be - * automatically converted to the correct destination port. - */ - const char *connect_channels; - - /* Optional: callback to run at start to set migration parameters */ - TestMigrateStartHook start_hook; - /* Optional: callback to run at finish to cleanup */ - TestMigrateFinishHook finish_hook; - - /* - * Optional: normally we expect the migration process to complete. - * - * There can be a variety of reasons and stages in which failure - * can happen during tests. - * - * If a failure is expected to happen at time of establishing - * the connection, then MIG_TEST_FAIL will indicate that the dst - * QEMU is expected to stay running and accept future migration - * connections. - * - * If a failure is expected to happen while processing the - * migration stream, then MIG_TEST_FAIL_DEST_QUIT_ERR will indicate - * that the dst QEMU is expected to quit with non-zero exit status - */ - enum { - /* This test should succeed, the default */ - MIG_TEST_SUCCEED = 0, - /* This test should fail, dest qemu should keep alive */ - MIG_TEST_FAIL, - /* This test should fail, dest qemu should fail with abnormal status */ - MIG_TEST_FAIL_DEST_QUIT_ERR, - /* The QMP command for this migration should fail with an error */ - MIG_TEST_QMP_ERROR, - } result; - - /* - * Optional: set number of migration passes to wait for, if live==true. - * If zero, then merely wait for a few MB of dirty data - */ - unsigned int iterations; - - /* - * Optional: whether the guest CPUs should be running during a precopy - * migration test. We used to always run with live but it took much - * longer so we reduced live tests to only the ones that have solid - * reason to be tested live-only. For each of the new test cases for - * precopy please provide justifications to use live explicitly (please - * refer to existing ones with live=true), or use live=off by default. - */ - bool live; - - /* Postcopy specific fields */ - void *postcopy_data; - bool postcopy_preempt; - PostcopyRecoveryFailStage postcopy_recovery_fail_stage; -} MigrateCommon; - -static int test_migrate_start(QTestState **from, QTestState **to, - const char *uri, MigrateStart *args) -{ - g_autofree gchar *arch_source = NULL; - g_autofree gchar *arch_target = NULL; - /* options for source and target */ - g_autofree gchar *arch_opts = NULL; - g_autofree gchar *cmd_source = NULL; - g_autofree gchar *cmd_target = NULL; - const gchar *ignore_stderr; - g_autofree char *shmem_opts = NULL; - g_autofree char *shmem_path = NULL; - const char *kvm_opts = NULL; - const char *arch = qtest_get_arch(); - const char *memory_size; - const char *machine_alias, *machine_opts = ""; - g_autofree char *machine = NULL; - - if (args->use_shmem) { - if (!g_file_test("/dev/shm", G_FILE_TEST_IS_DIR)) { - g_test_skip("/dev/shm is not supported"); - return -1; - } - } - - dst_state = (QTestMigrationState) { }; - src_state = (QTestMigrationState) { }; - bootfile_create(tmpfs, args->suspend_me); - src_state.suspend_me = args->suspend_me; - - if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { - memory_size = "150M"; - - if (g_str_equal(arch, "i386")) { - machine_alias = "pc"; - } else { - machine_alias = "q35"; - } - arch_opts = g_strdup_printf( - "-drive if=none,id=d0,file=%s,format=raw " - "-device ide-hd,drive=d0,secs=1,cyls=1,heads=1", bootpath); - start_address = X86_TEST_MEM_START; - end_address = X86_TEST_MEM_END; - } else if (g_str_equal(arch, "s390x")) { - memory_size = "128M"; - machine_alias = "s390-ccw-virtio"; - arch_opts = g_strdup_printf("-bios %s", bootpath); - start_address = S390_TEST_MEM_START; - end_address = S390_TEST_MEM_END; - } else if (strcmp(arch, "ppc64") == 0) { - memory_size = "256M"; - start_address = PPC_TEST_MEM_START; - end_address = PPC_TEST_MEM_END; - machine_alias = "pseries"; - machine_opts = "vsmt=8"; - arch_opts = g_strdup_printf( - "-nodefaults -machine " PSERIES_DEFAULT_CAPABILITIES " " - "-bios %s", bootpath); - } else if (strcmp(arch, "aarch64") == 0) { - memory_size = "150M"; - machine_alias = "virt"; - machine_opts = "gic-version=3"; - arch_opts = g_strdup_printf("-cpu max -kernel %s", bootpath); - start_address = ARM_TEST_MEM_START; - end_address = ARM_TEST_MEM_END; - } else { - g_assert_not_reached(); - } - - if (!getenv("QTEST_LOG") && args->hide_stderr) { -#ifndef _WIN32 - ignore_stderr = "2>/dev/null"; -#else - /* - * On Windows the QEMU executable is created via CreateProcess() and - * IO redirection does not work, so don't bother adding IO redirection - * to the command line. - */ - ignore_stderr = ""; -#endif - } else { - ignore_stderr = ""; - } - - if (args->use_shmem) { - shmem_path = g_strdup_printf("/dev/shm/qemu-%d", getpid()); - shmem_opts = g_strdup_printf( - "-object memory-backend-file,id=mem0,size=%s" - ",mem-path=%s,share=on -numa node,memdev=mem0", - memory_size, shmem_path); - } - - if (args->use_dirty_ring) { - kvm_opts = ",dirty-ring-size=4096"; - } - - if (!qtest_has_machine(machine_alias)) { - g_autofree char *msg = g_strdup_printf("machine %s not supported", machine_alias); - g_test_skip(msg); - return -1; - } - - machine = resolve_machine_version(machine_alias, QEMU_ENV_SRC, - QEMU_ENV_DST); - - g_test_message("Using machine type: %s", machine); - - cmd_source = g_strdup_printf("-accel kvm%s -accel tcg " - "-machine %s,%s " - "-name source,debug-threads=on " - "-m %s " - "-serial file:%s/src_serial " - "%s %s %s %s %s", - kvm_opts ? kvm_opts : "", - machine, machine_opts, - memory_size, tmpfs, - arch_opts ? arch_opts : "", - arch_source ? arch_source : "", - shmem_opts ? shmem_opts : "", - args->opts_source ? args->opts_source : "", - ignore_stderr); - if (!args->only_target) { - *from = qtest_init_with_env(QEMU_ENV_SRC, cmd_source); - qtest_qmp_set_event_callback(*from, - migrate_watch_for_events, - &src_state); - } - - cmd_target = g_strdup_printf("-accel kvm%s -accel tcg " - "-machine %s,%s " - "-name target,debug-threads=on " - "-m %s " - "-serial file:%s/dest_serial " - "-incoming %s " - "%s %s %s %s %s", - kvm_opts ? kvm_opts : "", - machine, machine_opts, - memory_size, tmpfs, uri, - arch_opts ? arch_opts : "", - arch_target ? arch_target : "", - shmem_opts ? shmem_opts : "", - args->opts_target ? args->opts_target : "", - ignore_stderr); - *to = qtest_init_with_env(QEMU_ENV_DST, cmd_target); - qtest_qmp_set_event_callback(*to, - migrate_watch_for_events, - &dst_state); - - /* - * Remove shmem file immediately to avoid memory leak in test failed case. - * It's valid because QEMU has already opened this file - */ - if (args->use_shmem) { - unlink(shmem_path); - } - - /* - * Always enable migration events. Libvirt always uses it, let's try - * to mimic as closer as that. - */ - migrate_set_capability(*from, "events", true); - migrate_set_capability(*to, "events", true); - - return 0; -} - -static void test_migrate_end(QTestState *from, QTestState *to, bool test_dest) -{ - unsigned char dest_byte_a, dest_byte_b, dest_byte_c, dest_byte_d; - - qtest_quit(from); - - if (test_dest) { - qtest_memread(to, start_address, &dest_byte_a, 1); - - /* Destination still running, wait for a byte to change */ - do { - qtest_memread(to, start_address, &dest_byte_b, 1); - usleep(1000 * 10); - } while (dest_byte_a == dest_byte_b); - - qtest_qmp_assert_success(to, "{ 'execute' : 'stop'}"); - - /* With it stopped, check nothing changes */ - qtest_memread(to, start_address, &dest_byte_c, 1); - usleep(1000 * 200); - qtest_memread(to, start_address, &dest_byte_d, 1); - g_assert_cmpint(dest_byte_c, ==, dest_byte_d); - - check_guests_ram(to); - } - - qtest_quit(to); - - cleanup("migsocket"); - cleanup("src_serial"); - cleanup("dest_serial"); - cleanup(FILE_TEST_FILENAME); -} - -#ifdef CONFIG_GNUTLS -struct TestMigrateTLSPSKData { - char *workdir; - char *workdiralt; - char *pskfile; - char *pskfilealt; -}; - -static void * -test_migrate_tls_psk_start_common(QTestState *from, - QTestState *to, - bool mismatch) -{ - struct TestMigrateTLSPSKData *data = - g_new0(struct TestMigrateTLSPSKData, 1); - - data->workdir = g_strdup_printf("%s/tlscredspsk0", tmpfs); - data->pskfile = g_strdup_printf("%s/%s", data->workdir, - QCRYPTO_TLS_CREDS_PSKFILE); - g_mkdir_with_parents(data->workdir, 0700); - test_tls_psk_init(data->pskfile); - - if (mismatch) { - data->workdiralt = g_strdup_printf("%s/tlscredspskalt0", tmpfs); - data->pskfilealt = g_strdup_printf("%s/%s", data->workdiralt, - QCRYPTO_TLS_CREDS_PSKFILE); - g_mkdir_with_parents(data->workdiralt, 0700); - test_tls_psk_init_alt(data->pskfilealt); - } - - qtest_qmp_assert_success(from, - "{ 'execute': 'object-add'," - " 'arguments': { 'qom-type': 'tls-creds-psk'," - " 'id': 'tlscredspsk0'," - " 'endpoint': 'client'," - " 'dir': %s," - " 'username': 'qemu'} }", - data->workdir); - - qtest_qmp_assert_success(to, - "{ 'execute': 'object-add'," - " 'arguments': { 'qom-type': 'tls-creds-psk'," - " 'id': 'tlscredspsk0'," - " 'endpoint': 'server'," - " 'dir': %s } }", - mismatch ? data->workdiralt : data->workdir); - - migrate_set_parameter_str(from, "tls-creds", "tlscredspsk0"); - migrate_set_parameter_str(to, "tls-creds", "tlscredspsk0"); - - return data; -} - -static void * -test_migrate_tls_psk_start_match(QTestState *from, - QTestState *to) -{ - return test_migrate_tls_psk_start_common(from, to, false); -} - -static void * -test_migrate_tls_psk_start_mismatch(QTestState *from, - QTestState *to) -{ - return test_migrate_tls_psk_start_common(from, to, true); -} - -static void -test_migrate_tls_psk_finish(QTestState *from, - QTestState *to, - void *opaque) -{ - struct TestMigrateTLSPSKData *data = opaque; - - test_tls_psk_cleanup(data->pskfile); - if (data->pskfilealt) { - test_tls_psk_cleanup(data->pskfilealt); - } - rmdir(data->workdir); - if (data->workdiralt) { - rmdir(data->workdiralt); - } - - g_free(data->workdiralt); - g_free(data->pskfilealt); - g_free(data->workdir); - g_free(data->pskfile); - g_free(data); -} - -#ifdef CONFIG_TASN1 -typedef struct { - char *workdir; - char *keyfile; - char *cacert; - char *servercert; - char *serverkey; - char *clientcert; - char *clientkey; -} TestMigrateTLSX509Data; - -typedef struct { - bool verifyclient; - bool clientcert; - bool hostileclient; - bool authzclient; - const char *certhostname; - const char *certipaddr; -} TestMigrateTLSX509; - -static void * -test_migrate_tls_x509_start_common(QTestState *from, - QTestState *to, - TestMigrateTLSX509 *args) -{ - TestMigrateTLSX509Data *data = g_new0(TestMigrateTLSX509Data, 1); - - data->workdir = g_strdup_printf("%s/tlscredsx5090", tmpfs); - data->keyfile = g_strdup_printf("%s/key.pem", data->workdir); - - data->cacert = g_strdup_printf("%s/ca-cert.pem", data->workdir); - data->serverkey = g_strdup_printf("%s/server-key.pem", data->workdir); - data->servercert = g_strdup_printf("%s/server-cert.pem", data->workdir); - if (args->clientcert) { - data->clientkey = g_strdup_printf("%s/client-key.pem", data->workdir); - data->clientcert = g_strdup_printf("%s/client-cert.pem", data->workdir); - } - - g_mkdir_with_parents(data->workdir, 0700); - - test_tls_init(data->keyfile); -#ifndef _WIN32 - g_assert(link(data->keyfile, data->serverkey) == 0); -#else - g_assert(CreateHardLink(data->serverkey, data->keyfile, NULL) != 0); -#endif - if (args->clientcert) { -#ifndef _WIN32 - g_assert(link(data->keyfile, data->clientkey) == 0); -#else - g_assert(CreateHardLink(data->clientkey, data->keyfile, NULL) != 0); -#endif - } - - TLS_ROOT_REQ_SIMPLE(cacertreq, data->cacert); - if (args->clientcert) { - TLS_CERT_REQ_SIMPLE_CLIENT(servercertreq, cacertreq, - args->hostileclient ? - QCRYPTO_TLS_TEST_CLIENT_HOSTILE_NAME : - QCRYPTO_TLS_TEST_CLIENT_NAME, - data->clientcert); - test_tls_deinit_cert(&servercertreq); - } - - TLS_CERT_REQ_SIMPLE_SERVER(clientcertreq, cacertreq, - data->servercert, - args->certhostname, - args->certipaddr); - test_tls_deinit_cert(&clientcertreq); - test_tls_deinit_cert(&cacertreq); - - qtest_qmp_assert_success(from, - "{ 'execute': 'object-add'," - " 'arguments': { 'qom-type': 'tls-creds-x509'," - " 'id': 'tlscredsx509client0'," - " 'endpoint': 'client'," - " 'dir': %s," - " 'sanity-check': true," - " 'verify-peer': true} }", - data->workdir); - migrate_set_parameter_str(from, "tls-creds", "tlscredsx509client0"); - if (args->certhostname) { - migrate_set_parameter_str(from, "tls-hostname", args->certhostname); - } - - qtest_qmp_assert_success(to, - "{ 'execute': 'object-add'," - " 'arguments': { 'qom-type': 'tls-creds-x509'," - " 'id': 'tlscredsx509server0'," - " 'endpoint': 'server'," - " 'dir': %s," - " 'sanity-check': true," - " 'verify-peer': %i} }", - data->workdir, args->verifyclient); - migrate_set_parameter_str(to, "tls-creds", "tlscredsx509server0"); - - if (args->authzclient) { - qtest_qmp_assert_success(to, - "{ 'execute': 'object-add'," - " 'arguments': { 'qom-type': 'authz-simple'," - " 'id': 'tlsauthz0'," - " 'identity': %s} }", - "CN=" QCRYPTO_TLS_TEST_CLIENT_NAME); - migrate_set_parameter_str(to, "tls-authz", "tlsauthz0"); - } - - return data; -} - -/* - * The normal case: match server's cert hostname against - * whatever host we were telling QEMU to connect to (if any) - */ -static void * -test_migrate_tls_x509_start_default_host(QTestState *from, - QTestState *to) -{ - TestMigrateTLSX509 args = { - .verifyclient = true, - .clientcert = true, - .certipaddr = "127.0.0.1" - }; - return test_migrate_tls_x509_start_common(from, to, &args); -} - -/* - * The unusual case: the server's cert is different from - * the address we're telling QEMU to connect to (if any), - * so we must give QEMU an explicit hostname to validate - */ -static void * -test_migrate_tls_x509_start_override_host(QTestState *from, - QTestState *to) -{ - TestMigrateTLSX509 args = { - .verifyclient = true, - .clientcert = true, - .certhostname = "qemu.org", - }; - return test_migrate_tls_x509_start_common(from, to, &args); -} - -/* - * The unusual case: the server's cert is different from - * the address we're telling QEMU to connect to, and so we - * expect the client to reject the server - */ -static void * -test_migrate_tls_x509_start_mismatch_host(QTestState *from, - QTestState *to) -{ - TestMigrateTLSX509 args = { - .verifyclient = true, - .clientcert = true, - .certipaddr = "10.0.0.1", - }; - return test_migrate_tls_x509_start_common(from, to, &args); -} - -static void * -test_migrate_tls_x509_start_friendly_client(QTestState *from, - QTestState *to) -{ - TestMigrateTLSX509 args = { - .verifyclient = true, - .clientcert = true, - .authzclient = true, - .certipaddr = "127.0.0.1", - }; - return test_migrate_tls_x509_start_common(from, to, &args); -} - -static void * -test_migrate_tls_x509_start_hostile_client(QTestState *from, - QTestState *to) -{ - TestMigrateTLSX509 args = { - .verifyclient = true, - .clientcert = true, - .hostileclient = true, - .authzclient = true, - .certipaddr = "127.0.0.1", - }; - return test_migrate_tls_x509_start_common(from, to, &args); -} - -/* - * The case with no client certificate presented, - * and no server verification - */ -static void * -test_migrate_tls_x509_start_allow_anon_client(QTestState *from, - QTestState *to) -{ - TestMigrateTLSX509 args = { - .certipaddr = "127.0.0.1", - }; - return test_migrate_tls_x509_start_common(from, to, &args); -} - -/* - * The case with no client certificate presented, - * and server verification rejecting - */ -static void * -test_migrate_tls_x509_start_reject_anon_client(QTestState *from, - QTestState *to) -{ - TestMigrateTLSX509 args = { - .verifyclient = true, - .certipaddr = "127.0.0.1", - }; - return test_migrate_tls_x509_start_common(from, to, &args); -} - -static void -test_migrate_tls_x509_finish(QTestState *from, - QTestState *to, - void *opaque) -{ - TestMigrateTLSX509Data *data = opaque; - - test_tls_cleanup(data->keyfile); - g_free(data->keyfile); - - unlink(data->cacert); - g_free(data->cacert); - unlink(data->servercert); - g_free(data->servercert); - unlink(data->serverkey); - g_free(data->serverkey); - - if (data->clientcert) { - unlink(data->clientcert); - g_free(data->clientcert); - } - if (data->clientkey) { - unlink(data->clientkey); - g_free(data->clientkey); - } - - rmdir(data->workdir); - g_free(data->workdir); - - g_free(data); -} -#endif /* CONFIG_TASN1 */ -#endif /* CONFIG_GNUTLS */ - -static int migrate_postcopy_prepare(QTestState **from_ptr, - QTestState **to_ptr, - MigrateCommon *args) -{ - QTestState *from, *to; - - if (test_migrate_start(&from, &to, "defer", &args->start)) { - return -1; - } - - if (args->start_hook) { - args->postcopy_data = args->start_hook(from, to); - } - - migrate_set_capability(from, "postcopy-ram", true); - migrate_set_capability(to, "postcopy-ram", true); - migrate_set_capability(to, "postcopy-blocktime", true); - - if (args->postcopy_preempt) { - migrate_set_capability(from, "postcopy-preempt", true); - migrate_set_capability(to, "postcopy-preempt", true); - } - - migrate_ensure_non_converge(from); - - migrate_prepare_for_dirty_mem(from); - qtest_qmp_assert_success(to, "{ 'execute': 'migrate-incoming'," - " 'arguments': { " - " 'channels': [ { 'channel-type': 'main'," - " 'addr': { 'transport': 'socket'," - " 'type': 'inet'," - " 'host': '127.0.0.1'," - " 'port': '0' } } ] } }"); - - /* Wait for the first serial output from the source */ - wait_for_serial("src_serial"); - wait_for_suspend(from, &src_state); - - migrate_qmp(from, to, NULL, NULL, "{}"); - - migrate_wait_for_dirty_mem(from, to); - - *from_ptr = from; - *to_ptr = to; - - return 0; -} - -static void migrate_postcopy_complete(QTestState *from, QTestState *to, - MigrateCommon *args) -{ - wait_for_migration_complete(from); - - if (args->start.suspend_me) { - /* wakeup succeeds only if guest is suspended */ - qtest_qmp_assert_success(to, "{'execute': 'system_wakeup'}"); - } - - /* Make sure we get at least one "B" on destination */ - wait_for_serial("dest_serial"); - - if (uffd_feature_thread_id) { - read_blocktime(to); - } - - if (args->finish_hook) { - args->finish_hook(from, to, args->postcopy_data); - args->postcopy_data = NULL; - } - - test_migrate_end(from, to, true); -} - -static void test_postcopy_common(MigrateCommon *args) -{ - QTestState *from, *to; - - if (migrate_postcopy_prepare(&from, &to, args)) { - return; - } - migrate_postcopy_start(from, to); - migrate_postcopy_complete(from, to, args); -} - -static void test_postcopy(void) -{ - MigrateCommon args = { }; - - test_postcopy_common(&args); -} - -static void test_postcopy_suspend(void) -{ - MigrateCommon args = { - .start.suspend_me = true, - }; - - test_postcopy_common(&args); -} - -static void test_postcopy_preempt(void) -{ - MigrateCommon args = { - .postcopy_preempt = true, - }; - - test_postcopy_common(&args); -} - -#ifdef CONFIG_GNUTLS -static void test_postcopy_tls_psk(void) -{ - MigrateCommon args = { - .start_hook = test_migrate_tls_psk_start_match, - .finish_hook = test_migrate_tls_psk_finish, - }; - - test_postcopy_common(&args); -} - -static void test_postcopy_preempt_tls_psk(void) -{ - MigrateCommon args = { - .postcopy_preempt = true, - .start_hook = test_migrate_tls_psk_start_match, - .finish_hook = test_migrate_tls_psk_finish, - }; - - test_postcopy_common(&args); -} -#endif - -static void wait_for_postcopy_status(QTestState *one, const char *status) -{ - wait_for_migration_status(one, status, - (const char * []) { "failed", "active", - "completed", NULL }); -} - -static void postcopy_recover_fail(QTestState *from, QTestState *to, - PostcopyRecoveryFailStage stage) -{ -#ifndef _WIN32 - bool fail_early = (stage == POSTCOPY_FAIL_CHANNEL_ESTABLISH); - int ret, pair1[2], pair2[2]; - char c; - - g_assert(stage > POSTCOPY_FAIL_NONE && stage < POSTCOPY_FAIL_MAX); - - /* Create two unrelated socketpairs */ - ret = qemu_socketpair(PF_LOCAL, SOCK_STREAM, 0, pair1); - g_assert_cmpint(ret, ==, 0); - - ret = qemu_socketpair(PF_LOCAL, SOCK_STREAM, 0, pair2); - g_assert_cmpint(ret, ==, 0); - - /* - * Give the guests unpaired ends of the sockets, so they'll all blocked - * at reading. This mimics a wrong channel established. - */ - qtest_qmp_fds_assert_success(from, &pair1[0], 1, - "{ 'execute': 'getfd'," - " 'arguments': { 'fdname': 'fd-mig' }}"); - qtest_qmp_fds_assert_success(to, &pair2[0], 1, - "{ 'execute': 'getfd'," - " 'arguments': { 'fdname': 'fd-mig' }}"); - - /* - * Write the 1st byte as QEMU_VM_COMMAND (0x8) for the dest socket, to - * emulate the 1st byte of a real recovery, but stops from there to - * keep dest QEMU in RECOVER. This is needed so that we can kick off - * the recover process on dest QEMU (by triggering the G_IO_IN event). - * - * NOTE: this trick is not needed on src QEMUs, because src doesn't - * rely on an pre-existing G_IO_IN event, so it will always trigger the - * upcoming recovery anyway even if it can read nothing. - */ -#define QEMU_VM_COMMAND 0x08 - c = QEMU_VM_COMMAND; - ret = send(pair2[1], &c, 1, 0); - g_assert_cmpint(ret, ==, 1); - - if (stage == POSTCOPY_FAIL_CHANNEL_ESTABLISH) { - /* - * This will make src QEMU to fail at an early stage when trying to - * resume later, where it shouldn't reach RECOVER stage at all. - */ - close(pair1[1]); - } - - migrate_recover(to, "fd:fd-mig"); - migrate_qmp(from, to, "fd:fd-mig", NULL, "{'resume': true}"); - - /* - * Source QEMU has an extra RECOVER_SETUP phase, dest doesn't have it. - * Make sure it appears along the way. - */ - migration_event_wait(from, "postcopy-recover-setup"); - - if (fail_early) { - /* - * When fails at reconnection, src QEMU will automatically goes - * back to PAUSED state. Making sure there is an event in this - * case: Libvirt relies on this to detect early reconnection - * errors. - */ - migration_event_wait(from, "postcopy-paused"); - } else { - /* - * We want to test "fail later" at RECOVER stage here. Make sure - * both QEMU instances will go into RECOVER stage first, then test - * kicking them out using migrate-pause. - * - * Explicitly check the RECOVER event on src, that's what Libvirt - * relies on, rather than polling. - */ - migration_event_wait(from, "postcopy-recover"); - wait_for_postcopy_status(from, "postcopy-recover"); - - /* Need an explicit kick on src QEMU in this case */ - migrate_pause(from); - } - - /* - * For all failure cases, we'll reach such states on both sides now. - * Check them. - */ - wait_for_postcopy_status(from, "postcopy-paused"); - wait_for_postcopy_status(to, "postcopy-recover"); - - /* - * Kick dest QEMU out too. This is normally not needed in reality - * because when the channel is shutdown it should also happen on src. - * However here we used separate socket pairs so we need to do that - * explicitly. - */ - migrate_pause(to); - wait_for_postcopy_status(to, "postcopy-paused"); - - close(pair1[0]); - close(pair2[0]); - close(pair2[1]); - - if (stage != POSTCOPY_FAIL_CHANNEL_ESTABLISH) { - close(pair1[1]); - } -#endif -} - -static void test_postcopy_recovery_common(MigrateCommon *args) -{ - QTestState *from, *to; - g_autofree char *uri = NULL; - - /* Always hide errors for postcopy recover tests since they're expected */ - args->start.hide_stderr = true; - - if (migrate_postcopy_prepare(&from, &to, args)) { - return; - } - - /* Turn postcopy speed down, 4K/s is slow enough on any machines */ - migrate_set_parameter_int(from, "max-postcopy-bandwidth", 4096); - - /* Now we start the postcopy */ - migrate_postcopy_start(from, to); - - /* - * Wait until postcopy is really started; we can only run the - * migrate-pause command during a postcopy - */ - wait_for_migration_status(from, "postcopy-active", NULL); - - /* - * Manually stop the postcopy migration. This emulates a network - * failure with the migration socket - */ - migrate_pause(from); - - /* - * Wait for destination side to reach postcopy-paused state. The - * migrate-recover command can only succeed if destination machine - * is in the paused state - */ - wait_for_postcopy_status(to, "postcopy-paused"); - wait_for_postcopy_status(from, "postcopy-paused"); - - if (args->postcopy_recovery_fail_stage) { - /* - * Test when a wrong socket specified for recover, and then the - * ability to kick it out, and continue with a correct socket. - */ - postcopy_recover_fail(from, to, args->postcopy_recovery_fail_stage); - /* continue with a good recovery */ - } - - /* - * Create a new socket to emulate a new channel that is different - * from the broken migration channel; tell the destination to - * listen to the new port - */ - uri = g_strdup_printf("unix:%s/migsocket-recover", tmpfs); - migrate_recover(to, uri); - - /* - * Try to rebuild the migration channel using the resume flag and - * the newly created channel - */ - migrate_qmp(from, to, uri, NULL, "{'resume': true}"); - - /* Restore the postcopy bandwidth to unlimited */ - migrate_set_parameter_int(from, "max-postcopy-bandwidth", 0); - - migrate_postcopy_complete(from, to, args); -} - -static void test_postcopy_recovery(void) -{ - MigrateCommon args = { }; - - test_postcopy_recovery_common(&args); -} - -static void test_postcopy_recovery_fail_handshake(void) -{ - MigrateCommon args = { - .postcopy_recovery_fail_stage = POSTCOPY_FAIL_RECOVERY, - }; - - test_postcopy_recovery_common(&args); -} - -static void test_postcopy_recovery_fail_reconnect(void) -{ - MigrateCommon args = { - .postcopy_recovery_fail_stage = POSTCOPY_FAIL_CHANNEL_ESTABLISH, - }; - - test_postcopy_recovery_common(&args); -} - -#ifdef CONFIG_GNUTLS -static void test_postcopy_recovery_tls_psk(void) -{ - MigrateCommon args = { - .start_hook = test_migrate_tls_psk_start_match, - .finish_hook = test_migrate_tls_psk_finish, - }; - - test_postcopy_recovery_common(&args); -} -#endif - -static void test_postcopy_preempt_recovery(void) -{ - MigrateCommon args = { - .postcopy_preempt = true, - }; - - test_postcopy_recovery_common(&args); -} - -#ifdef CONFIG_GNUTLS -/* This contains preempt+recovery+tls test altogether */ -static void test_postcopy_preempt_all(void) -{ - MigrateCommon args = { - .postcopy_preempt = true, - .start_hook = test_migrate_tls_psk_start_match, - .finish_hook = test_migrate_tls_psk_finish, - }; - - test_postcopy_recovery_common(&args); -} - -#endif - -static void test_baddest(void) -{ - MigrateStart args = { - .hide_stderr = true - }; - QTestState *from, *to; - - if (test_migrate_start(&from, &to, "tcp:127.0.0.1:0", &args)) { - return; - } - migrate_qmp(from, to, "tcp:127.0.0.1:0", NULL, "{}"); - wait_for_migration_fail(from, false); - test_migrate_end(from, to, false); -} - -#ifndef _WIN32 -static void test_analyze_script(void) -{ - MigrateStart args = { - .opts_source = "-uuid 11111111-1111-1111-1111-111111111111", - }; - QTestState *from, *to; - g_autofree char *uri = NULL; - g_autofree char *file = NULL; - int pid, wstatus; - const char *python = g_getenv("PYTHON"); - - if (!python) { - g_test_skip("PYTHON variable not set"); - return; - } - - /* dummy url */ - if (test_migrate_start(&from, &to, "tcp:127.0.0.1:0", &args)) { - return; - } - - /* - * Setting these two capabilities causes the "configuration" - * vmstate to include subsections for them. The script needs to - * parse those subsections properly. - */ - migrate_set_capability(from, "validate-uuid", true); - migrate_set_capability(from, "x-ignore-shared", true); - - file = g_strdup_printf("%s/migfile", tmpfs); - uri = g_strdup_printf("exec:cat > %s", file); - - migrate_ensure_converge(from); - migrate_qmp(from, to, uri, NULL, "{}"); - wait_for_migration_complete(from); - - pid = fork(); - if (!pid) { - close(1); - open("/dev/null", O_WRONLY); - execl(python, python, ANALYZE_SCRIPT, "-f", file, NULL); - g_assert_not_reached(); - } - - g_assert(waitpid(pid, &wstatus, 0) == pid); - if (!WIFEXITED(wstatus) || WEXITSTATUS(wstatus) != 0) { - g_test_message("Failed to analyze the migration stream"); - g_test_fail(); - } - test_migrate_end(from, to, false); - cleanup("migfile"); -} -#endif - -static void test_precopy_common(MigrateCommon *args) -{ - QTestState *from, *to; - void *data_hook = NULL; - - if (test_migrate_start(&from, &to, args->listen_uri, &args->start)) { - return; - } - - if (args->start_hook) { - data_hook = args->start_hook(from, to); - } - - /* Wait for the first serial output from the source */ - if (args->result == MIG_TEST_SUCCEED) { - wait_for_serial("src_serial"); - wait_for_suspend(from, &src_state); - } - - if (args->live) { - migrate_ensure_non_converge(from); - migrate_prepare_for_dirty_mem(from); - } else { - /* - * Testing non-live migration, we allow it to run at - * full speed to ensure short test case duration. - * For tests expected to fail, we don't need to - * change anything. - */ - if (args->result == MIG_TEST_SUCCEED) { - qtest_qmp_assert_success(from, "{ 'execute' : 'stop'}"); - wait_for_stop(from, &src_state); - migrate_ensure_converge(from); - } - } - - if (args->result == MIG_TEST_QMP_ERROR) { - migrate_qmp_fail(from, args->connect_uri, args->connect_channels, "{}"); - goto finish; - } - - migrate_qmp(from, to, args->connect_uri, args->connect_channels, "{}"); - - if (args->result != MIG_TEST_SUCCEED) { - bool allow_active = args->result == MIG_TEST_FAIL; - wait_for_migration_fail(from, allow_active); - - if (args->result == MIG_TEST_FAIL_DEST_QUIT_ERR) { - qtest_set_expected_status(to, EXIT_FAILURE); - } - } else { - if (args->live) { - /* - * For initial iteration(s) we must do a full pass, - * but for the final iteration, we need only wait - * for some dirty mem before switching to converge - */ - while (args->iterations > 1) { - wait_for_migration_pass(from); - args->iterations--; - } - migrate_wait_for_dirty_mem(from, to); - - migrate_ensure_converge(from); - - /* - * We do this first, as it has a timeout to stop us - * hanging forever if migration didn't converge - */ - wait_for_migration_complete(from); - - wait_for_stop(from, &src_state); - - } else { - wait_for_migration_complete(from); - /* - * Must wait for dst to finish reading all incoming - * data on the socket before issuing 'cont' otherwise - * it'll be ignored - */ - wait_for_migration_complete(to); - - qtest_qmp_assert_success(to, "{ 'execute' : 'cont'}"); - } - - wait_for_resume(to, &dst_state); - - if (args->start.suspend_me) { - /* wakeup succeeds only if guest is suspended */ - qtest_qmp_assert_success(to, "{'execute': 'system_wakeup'}"); - } - - wait_for_serial("dest_serial"); - } - -finish: - if (args->finish_hook) { - args->finish_hook(from, to, data_hook); - } - - test_migrate_end(from, to, args->result == MIG_TEST_SUCCEED); -} - -static void file_dirty_offset_region(void) -{ - g_autofree char *path = g_strdup_printf("%s/%s", tmpfs, FILE_TEST_FILENAME); - size_t size = FILE_TEST_OFFSET; - g_autofree char *data = g_new0(char, size); - - memset(data, FILE_TEST_MARKER, size); - g_assert(g_file_set_contents(path, data, size, NULL)); -} - -static void file_check_offset_region(void) -{ - g_autofree char *path = g_strdup_printf("%s/%s", tmpfs, FILE_TEST_FILENAME); - size_t size = FILE_TEST_OFFSET; - g_autofree char *expected = g_new0(char, size); - g_autofree char *actual = NULL; - uint64_t *stream_start; - - /* - * Ensure the skipped offset region's data has not been touched - * and the migration stream starts at the right place. - */ - - memset(expected, FILE_TEST_MARKER, size); - - g_assert(g_file_get_contents(path, &actual, NULL, NULL)); - g_assert(!memcmp(actual, expected, size)); - - stream_start = (uint64_t *)(actual + size); - g_assert_cmpint(cpu_to_be64(*stream_start) >> 32, ==, QEMU_VM_FILE_MAGIC); -} - -static void test_file_common(MigrateCommon *args, bool stop_src) -{ - QTestState *from, *to; - void *data_hook = NULL; - bool check_offset = false; - - if (test_migrate_start(&from, &to, args->listen_uri, &args->start)) { - return; - } - - /* - * File migration is never live. We can keep the source VM running - * during migration, but the destination will not be running - * concurrently. - */ - g_assert_false(args->live); - - if (g_strrstr(args->connect_uri, "offset=")) { - check_offset = true; - /* - * This comes before the start_hook because it's equivalent to - * a management application creating the file and writing to - * it so hooks should expect the file to be already present. - */ - file_dirty_offset_region(); - } - - if (args->start_hook) { - data_hook = args->start_hook(from, to); - } - - migrate_ensure_converge(from); - wait_for_serial("src_serial"); - - if (stop_src) { - qtest_qmp_assert_success(from, "{ 'execute' : 'stop'}"); - wait_for_stop(from, &src_state); - } - - if (args->result == MIG_TEST_QMP_ERROR) { - migrate_qmp_fail(from, args->connect_uri, NULL, "{}"); - goto finish; - } - - migrate_qmp(from, to, args->connect_uri, NULL, "{}"); - wait_for_migration_complete(from); - - /* - * We need to wait for the source to finish before starting the - * destination. - */ - migrate_incoming_qmp(to, args->connect_uri, "{}"); - wait_for_migration_complete(to); - - if (stop_src) { - qtest_qmp_assert_success(to, "{ 'execute' : 'cont'}"); - } - wait_for_resume(to, &dst_state); - - wait_for_serial("dest_serial"); - - if (check_offset) { - file_check_offset_region(); - } - -finish: - if (args->finish_hook) { - args->finish_hook(from, to, data_hook); - } - - test_migrate_end(from, to, args->result == MIG_TEST_SUCCEED); -} - -static void test_precopy_unix_plain(void) -{ - g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); - MigrateCommon args = { - .listen_uri = uri, - .connect_uri = uri, - /* - * The simplest use case of precopy, covering smoke tests of - * get-dirty-log dirty tracking. - */ - .live = true, - }; - - test_precopy_common(&args); -} - -static void test_precopy_unix_suspend_live(void) -{ - g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); - MigrateCommon args = { - .listen_uri = uri, - .connect_uri = uri, - /* - * despite being live, the test is fast because the src - * suspends immediately. - */ - .live = true, - .start.suspend_me = true, - }; - - test_precopy_common(&args); -} - -static void test_precopy_unix_suspend_notlive(void) -{ - g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); - MigrateCommon args = { - .listen_uri = uri, - .connect_uri = uri, - .start.suspend_me = true, - }; - - test_precopy_common(&args); -} - -static void test_precopy_unix_dirty_ring(void) -{ - g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); - MigrateCommon args = { - .start = { - .use_dirty_ring = true, - }, - .listen_uri = uri, - .connect_uri = uri, - /* - * Besides the precopy/unix basic test, cover dirty ring interface - * rather than get-dirty-log. - */ - .live = true, - }; - - test_precopy_common(&args); -} - -#ifdef CONFIG_GNUTLS -static void test_precopy_unix_tls_psk(void) -{ - g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); - MigrateCommon args = { - .connect_uri = uri, - .listen_uri = uri, - .start_hook = test_migrate_tls_psk_start_match, - .finish_hook = test_migrate_tls_psk_finish, - }; - - test_precopy_common(&args); -} - -#ifdef CONFIG_TASN1 -static void test_precopy_unix_tls_x509_default_host(void) -{ - g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); - MigrateCommon args = { - .start = { - .hide_stderr = true, - }, - .connect_uri = uri, - .listen_uri = uri, - .start_hook = test_migrate_tls_x509_start_default_host, - .finish_hook = test_migrate_tls_x509_finish, - .result = MIG_TEST_FAIL_DEST_QUIT_ERR, - }; - test_precopy_common(&args); -} - -static void test_precopy_unix_tls_x509_override_host(void) +static void parse_args(int *argc_p, char ***argv_p, bool *full_set) { - g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); - MigrateCommon args = { - .connect_uri = uri, - .listen_uri = uri, - .start_hook = test_migrate_tls_x509_start_override_host, - .finish_hook = test_migrate_tls_x509_finish, - }; - - test_precopy_common(&args); -} -#endif /* CONFIG_TASN1 */ -#endif /* CONFIG_GNUTLS */ - -#if 0 -/* Currently upset on aarch64 TCG */ -static void test_ignore_shared(void) -{ - g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); - QTestState *from, *to; - - if (test_migrate_start(&from, &to, uri, false, true, NULL, NULL)) { - return; - } - - migrate_ensure_non_converge(from); - migrate_prepare_for_dirty_mem(from); + int argc = *argc_p; + char **argv = *argv_p; + int i, j; - migrate_set_capability(from, "x-ignore-shared", true); - migrate_set_capability(to, "x-ignore-shared", true); - - /* Wait for the first serial output from the source */ - wait_for_serial("src_serial"); - - migrate_qmp(from, to, uri, NULL, "{}"); - - migrate_wait_for_dirty_mem(from, to); - - wait_for_stop(from, &src_state); - - qtest_qmp_eventwait(to, "RESUME"); - - wait_for_serial("dest_serial"); - wait_for_migration_complete(from); - - /* Check whether shared RAM has been really skipped */ - g_assert_cmpint(read_ram_property_int(from, "transferred"), <, 1024 * 1024); - - test_migrate_end(from, to, true); -} -#endif - -static void * -test_migrate_xbzrle_start(QTestState *from, - QTestState *to) -{ - migrate_set_parameter_int(from, "xbzrle-cache-size", 33554432); - - migrate_set_capability(from, "xbzrle", true); - migrate_set_capability(to, "xbzrle", true); - - return NULL; -} - -static void test_precopy_unix_xbzrle(void) -{ - g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); - MigrateCommon args = { - .connect_uri = uri, - .listen_uri = uri, - .start_hook = test_migrate_xbzrle_start, - .iterations = 2, - /* - * XBZRLE needs pages to be modified when doing the 2nd+ round - * iteration to have real data pushed to the stream. - */ - .live = true, - }; - - test_precopy_common(&args); -} - -static void test_precopy_file(void) -{ - g_autofree char *uri = g_strdup_printf("file:%s/%s", tmpfs, - FILE_TEST_FILENAME); - MigrateCommon args = { - .connect_uri = uri, - .listen_uri = "defer", - }; - - test_file_common(&args, true); -} - -#ifndef _WIN32 -static void fdset_add_fds(QTestState *qts, const char *file, int flags, - int num_fds, bool direct_io) -{ - for (int i = 0; i < num_fds; i++) { - int fd; - -#ifdef O_DIRECT - /* only secondary channels can use direct-io */ - if (direct_io && i != 0) { - flags |= O_DIRECT; + j = 1; + for (i = 1; i < argc; i++) { + if (g_str_equal(argv[i], "--full")) { + *full_set = true; + continue; } -#endif - - fd = open(file, flags, 0660); - assert(fd != -1); - - qtest_qmp_fds_assert_success(qts, &fd, 1, "{'execute': 'add-fd', " - "'arguments': {'fdset-id': 1}}"); - close(fd); - } -} - -static void *file_offset_fdset_start_hook(QTestState *from, QTestState *to) -{ - g_autofree char *file = g_strdup_printf("%s/%s", tmpfs, FILE_TEST_FILENAME); - - fdset_add_fds(from, file, O_WRONLY, 1, false); - fdset_add_fds(to, file, O_RDONLY, 1, false); - - return NULL; -} - -static void test_precopy_file_offset_fdset(void) -{ - g_autofree char *uri = g_strdup_printf("file:/dev/fdset/1,offset=%d", - FILE_TEST_OFFSET); - MigrateCommon args = { - .connect_uri = uri, - .listen_uri = "defer", - .start_hook = file_offset_fdset_start_hook, - }; - - test_file_common(&args, false); -} -#endif - -static void test_precopy_file_offset(void) -{ - g_autofree char *uri = g_strdup_printf("file:%s/%s,offset=%d", tmpfs, - FILE_TEST_FILENAME, - FILE_TEST_OFFSET); - MigrateCommon args = { - .connect_uri = uri, - .listen_uri = "defer", - }; - - test_file_common(&args, false); -} - -static void test_precopy_file_offset_bad(void) -{ - /* using a value not supported by qemu_strtosz() */ - g_autofree char *uri = g_strdup_printf("file:%s/%s,offset=0x20M", - tmpfs, FILE_TEST_FILENAME); - MigrateCommon args = { - .connect_uri = uri, - .listen_uri = "defer", - .result = MIG_TEST_QMP_ERROR, - }; - - test_file_common(&args, false); -} - -static void *test_mode_reboot_start(QTestState *from, QTestState *to) -{ - migrate_set_parameter_str(from, "mode", "cpr-reboot"); - migrate_set_parameter_str(to, "mode", "cpr-reboot"); - - migrate_set_capability(from, "x-ignore-shared", true); - migrate_set_capability(to, "x-ignore-shared", true); - - return NULL; -} - -static void *migrate_mapped_ram_start(QTestState *from, QTestState *to) -{ - migrate_set_capability(from, "mapped-ram", true); - migrate_set_capability(to, "mapped-ram", true); - - return NULL; -} - -static void test_mode_reboot(void) -{ - g_autofree char *uri = g_strdup_printf("file:%s/%s", tmpfs, - FILE_TEST_FILENAME); - MigrateCommon args = { - .start.use_shmem = true, - .connect_uri = uri, - .listen_uri = "defer", - .start_hook = test_mode_reboot_start - }; - - test_file_common(&args, true); -} - -static void test_precopy_file_mapped_ram_live(void) -{ - g_autofree char *uri = g_strdup_printf("file:%s/%s", tmpfs, - FILE_TEST_FILENAME); - MigrateCommon args = { - .connect_uri = uri, - .listen_uri = "defer", - .start_hook = migrate_mapped_ram_start, - }; - - test_file_common(&args, false); -} - -static void test_precopy_file_mapped_ram(void) -{ - g_autofree char *uri = g_strdup_printf("file:%s/%s", tmpfs, - FILE_TEST_FILENAME); - MigrateCommon args = { - .connect_uri = uri, - .listen_uri = "defer", - .start_hook = migrate_mapped_ram_start, - }; - - test_file_common(&args, true); -} - -static void *migrate_multifd_mapped_ram_start(QTestState *from, QTestState *to) -{ - migrate_mapped_ram_start(from, to); - - migrate_set_parameter_int(from, "multifd-channels", 4); - migrate_set_parameter_int(to, "multifd-channels", 4); - - migrate_set_capability(from, "multifd", true); - migrate_set_capability(to, "multifd", true); - - return NULL; -} - -static void test_multifd_file_mapped_ram_live(void) -{ - g_autofree char *uri = g_strdup_printf("file:%s/%s", tmpfs, - FILE_TEST_FILENAME); - MigrateCommon args = { - .connect_uri = uri, - .listen_uri = "defer", - .start_hook = migrate_multifd_mapped_ram_start, - }; - - test_file_common(&args, false); -} - -static void test_multifd_file_mapped_ram(void) -{ - g_autofree char *uri = g_strdup_printf("file:%s/%s", tmpfs, - FILE_TEST_FILENAME); - MigrateCommon args = { - .connect_uri = uri, - .listen_uri = "defer", - .start_hook = migrate_multifd_mapped_ram_start, - }; - - test_file_common(&args, true); -} - -static void *multifd_mapped_ram_dio_start(QTestState *from, QTestState *to) -{ - migrate_multifd_mapped_ram_start(from, to); - - migrate_set_parameter_bool(from, "direct-io", true); - migrate_set_parameter_bool(to, "direct-io", true); - - return NULL; -} - -static void test_multifd_file_mapped_ram_dio(void) -{ - g_autofree char *uri = g_strdup_printf("file:%s/%s", tmpfs, - FILE_TEST_FILENAME); - MigrateCommon args = { - .connect_uri = uri, - .listen_uri = "defer", - .start_hook = multifd_mapped_ram_dio_start, - }; - - if (!probe_o_direct_support(tmpfs)) { - g_test_skip("Filesystem does not support O_DIRECT"); - return; - } - - test_file_common(&args, true); -} - -#ifndef _WIN32 -static void multifd_mapped_ram_fdset_end(QTestState *from, QTestState *to, - void *opaque) -{ - QDict *resp; - QList *fdsets; - - /* - * Remove the fdsets after migration, otherwise a second migration - * would fail due fdset reuse. - */ - qtest_qmp_assert_success(from, "{'execute': 'remove-fd', " - "'arguments': { 'fdset-id': 1}}"); - - /* - * Make sure no fdsets are left after migration, otherwise a - * second migration would fail due fdset reuse. - */ - resp = qtest_qmp(from, "{'execute': 'query-fdsets', " - "'arguments': {}}"); - g_assert(qdict_haskey(resp, "return")); - fdsets = qdict_get_qlist(resp, "return"); - g_assert(fdsets && qlist_empty(fdsets)); - qobject_unref(resp); -} - -static void *multifd_mapped_ram_fdset_dio(QTestState *from, QTestState *to) -{ - g_autofree char *file = g_strdup_printf("%s/%s", tmpfs, FILE_TEST_FILENAME); - - fdset_add_fds(from, file, O_WRONLY, 2, true); - fdset_add_fds(to, file, O_RDONLY, 2, true); - - migrate_multifd_mapped_ram_start(from, to); - migrate_set_parameter_bool(from, "direct-io", true); - migrate_set_parameter_bool(to, "direct-io", true); - - return NULL; -} - -static void *multifd_mapped_ram_fdset(QTestState *from, QTestState *to) -{ - g_autofree char *file = g_strdup_printf("%s/%s", tmpfs, FILE_TEST_FILENAME); - - fdset_add_fds(from, file, O_WRONLY, 2, false); - fdset_add_fds(to, file, O_RDONLY, 2, false); - - migrate_multifd_mapped_ram_start(from, to); - - return NULL; -} - -static void test_multifd_file_mapped_ram_fdset(void) -{ - g_autofree char *uri = g_strdup_printf("file:/dev/fdset/1,offset=%d", - FILE_TEST_OFFSET); - MigrateCommon args = { - .connect_uri = uri, - .listen_uri = "defer", - .start_hook = multifd_mapped_ram_fdset, - .finish_hook = multifd_mapped_ram_fdset_end, - }; - - test_file_common(&args, true); -} - -static void test_multifd_file_mapped_ram_fdset_dio(void) -{ - g_autofree char *uri = g_strdup_printf("file:/dev/fdset/1,offset=%d", - FILE_TEST_OFFSET); - MigrateCommon args = { - .connect_uri = uri, - .listen_uri = "defer", - .start_hook = multifd_mapped_ram_fdset_dio, - .finish_hook = multifd_mapped_ram_fdset_end, - }; - - if (!probe_o_direct_support(tmpfs)) { - g_test_skip("Filesystem does not support O_DIRECT"); - return; - } - - test_file_common(&args, true); -} -#endif /* !_WIN32 */ - -static void test_precopy_tcp_plain(void) -{ - MigrateCommon args = { - .listen_uri = "tcp:127.0.0.1:0", - }; - - test_precopy_common(&args); -} - -static void *test_migrate_switchover_ack_start(QTestState *from, QTestState *to) -{ - - migrate_set_capability(from, "return-path", true); - migrate_set_capability(to, "return-path", true); - - migrate_set_capability(from, "switchover-ack", true); - migrate_set_capability(to, "switchover-ack", true); - - return NULL; -} - -static void test_precopy_tcp_switchover_ack(void) -{ - MigrateCommon args = { - .listen_uri = "tcp:127.0.0.1:0", - .start_hook = test_migrate_switchover_ack_start, - /* - * Source VM must be running in order to consider the switchover ACK - * when deciding to do switchover or not. - */ - .live = true, - }; - - test_precopy_common(&args); -} - -#ifdef CONFIG_GNUTLS -static void test_precopy_tcp_tls_psk_match(void) -{ - MigrateCommon args = { - .listen_uri = "tcp:127.0.0.1:0", - .start_hook = test_migrate_tls_psk_start_match, - .finish_hook = test_migrate_tls_psk_finish, - }; - - test_precopy_common(&args); -} - -static void test_precopy_tcp_tls_psk_mismatch(void) -{ - MigrateCommon args = { - .start = { - .hide_stderr = true, - }, - .listen_uri = "tcp:127.0.0.1:0", - .start_hook = test_migrate_tls_psk_start_mismatch, - .finish_hook = test_migrate_tls_psk_finish, - .result = MIG_TEST_FAIL, - }; - - test_precopy_common(&args); -} - -#ifdef CONFIG_TASN1 -static void test_precopy_tcp_tls_x509_default_host(void) -{ - MigrateCommon args = { - .listen_uri = "tcp:127.0.0.1:0", - .start_hook = test_migrate_tls_x509_start_default_host, - .finish_hook = test_migrate_tls_x509_finish, - }; - - test_precopy_common(&args); -} - -static void test_precopy_tcp_tls_x509_override_host(void) -{ - MigrateCommon args = { - .listen_uri = "tcp:127.0.0.1:0", - .start_hook = test_migrate_tls_x509_start_override_host, - .finish_hook = test_migrate_tls_x509_finish, - }; - - test_precopy_common(&args); -} - -static void test_precopy_tcp_tls_x509_mismatch_host(void) -{ - MigrateCommon args = { - .start = { - .hide_stderr = true, - }, - .listen_uri = "tcp:127.0.0.1:0", - .start_hook = test_migrate_tls_x509_start_mismatch_host, - .finish_hook = test_migrate_tls_x509_finish, - .result = MIG_TEST_FAIL_DEST_QUIT_ERR, - }; - - test_precopy_common(&args); -} - -static void test_precopy_tcp_tls_x509_friendly_client(void) -{ - MigrateCommon args = { - .listen_uri = "tcp:127.0.0.1:0", - .start_hook = test_migrate_tls_x509_start_friendly_client, - .finish_hook = test_migrate_tls_x509_finish, - }; - - test_precopy_common(&args); -} - -static void test_precopy_tcp_tls_x509_hostile_client(void) -{ - MigrateCommon args = { - .start = { - .hide_stderr = true, - }, - .listen_uri = "tcp:127.0.0.1:0", - .start_hook = test_migrate_tls_x509_start_hostile_client, - .finish_hook = test_migrate_tls_x509_finish, - .result = MIG_TEST_FAIL, - }; - - test_precopy_common(&args); -} - -static void test_precopy_tcp_tls_x509_allow_anon_client(void) -{ - MigrateCommon args = { - .listen_uri = "tcp:127.0.0.1:0", - .start_hook = test_migrate_tls_x509_start_allow_anon_client, - .finish_hook = test_migrate_tls_x509_finish, - }; - - test_precopy_common(&args); -} - -static void test_precopy_tcp_tls_x509_reject_anon_client(void) -{ - MigrateCommon args = { - .start = { - .hide_stderr = true, - }, - .listen_uri = "tcp:127.0.0.1:0", - .start_hook = test_migrate_tls_x509_start_reject_anon_client, - .finish_hook = test_migrate_tls_x509_finish, - .result = MIG_TEST_FAIL, - }; - - test_precopy_common(&args); -} -#endif /* CONFIG_TASN1 */ -#endif /* CONFIG_GNUTLS */ - -#ifndef _WIN32 -static void *test_migrate_fd_start_hook(QTestState *from, - QTestState *to) -{ - int ret; - int pair[2]; - - /* Create two connected sockets for migration */ - ret = qemu_socketpair(PF_LOCAL, SOCK_STREAM, 0, pair); - g_assert_cmpint(ret, ==, 0); - - /* Send the 1st socket to the target */ - qtest_qmp_fds_assert_success(to, &pair[0], 1, - "{ 'execute': 'getfd'," - " 'arguments': { 'fdname': 'fd-mig' }}"); - close(pair[0]); - - /* Start incoming migration from the 1st socket */ - migrate_incoming_qmp(to, "fd:fd-mig", "{}"); - - /* Send the 2nd socket to the target */ - qtest_qmp_fds_assert_success(from, &pair[1], 1, - "{ 'execute': 'getfd'," - " 'arguments': { 'fdname': 'fd-mig' }}"); - close(pair[1]); - - return NULL; -} - -static void test_migrate_fd_finish_hook(QTestState *from, - QTestState *to, - void *opaque) -{ - QDict *rsp; - const char *error_desc; - - /* Test closing fds */ - /* We assume, that QEMU removes named fd from its list, - * so this should fail */ - rsp = qtest_qmp(from, "{ 'execute': 'closefd'," - " 'arguments': { 'fdname': 'fd-mig' }}"); - g_assert_true(qdict_haskey(rsp, "error")); - error_desc = qdict_get_str(qdict_get_qdict(rsp, "error"), "desc"); - g_assert_cmpstr(error_desc, ==, "File descriptor named 'fd-mig' not found"); - qobject_unref(rsp); - - rsp = qtest_qmp(to, "{ 'execute': 'closefd'," - " 'arguments': { 'fdname': 'fd-mig' }}"); - g_assert_true(qdict_haskey(rsp, "error")); - error_desc = qdict_get_str(qdict_get_qdict(rsp, "error"), "desc"); - g_assert_cmpstr(error_desc, ==, "File descriptor named 'fd-mig' not found"); - qobject_unref(rsp); -} - -static void test_migrate_precopy_fd_socket(void) -{ - MigrateCommon args = { - .listen_uri = "defer", - .connect_uri = "fd:fd-mig", - .start_hook = test_migrate_fd_start_hook, - .finish_hook = test_migrate_fd_finish_hook - }; - test_precopy_common(&args); -} - -static void *migrate_precopy_fd_file_start(QTestState *from, QTestState *to) -{ - g_autofree char *file = g_strdup_printf("%s/%s", tmpfs, FILE_TEST_FILENAME); - int src_flags = O_CREAT | O_RDWR; - int dst_flags = O_CREAT | O_RDWR; - int fds[2]; - - fds[0] = open(file, src_flags, 0660); - assert(fds[0] != -1); - - fds[1] = open(file, dst_flags, 0660); - assert(fds[1] != -1); - - - qtest_qmp_fds_assert_success(to, &fds[0], 1, - "{ 'execute': 'getfd'," - " 'arguments': { 'fdname': 'fd-mig' }}"); - - qtest_qmp_fds_assert_success(from, &fds[1], 1, - "{ 'execute': 'getfd'," - " 'arguments': { 'fdname': 'fd-mig' }}"); - - close(fds[0]); - close(fds[1]); - - return NULL; -} - -static void test_migrate_precopy_fd_file(void) -{ - MigrateCommon args = { - .listen_uri = "defer", - .connect_uri = "fd:fd-mig", - .start_hook = migrate_precopy_fd_file_start, - .finish_hook = test_migrate_fd_finish_hook - }; - test_file_common(&args, true); -} -#endif /* _WIN32 */ - -static void do_test_validate_uuid(MigrateStart *args, bool should_fail) -{ - g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); - QTestState *from, *to; - - if (test_migrate_start(&from, &to, uri, args)) { - return; - } - - /* - * UUID validation is at the begin of migration. So, the main process of - * migration is not interesting for us here. Thus, set huge downtime for - * very fast migration. - */ - migrate_set_parameter_int(from, "downtime-limit", 1000000); - migrate_set_capability(from, "validate-uuid", true); - - /* Wait for the first serial output from the source */ - wait_for_serial("src_serial"); - - migrate_qmp(from, to, uri, NULL, "{}"); - - if (should_fail) { - qtest_set_expected_status(to, EXIT_FAILURE); - wait_for_migration_fail(from, true); - } else { - wait_for_migration_complete(from); - } - - test_migrate_end(from, to, false); -} - -static void test_validate_uuid(void) -{ - MigrateStart args = { - .opts_source = "-uuid 11111111-1111-1111-1111-111111111111", - .opts_target = "-uuid 11111111-1111-1111-1111-111111111111", - }; - - do_test_validate_uuid(&args, false); -} - -static void test_validate_uuid_error(void) -{ - MigrateStart args = { - .opts_source = "-uuid 11111111-1111-1111-1111-111111111111", - .opts_target = "-uuid 22222222-2222-2222-2222-222222222222", - .hide_stderr = true, - }; - - do_test_validate_uuid(&args, true); -} - -static void test_validate_uuid_src_not_set(void) -{ - MigrateStart args = { - .opts_target = "-uuid 22222222-2222-2222-2222-222222222222", - .hide_stderr = true, - }; - - do_test_validate_uuid(&args, false); -} - -static void test_validate_uuid_dst_not_set(void) -{ - MigrateStart args = { - .opts_source = "-uuid 11111111-1111-1111-1111-111111111111", - .hide_stderr = true, - }; - - do_test_validate_uuid(&args, false); -} - -static void do_test_validate_uri_channel(MigrateCommon *args) -{ - QTestState *from, *to; - - if (test_migrate_start(&from, &to, args->listen_uri, &args->start)) { - return; - } - - /* Wait for the first serial output from the source */ - wait_for_serial("src_serial"); - - /* - * 'uri' and 'channels' validation is checked even before the migration - * starts. - */ - migrate_qmp_fail(from, args->connect_uri, args->connect_channels, "{}"); - test_migrate_end(from, to, false); -} - -static void test_validate_uri_channels_both_set(void) -{ - MigrateCommon args = { - .start = { - .hide_stderr = true, - }, - .listen_uri = "defer", - .connect_uri = "tcp:127.0.0.1:0", - .connect_channels = "[ { 'channel-type': 'main'," - " 'addr': { 'transport': 'socket'," - " 'type': 'inet'," - " 'host': '127.0.0.1'," - " 'port': '0' } } ]", - }; - - do_test_validate_uri_channel(&args); -} - -static void test_validate_uri_channels_none_set(void) -{ - MigrateCommon args = { - .start = { - .hide_stderr = true, - }, - .listen_uri = "defer", - }; - - do_test_validate_uri_channel(&args); -} - -/* - * The way auto_converge works, we need to do too many passes to - * run this test. Auto_converge logic is only run once every - * three iterations, so: - * - * - 3 iterations without auto_converge enabled - * - 3 iterations with pct = 5 - * - 3 iterations with pct = 30 - * - 3 iterations with pct = 55 - * - 3 iterations with pct = 80 - * - 3 iterations with pct = 95 (max(95, 80 + 25)) - * - * To make things even worse, we need to run the initial stage at - * 3MB/s so we enter autoconverge even when host is (over)loaded. - */ -static void test_migrate_auto_converge(void) -{ - g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); - MigrateStart args = {}; - QTestState *from, *to; - int64_t percentage; - - /* - * We want the test to be stable and as fast as possible. - * E.g., with 1Gb/s bandwidth migration may pass without throttling, - * so we need to decrease a bandwidth. - */ - const int64_t init_pct = 5, inc_pct = 25, max_pct = 95; - - if (test_migrate_start(&from, &to, uri, &args)) { - return; - } - - migrate_set_capability(from, "auto-converge", true); - migrate_set_parameter_int(from, "cpu-throttle-initial", init_pct); - migrate_set_parameter_int(from, "cpu-throttle-increment", inc_pct); - migrate_set_parameter_int(from, "max-cpu-throttle", max_pct); - - /* - * Set the initial parameters so that the migration could not converge - * without throttling. - */ - migrate_ensure_non_converge(from); - - /* To check remaining size after precopy */ - migrate_set_capability(from, "pause-before-switchover", true); - - /* Wait for the first serial output from the source */ - wait_for_serial("src_serial"); - - migrate_qmp(from, to, uri, NULL, "{}"); - - /* Wait for throttling begins */ - percentage = 0; - do { - percentage = read_migrate_property_int(from, "cpu-throttle-percentage"); - if (percentage != 0) { - break; + argv[j++] = argv[i]; + if (i >= j) { + argv[i] = NULL; } - usleep(20); - g_assert_false(src_state.stop_seen); - } while (true); - /* The first percentage of throttling should be at least init_pct */ - g_assert_cmpint(percentage, >=, init_pct); - /* Now, when we tested that throttling works, let it converge */ - migrate_ensure_converge(from); - - /* - * Wait for pre-switchover status to check last throttle percentage - * and remaining. These values will be zeroed later - */ - wait_for_migration_status(from, "pre-switchover", NULL); - - /* The final percentage of throttling shouldn't be greater than max_pct */ - percentage = read_migrate_property_int(from, "cpu-throttle-percentage"); - g_assert_cmpint(percentage, <=, max_pct); - migrate_continue(from, "pre-switchover"); - - qtest_qmp_eventwait(to, "RESUME"); - - wait_for_serial("dest_serial"); - wait_for_migration_complete(from); - - test_migrate_end(from, to, true); -} - -static void * -test_migrate_precopy_tcp_multifd_start_common(QTestState *from, - QTestState *to, - const char *method) -{ - migrate_set_parameter_int(from, "multifd-channels", 16); - migrate_set_parameter_int(to, "multifd-channels", 16); - - migrate_set_parameter_str(from, "multifd-compression", method); - migrate_set_parameter_str(to, "multifd-compression", method); - - migrate_set_capability(from, "multifd", true); - migrate_set_capability(to, "multifd", true); - - /* Start incoming migration from the 1st socket */ - migrate_incoming_qmp(to, "tcp:127.0.0.1:0", "{}"); - - return NULL; -} - -static void * -test_migrate_precopy_tcp_multifd_start(QTestState *from, - QTestState *to) -{ - return test_migrate_precopy_tcp_multifd_start_common(from, to, "none"); -} - -static void * -test_migrate_precopy_tcp_multifd_start_zero_page_legacy(QTestState *from, - QTestState *to) -{ - test_migrate_precopy_tcp_multifd_start_common(from, to, "none"); - migrate_set_parameter_str(from, "zero-page-detection", "legacy"); - return NULL; -} - -static void * -test_migration_precopy_tcp_multifd_start_no_zero_page(QTestState *from, - QTestState *to) -{ - test_migrate_precopy_tcp_multifd_start_common(from, to, "none"); - migrate_set_parameter_str(from, "zero-page-detection", "none"); - return NULL; -} - -static void * -test_migrate_precopy_tcp_multifd_zlib_start(QTestState *from, - QTestState *to) -{ - /* - * Overloading this test to also check that set_parameter does not error. - * This is also done in the tests for the other compression methods. - */ - migrate_set_parameter_int(from, "multifd-zlib-level", 2); - migrate_set_parameter_int(to, "multifd-zlib-level", 2); - - return test_migrate_precopy_tcp_multifd_start_common(from, to, "zlib"); -} - -#ifdef CONFIG_ZSTD -static void * -test_migrate_precopy_tcp_multifd_zstd_start(QTestState *from, - QTestState *to) -{ - migrate_set_parameter_int(from, "multifd-zstd-level", 2); - migrate_set_parameter_int(to, "multifd-zstd-level", 2); - - return test_migrate_precopy_tcp_multifd_start_common(from, to, "zstd"); -} -#endif /* CONFIG_ZSTD */ - -#ifdef CONFIG_QPL -static void * -test_migrate_precopy_tcp_multifd_qpl_start(QTestState *from, - QTestState *to) -{ - return test_migrate_precopy_tcp_multifd_start_common(from, to, "qpl"); -} -#endif /* CONFIG_QPL */ -#ifdef CONFIG_UADK -static void * -test_migrate_precopy_tcp_multifd_uadk_start(QTestState *from, - QTestState *to) -{ - return test_migrate_precopy_tcp_multifd_start_common(from, to, "uadk"); -} -#endif /* CONFIG_UADK */ - -static void test_multifd_tcp_uri_none(void) -{ - MigrateCommon args = { - .listen_uri = "defer", - .start_hook = test_migrate_precopy_tcp_multifd_start, - /* - * Multifd is more complicated than most of the features, it - * directly takes guest page buffers when sending, make sure - * everything will work alright even if guest page is changing. - */ - .live = true, - }; - test_precopy_common(&args); -} - -static void test_multifd_tcp_zero_page_legacy(void) -{ - MigrateCommon args = { - .listen_uri = "defer", - .start_hook = test_migrate_precopy_tcp_multifd_start_zero_page_legacy, - /* - * Multifd is more complicated than most of the features, it - * directly takes guest page buffers when sending, make sure - * everything will work alright even if guest page is changing. - */ - .live = true, - }; - test_precopy_common(&args); -} - -static void test_multifd_tcp_no_zero_page(void) -{ - MigrateCommon args = { - .listen_uri = "defer", - .start_hook = test_migration_precopy_tcp_multifd_start_no_zero_page, - /* - * Multifd is more complicated than most of the features, it - * directly takes guest page buffers when sending, make sure - * everything will work alright even if guest page is changing. - */ - .live = true, - }; - test_precopy_common(&args); -} - -static void test_multifd_tcp_channels_none(void) -{ - MigrateCommon args = { - .listen_uri = "defer", - .start_hook = test_migrate_precopy_tcp_multifd_start, - .live = true, - .connect_channels = "[ { 'channel-type': 'main'," - " 'addr': { 'transport': 'socket'," - " 'type': 'inet'," - " 'host': '127.0.0.1'," - " 'port': '0' } } ]", - }; - test_precopy_common(&args); -} - -static void test_multifd_tcp_zlib(void) -{ - MigrateCommon args = { - .listen_uri = "defer", - .start_hook = test_migrate_precopy_tcp_multifd_zlib_start, - }; - test_precopy_common(&args); -} - -#ifdef CONFIG_ZSTD -static void test_multifd_tcp_zstd(void) -{ - MigrateCommon args = { - .listen_uri = "defer", - .start_hook = test_migrate_precopy_tcp_multifd_zstd_start, - }; - test_precopy_common(&args); -} -#endif - -#ifdef CONFIG_QPL -static void test_multifd_tcp_qpl(void) -{ - MigrateCommon args = { - .listen_uri = "defer", - .start_hook = test_migrate_precopy_tcp_multifd_qpl_start, - }; - test_precopy_common(&args); -} -#endif - -#ifdef CONFIG_UADK -static void test_multifd_tcp_uadk(void) -{ - MigrateCommon args = { - .listen_uri = "defer", - .start_hook = test_migrate_precopy_tcp_multifd_uadk_start, - }; - test_precopy_common(&args); -} -#endif - -#ifdef CONFIG_GNUTLS -static void * -test_migrate_multifd_tcp_tls_psk_start_match(QTestState *from, - QTestState *to) -{ - test_migrate_precopy_tcp_multifd_start_common(from, to, "none"); - return test_migrate_tls_psk_start_match(from, to); -} - -static void * -test_migrate_multifd_tcp_tls_psk_start_mismatch(QTestState *from, - QTestState *to) -{ - test_migrate_precopy_tcp_multifd_start_common(from, to, "none"); - return test_migrate_tls_psk_start_mismatch(from, to); -} - -#ifdef CONFIG_TASN1 -static void * -test_migrate_multifd_tls_x509_start_default_host(QTestState *from, - QTestState *to) -{ - test_migrate_precopy_tcp_multifd_start_common(from, to, "none"); - return test_migrate_tls_x509_start_default_host(from, to); -} - -static void * -test_migrate_multifd_tls_x509_start_override_host(QTestState *from, - QTestState *to) -{ - test_migrate_precopy_tcp_multifd_start_common(from, to, "none"); - return test_migrate_tls_x509_start_override_host(from, to); -} - -static void * -test_migrate_multifd_tls_x509_start_mismatch_host(QTestState *from, - QTestState *to) -{ - test_migrate_precopy_tcp_multifd_start_common(from, to, "none"); - return test_migrate_tls_x509_start_mismatch_host(from, to); -} - -static void * -test_migrate_multifd_tls_x509_start_allow_anon_client(QTestState *from, - QTestState *to) -{ - test_migrate_precopy_tcp_multifd_start_common(from, to, "none"); - return test_migrate_tls_x509_start_allow_anon_client(from, to); -} - -static void * -test_migrate_multifd_tls_x509_start_reject_anon_client(QTestState *from, - QTestState *to) -{ - test_migrate_precopy_tcp_multifd_start_common(from, to, "none"); - return test_migrate_tls_x509_start_reject_anon_client(from, to); -} -#endif /* CONFIG_TASN1 */ - -static void test_multifd_tcp_tls_psk_match(void) -{ - MigrateCommon args = { - .listen_uri = "defer", - .start_hook = test_migrate_multifd_tcp_tls_psk_start_match, - .finish_hook = test_migrate_tls_psk_finish, - }; - test_precopy_common(&args); -} - -static void test_multifd_tcp_tls_psk_mismatch(void) -{ - MigrateCommon args = { - .start = { - .hide_stderr = true, - }, - .listen_uri = "defer", - .start_hook = test_migrate_multifd_tcp_tls_psk_start_mismatch, - .finish_hook = test_migrate_tls_psk_finish, - .result = MIG_TEST_FAIL, - }; - test_precopy_common(&args); -} - -#ifdef CONFIG_TASN1 -static void test_multifd_tcp_tls_x509_default_host(void) -{ - MigrateCommon args = { - .listen_uri = "defer", - .start_hook = test_migrate_multifd_tls_x509_start_default_host, - .finish_hook = test_migrate_tls_x509_finish, - }; - test_precopy_common(&args); -} - -static void test_multifd_tcp_tls_x509_override_host(void) -{ - MigrateCommon args = { - .listen_uri = "defer", - .start_hook = test_migrate_multifd_tls_x509_start_override_host, - .finish_hook = test_migrate_tls_x509_finish, - }; - test_precopy_common(&args); -} - -static void test_multifd_tcp_tls_x509_mismatch_host(void) -{ - /* - * This has different behaviour to the non-multifd case. - * - * In non-multifd case when client aborts due to mismatched - * cert host, the server has already started trying to load - * migration state, and so it exits with I/O failure. - * - * In multifd case when client aborts due to mismatched - * cert host, the server is still waiting for the other - * multifd connections to arrive so hasn't started trying - * to load migration state, and thus just aborts the migration - * without exiting. - */ - MigrateCommon args = { - .start = { - .hide_stderr = true, - }, - .listen_uri = "defer", - .start_hook = test_migrate_multifd_tls_x509_start_mismatch_host, - .finish_hook = test_migrate_tls_x509_finish, - .result = MIG_TEST_FAIL, - }; - test_precopy_common(&args); -} - -static void test_multifd_tcp_tls_x509_allow_anon_client(void) -{ - MigrateCommon args = { - .listen_uri = "defer", - .start_hook = test_migrate_multifd_tls_x509_start_allow_anon_client, - .finish_hook = test_migrate_tls_x509_finish, - }; - test_precopy_common(&args); -} - -static void test_multifd_tcp_tls_x509_reject_anon_client(void) -{ - MigrateCommon args = { - .start = { - .hide_stderr = true, - }, - .listen_uri = "defer", - .start_hook = test_migrate_multifd_tls_x509_start_reject_anon_client, - .finish_hook = test_migrate_tls_x509_finish, - .result = MIG_TEST_FAIL, - }; - test_precopy_common(&args); -} -#endif /* CONFIG_TASN1 */ -#endif /* CONFIG_GNUTLS */ - -/* - * This test does: - * source target - * migrate_incoming - * migrate - * migrate_cancel - * launch another target - * migrate - * - * And see that it works - */ -static void test_multifd_tcp_cancel(void) -{ - MigrateStart args = { - .hide_stderr = true, - }; - QTestState *from, *to, *to2; - - if (test_migrate_start(&from, &to, "defer", &args)) { - return; } - - migrate_ensure_non_converge(from); - migrate_prepare_for_dirty_mem(from); - - migrate_set_parameter_int(from, "multifd-channels", 16); - migrate_set_parameter_int(to, "multifd-channels", 16); - - migrate_set_capability(from, "multifd", true); - migrate_set_capability(to, "multifd", true); - - /* Start incoming migration from the 1st socket */ - migrate_incoming_qmp(to, "tcp:127.0.0.1:0", "{}"); - - /* Wait for the first serial output from the source */ - wait_for_serial("src_serial"); - - migrate_qmp(from, to, NULL, NULL, "{}"); - - migrate_wait_for_dirty_mem(from, to); - - migrate_cancel(from); - - /* Make sure QEMU process "to" exited */ - qtest_set_expected_status(to, EXIT_FAILURE); - qtest_wait_qemu(to); - qtest_quit(to); - - args = (MigrateStart){ - .only_target = true, - }; - - if (test_migrate_start(&from, &to2, "defer", &args)) { - return; - } - - migrate_set_parameter_int(to2, "multifd-channels", 16); - - migrate_set_capability(to2, "multifd", true); - - /* Start incoming migration from the 1st socket */ - migrate_incoming_qmp(to2, "tcp:127.0.0.1:0", "{}"); - - wait_for_migration_status(from, "cancelled", NULL); - - migrate_ensure_non_converge(from); - - migrate_qmp(from, to2, NULL, NULL, "{}"); - - migrate_wait_for_dirty_mem(from, to2); - - migrate_ensure_converge(from); - - wait_for_stop(from, &src_state); - qtest_qmp_eventwait(to2, "RESUME"); - - wait_for_serial("dest_serial"); - wait_for_migration_complete(from); - test_migrate_end(from, to2, true); -} - -static void calc_dirty_rate(QTestState *who, uint64_t calc_time) -{ - qtest_qmp_assert_success(who, - "{ 'execute': 'calc-dirty-rate'," - "'arguments': { " - "'calc-time': %" PRIu64 "," - "'mode': 'dirty-ring' }}", - calc_time); -} - -static QDict *query_dirty_rate(QTestState *who) -{ - return qtest_qmp_assert_success_ref(who, - "{ 'execute': 'query-dirty-rate' }"); -} - -static void dirtylimit_set_all(QTestState *who, uint64_t dirtyrate) -{ - qtest_qmp_assert_success(who, - "{ 'execute': 'set-vcpu-dirty-limit'," - "'arguments': { " - "'dirty-rate': %" PRIu64 " } }", - dirtyrate); -} - -static void cancel_vcpu_dirty_limit(QTestState *who) -{ - qtest_qmp_assert_success(who, - "{ 'execute': 'cancel-vcpu-dirty-limit' }"); -} - -static QDict *query_vcpu_dirty_limit(QTestState *who) -{ - QDict *rsp; - - rsp = qtest_qmp(who, "{ 'execute': 'query-vcpu-dirty-limit' }"); - g_assert(!qdict_haskey(rsp, "error")); - g_assert(qdict_haskey(rsp, "return")); - - return rsp; -} - -static bool calc_dirtyrate_ready(QTestState *who) -{ - QDict *rsp_return; - const char *status; - bool ready; - - rsp_return = query_dirty_rate(who); - g_assert(rsp_return); - - status = qdict_get_str(rsp_return, "status"); - g_assert(status); - ready = g_strcmp0(status, "measuring"); - qobject_unref(rsp_return); - - return ready; -} - -static void wait_for_calc_dirtyrate_complete(QTestState *who, - int64_t time_s) -{ - int max_try_count = 10000; - usleep(time_s * 1000000); - - while (!calc_dirtyrate_ready(who) && max_try_count--) { - usleep(1000); - } - - /* - * Set the timeout with 10 s(max_try_count * 1000us), - * if dirtyrate measurement not complete, fail test. - */ - g_assert_cmpint(max_try_count, !=, 0); -} - -static int64_t get_dirty_rate(QTestState *who) -{ - QDict *rsp_return; - const char *status; - QList *rates; - const QListEntry *entry; - QDict *rate; - int64_t dirtyrate; - - rsp_return = query_dirty_rate(who); - g_assert(rsp_return); - - status = qdict_get_str(rsp_return, "status"); - g_assert(status); - g_assert_cmpstr(status, ==, "measured"); - - rates = qdict_get_qlist(rsp_return, "vcpu-dirty-rate"); - g_assert(rates && !qlist_empty(rates)); - - entry = qlist_first(rates); - g_assert(entry); - - rate = qobject_to(QDict, qlist_entry_obj(entry)); - g_assert(rate); - - dirtyrate = qdict_get_try_int(rate, "dirty-rate", -1); - - qobject_unref(rsp_return); - return dirtyrate; -} - -static int64_t get_limit_rate(QTestState *who) -{ - QDict *rsp_return; - QList *rates; - const QListEntry *entry; - QDict *rate; - int64_t dirtyrate; - - rsp_return = query_vcpu_dirty_limit(who); - g_assert(rsp_return); - - rates = qdict_get_qlist(rsp_return, "return"); - g_assert(rates && !qlist_empty(rates)); - - entry = qlist_first(rates); - g_assert(entry); - - rate = qobject_to(QDict, qlist_entry_obj(entry)); - g_assert(rate); - - dirtyrate = qdict_get_try_int(rate, "limit-rate", -1); - - qobject_unref(rsp_return); - return dirtyrate; -} - -static QTestState *dirtylimit_start_vm(void) -{ - QTestState *vm = NULL; - g_autofree gchar *cmd = NULL; - - bootfile_create(tmpfs, false); - cmd = g_strdup_printf("-accel kvm,dirty-ring-size=4096 " - "-name dirtylimit-test,debug-threads=on " - "-m 150M -smp 1 " - "-serial file:%s/vm_serial " - "-drive file=%s,format=raw ", - tmpfs, bootpath); - - vm = qtest_init(cmd); - return vm; -} - -static void dirtylimit_stop_vm(QTestState *vm) -{ - qtest_quit(vm); - cleanup("vm_serial"); -} - -static void test_vcpu_dirty_limit(void) -{ - QTestState *vm; - int64_t origin_rate; - int64_t quota_rate; - int64_t rate ; - int max_try_count = 20; - int hit = 0; - - /* Start vm for vcpu dirtylimit test */ - vm = dirtylimit_start_vm(); - - /* Wait for the first serial output from the vm*/ - wait_for_serial("vm_serial"); - - /* Do dirtyrate measurement with calc time equals 1s */ - calc_dirty_rate(vm, 1); - - /* Sleep calc time and wait for calc dirtyrate complete */ - wait_for_calc_dirtyrate_complete(vm, 1); - - /* Query original dirty page rate */ - origin_rate = get_dirty_rate(vm); - - /* VM booted from bootsect should dirty memory steadily */ - assert(origin_rate != 0); - - /* Setup quota dirty page rate at half of origin */ - quota_rate = origin_rate / 2; - - /* Set dirtylimit */ - dirtylimit_set_all(vm, quota_rate); - - /* - * Check if set-vcpu-dirty-limit and query-vcpu-dirty-limit - * works literally - */ - g_assert_cmpint(quota_rate, ==, get_limit_rate(vm)); - - /* Sleep a bit to check if it take effect */ - usleep(2000000); - - /* - * Check if dirtylimit take effect realistically, set the - * timeout with 20 s(max_try_count * 1s), if dirtylimit - * doesn't take effect, fail test. - */ - while (--max_try_count) { - calc_dirty_rate(vm, 1); - wait_for_calc_dirtyrate_complete(vm, 1); - rate = get_dirty_rate(vm); - - /* - * Assume hitting if current rate is less - * than quota rate (within accepting error) - */ - if (rate < (quota_rate + DIRTYLIMIT_TOLERANCE_RANGE)) { - hit = 1; - break; - } - } - - g_assert_cmpint(hit, ==, 1); - - hit = 0; - max_try_count = 20; - - /* Check if dirtylimit cancellation take effect */ - cancel_vcpu_dirty_limit(vm); - while (--max_try_count) { - calc_dirty_rate(vm, 1); - wait_for_calc_dirtyrate_complete(vm, 1); - rate = get_dirty_rate(vm); - - /* - * Assume dirtylimit be canceled if current rate is - * greater than quota rate (within accepting error) - */ - if (rate > (quota_rate + DIRTYLIMIT_TOLERANCE_RANGE)) { - hit = 1; - break; - } - } - - g_assert_cmpint(hit, ==, 1); - dirtylimit_stop_vm(vm); -} - -static void migrate_dirty_limit_wait_showup(QTestState *from, - const int64_t period, - const int64_t value) -{ - /* Enable dirty limit capability */ - migrate_set_capability(from, "dirty-limit", true); - - /* Set dirty limit parameters */ - migrate_set_parameter_int(from, "x-vcpu-dirty-limit-period", period); - migrate_set_parameter_int(from, "vcpu-dirty-limit", value); - - /* Make sure migrate can't converge */ - migrate_ensure_non_converge(from); - - /* To check limit rate after precopy */ - migrate_set_capability(from, "pause-before-switchover", true); - - /* Wait for the serial output from the source */ - wait_for_serial("src_serial"); -} - -/* - * This test does: - * source destination - * start vm - * start incoming vm - * migrate - * wait dirty limit to begin - * cancel migrate - * cancellation check - * restart incoming vm - * migrate - * wait dirty limit to begin - * wait pre-switchover event - * convergence condition check - * - * And see if dirty limit migration works correctly. - * This test case involves many passes, so it runs in slow mode only. - */ -static void test_migrate_dirty_limit(void) -{ - g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); - QTestState *from, *to; - int64_t remaining; - uint64_t throttle_us_per_full; - /* - * We want the test to be stable and as fast as possible. - * E.g., with 1Gb/s bandwidth migration may pass without dirty limit, - * so we need to decrease a bandwidth. - */ - const int64_t dirtylimit_period = 1000, dirtylimit_value = 50; - const int64_t max_bandwidth = 400000000; /* ~400Mb/s */ - const int64_t downtime_limit = 250; /* 250ms */ - /* - * We migrate through unix-socket (> 500Mb/s). - * Thus, expected migration speed ~= bandwidth limit (< 500Mb/s). - * So, we can predict expected_threshold - */ - const int64_t expected_threshold = max_bandwidth * downtime_limit / 1000; - int max_try_count = 10; - MigrateCommon args = { - .start = { - .hide_stderr = true, - .use_dirty_ring = true, - }, - .listen_uri = uri, - .connect_uri = uri, - }; - - /* Start src, dst vm */ - if (test_migrate_start(&from, &to, args.listen_uri, &args.start)) { - return; - } - - /* Prepare for dirty limit migration and wait src vm show up */ - migrate_dirty_limit_wait_showup(from, dirtylimit_period, dirtylimit_value); - - /* Start migrate */ - migrate_qmp(from, to, args.connect_uri, NULL, "{}"); - - /* Wait for dirty limit throttle begin */ - throttle_us_per_full = 0; - while (throttle_us_per_full == 0) { - throttle_us_per_full = - read_migrate_property_int(from, "dirty-limit-throttle-time-per-round"); - usleep(100); - g_assert_false(src_state.stop_seen); - } - - /* Now cancel migrate and wait for dirty limit throttle switch off */ - migrate_cancel(from); - wait_for_migration_status(from, "cancelled", NULL); - - /* Check if dirty limit throttle switched off, set timeout 1ms */ - do { - throttle_us_per_full = - read_migrate_property_int(from, "dirty-limit-throttle-time-per-round"); - usleep(100); - g_assert_false(src_state.stop_seen); - } while (throttle_us_per_full != 0 && --max_try_count); - - /* Assert dirty limit is not in service */ - g_assert_cmpint(throttle_us_per_full, ==, 0); - - args = (MigrateCommon) { - .start = { - .only_target = true, - .use_dirty_ring = true, - }, - .listen_uri = uri, - .connect_uri = uri, - }; - - /* Restart dst vm, src vm already show up so we needn't wait anymore */ - if (test_migrate_start(&from, &to, args.listen_uri, &args.start)) { - return; - } - - /* Start migrate */ - migrate_qmp(from, to, args.connect_uri, NULL, "{}"); - - /* Wait for dirty limit throttle begin */ - throttle_us_per_full = 0; - while (throttle_us_per_full == 0) { - throttle_us_per_full = - read_migrate_property_int(from, "dirty-limit-throttle-time-per-round"); - usleep(100); - g_assert_false(src_state.stop_seen); - } - - /* - * The dirty limit rate should equals the return value of - * query-vcpu-dirty-limit if dirty limit cap set - */ - g_assert_cmpint(dirtylimit_value, ==, get_limit_rate(from)); - - /* Now, we have tested if dirty limit works, let it converge */ - migrate_set_parameter_int(from, "downtime-limit", downtime_limit); - migrate_set_parameter_int(from, "max-bandwidth", max_bandwidth); - - /* - * Wait for pre-switchover status to check if migration - * satisfy the convergence condition - */ - wait_for_migration_status(from, "pre-switchover", NULL); - - remaining = read_ram_property_int(from, "remaining"); - g_assert_cmpint(remaining, <, - (expected_threshold + expected_threshold / 100)); - - migrate_continue(from, "pre-switchover"); - - qtest_qmp_eventwait(to, "RESUME"); - - wait_for_serial("dest_serial"); - wait_for_migration_complete(from); - - test_migrate_end(from, to, true); -} - -static bool kvm_dirty_ring_supported(void) -{ -#if defined(__linux__) && defined(HOST_X86_64) - int ret, kvm_fd = open("/dev/kvm", O_RDONLY); - - if (kvm_fd < 0) { - return false; - } - - ret = ioctl(kvm_fd, KVM_CHECK_EXTENSION, KVM_CAP_DIRTY_LOG_RING); - close(kvm_fd); - - /* We test with 4096 slots */ - if (ret < 4096) { - return false; - } - - return true; -#else - return false; -#endif + *argc_p = j; } int main(int argc, char **argv) { - bool has_kvm, has_tcg; - bool has_uffd, is_x86; - const char *arch; - g_autoptr(GError) err = NULL; - const char *qemu_src = getenv(QEMU_ENV_SRC); - const char *qemu_dst = getenv(QEMU_ENV_DST); + MigrationTestEnv *env; int ret; + bool full_set = false; - g_test_init(&argc, &argv, NULL); - - /* - * The default QTEST_QEMU_BINARY must always be provided because - * that is what helpers use to query the accel type and - * architecture. - */ - if (qemu_src && qemu_dst) { - g_test_message("Only one of %s, %s is allowed", - QEMU_ENV_SRC, QEMU_ENV_DST); - exit(1); - } - - has_kvm = qtest_has_accel("kvm"); - has_tcg = qtest_has_accel("tcg"); - - if (!has_tcg && !has_kvm) { - g_test_skip("No KVM or TCG accelerator available"); - return 0; - } - - has_uffd = ufd_version_check(); - arch = qtest_get_arch(); - is_x86 = !strcmp(arch, "i386") || !strcmp(arch, "x86_64"); - - tmpfs = g_dir_make_tmp("migration-test-XXXXXX", &err); - if (!tmpfs) { - g_test_message("Can't create temporary directory in %s: %s", - g_get_tmp_dir(), err->message); - } - g_assert(tmpfs); + /* strip the --full option if it's present */ + parse_args(&argc, &argv, &full_set); + g_test_init(&argc, &argv, NULL); + env = migration_get_env(); + env->full_set = full_set; module_call_init(MODULE_INIT_QOM); - migration_test_add("/migration/bad_dest", test_baddest); -#ifndef _WIN32 - migration_test_add("/migration/analyze-script", test_analyze_script); -#endif - - if (is_x86) { - migration_test_add("/migration/precopy/unix/suspend/live", - test_precopy_unix_suspend_live); - migration_test_add("/migration/precopy/unix/suspend/notlive", - test_precopy_unix_suspend_notlive); - } - - if (has_uffd) { - migration_test_add("/migration/postcopy/plain", test_postcopy); - migration_test_add("/migration/postcopy/recovery/plain", - test_postcopy_recovery); - migration_test_add("/migration/postcopy/preempt/plain", - test_postcopy_preempt); - migration_test_add("/migration/postcopy/preempt/recovery/plain", - test_postcopy_preempt_recovery); - migration_test_add("/migration/postcopy/recovery/double-failures/handshake", - test_postcopy_recovery_fail_handshake); - migration_test_add("/migration/postcopy/recovery/double-failures/reconnect", - test_postcopy_recovery_fail_reconnect); - if (is_x86) { - migration_test_add("/migration/postcopy/suspend", - test_postcopy_suspend); - } - } - - migration_test_add("/migration/precopy/unix/plain", - test_precopy_unix_plain); - migration_test_add("/migration/precopy/unix/xbzrle", - test_precopy_unix_xbzrle); - migration_test_add("/migration/precopy/file", - test_precopy_file); - migration_test_add("/migration/precopy/file/offset", - test_precopy_file_offset); -#ifndef _WIN32 - migration_test_add("/migration/precopy/file/offset/fdset", - test_precopy_file_offset_fdset); -#endif - migration_test_add("/migration/precopy/file/offset/bad", - test_precopy_file_offset_bad); - - /* - * Our CI system has problems with shared memory. - * Don't run this test until we find a workaround. - */ - if (getenv("QEMU_TEST_FLAKY_TESTS")) { - migration_test_add("/migration/mode/reboot", test_mode_reboot); - } - - migration_test_add("/migration/precopy/file/mapped-ram", - test_precopy_file_mapped_ram); - migration_test_add("/migration/precopy/file/mapped-ram/live", - test_precopy_file_mapped_ram_live); - - migration_test_add("/migration/multifd/file/mapped-ram", - test_multifd_file_mapped_ram); - migration_test_add("/migration/multifd/file/mapped-ram/live", - test_multifd_file_mapped_ram_live); - - migration_test_add("/migration/multifd/file/mapped-ram/dio", - test_multifd_file_mapped_ram_dio); - -#ifndef _WIN32 - migration_test_add("/migration/multifd/file/mapped-ram/fdset", - test_multifd_file_mapped_ram_fdset); - migration_test_add("/migration/multifd/file/mapped-ram/fdset/dio", - test_multifd_file_mapped_ram_fdset_dio); -#endif - -#ifdef CONFIG_GNUTLS - migration_test_add("/migration/precopy/unix/tls/psk", - test_precopy_unix_tls_psk); - - if (has_uffd) { - /* - * NOTE: psk test is enough for postcopy, as other types of TLS - * channels are tested under precopy. Here what we want to test is the - * general postcopy path that has TLS channel enabled. - */ - migration_test_add("/migration/postcopy/tls/psk", - test_postcopy_tls_psk); - migration_test_add("/migration/postcopy/recovery/tls/psk", - test_postcopy_recovery_tls_psk); - migration_test_add("/migration/postcopy/preempt/tls/psk", - test_postcopy_preempt_tls_psk); - migration_test_add("/migration/postcopy/preempt/recovery/tls/psk", - test_postcopy_preempt_all); - } -#ifdef CONFIG_TASN1 - migration_test_add("/migration/precopy/unix/tls/x509/default-host", - test_precopy_unix_tls_x509_default_host); - migration_test_add("/migration/precopy/unix/tls/x509/override-host", - test_precopy_unix_tls_x509_override_host); -#endif /* CONFIG_TASN1 */ -#endif /* CONFIG_GNUTLS */ - - migration_test_add("/migration/precopy/tcp/plain", test_precopy_tcp_plain); - - migration_test_add("/migration/precopy/tcp/plain/switchover-ack", - test_precopy_tcp_switchover_ack); - -#ifdef CONFIG_GNUTLS - migration_test_add("/migration/precopy/tcp/tls/psk/match", - test_precopy_tcp_tls_psk_match); - migration_test_add("/migration/precopy/tcp/tls/psk/mismatch", - test_precopy_tcp_tls_psk_mismatch); -#ifdef CONFIG_TASN1 - migration_test_add("/migration/precopy/tcp/tls/x509/default-host", - test_precopy_tcp_tls_x509_default_host); - migration_test_add("/migration/precopy/tcp/tls/x509/override-host", - test_precopy_tcp_tls_x509_override_host); - migration_test_add("/migration/precopy/tcp/tls/x509/mismatch-host", - test_precopy_tcp_tls_x509_mismatch_host); - migration_test_add("/migration/precopy/tcp/tls/x509/friendly-client", - test_precopy_tcp_tls_x509_friendly_client); - migration_test_add("/migration/precopy/tcp/tls/x509/hostile-client", - test_precopy_tcp_tls_x509_hostile_client); - migration_test_add("/migration/precopy/tcp/tls/x509/allow-anon-client", - test_precopy_tcp_tls_x509_allow_anon_client); - migration_test_add("/migration/precopy/tcp/tls/x509/reject-anon-client", - test_precopy_tcp_tls_x509_reject_anon_client); -#endif /* CONFIG_TASN1 */ -#endif /* CONFIG_GNUTLS */ - - /* migration_test_add("/migration/ignore_shared", test_ignore_shared); */ -#ifndef _WIN32 - migration_test_add("/migration/precopy/fd/tcp", - test_migrate_precopy_fd_socket); - migration_test_add("/migration/precopy/fd/file", - test_migrate_precopy_fd_file); -#endif - migration_test_add("/migration/validate_uuid", test_validate_uuid); - migration_test_add("/migration/validate_uuid_error", - test_validate_uuid_error); - migration_test_add("/migration/validate_uuid_src_not_set", - test_validate_uuid_src_not_set); - migration_test_add("/migration/validate_uuid_dst_not_set", - test_validate_uuid_dst_not_set); - migration_test_add("/migration/validate_uri/channels/both_set", - test_validate_uri_channels_both_set); - migration_test_add("/migration/validate_uri/channels/none_set", - test_validate_uri_channels_none_set); - /* - * See explanation why this test is slow on function definition - */ - if (g_test_slow()) { - migration_test_add("/migration/auto_converge", - test_migrate_auto_converge); - if (g_str_equal(arch, "x86_64") && - has_kvm && kvm_dirty_ring_supported()) { - migration_test_add("/migration/dirty_limit", - test_migrate_dirty_limit); - } - } - migration_test_add("/migration/multifd/tcp/uri/plain/none", - test_multifd_tcp_uri_none); - migration_test_add("/migration/multifd/tcp/channels/plain/none", - test_multifd_tcp_channels_none); - migration_test_add("/migration/multifd/tcp/plain/zero-page/legacy", - test_multifd_tcp_zero_page_legacy); - migration_test_add("/migration/multifd/tcp/plain/zero-page/none", - test_multifd_tcp_no_zero_page); - migration_test_add("/migration/multifd/tcp/plain/cancel", - test_multifd_tcp_cancel); - migration_test_add("/migration/multifd/tcp/plain/zlib", - test_multifd_tcp_zlib); -#ifdef CONFIG_ZSTD - migration_test_add("/migration/multifd/tcp/plain/zstd", - test_multifd_tcp_zstd); -#endif -#ifdef CONFIG_QPL - migration_test_add("/migration/multifd/tcp/plain/qpl", - test_multifd_tcp_qpl); -#endif -#ifdef CONFIG_UADK - migration_test_add("/migration/multifd/tcp/plain/uadk", - test_multifd_tcp_uadk); -#endif -#ifdef CONFIG_GNUTLS - migration_test_add("/migration/multifd/tcp/tls/psk/match", - test_multifd_tcp_tls_psk_match); - migration_test_add("/migration/multifd/tcp/tls/psk/mismatch", - test_multifd_tcp_tls_psk_mismatch); -#ifdef CONFIG_TASN1 - migration_test_add("/migration/multifd/tcp/tls/x509/default-host", - test_multifd_tcp_tls_x509_default_host); - migration_test_add("/migration/multifd/tcp/tls/x509/override-host", - test_multifd_tcp_tls_x509_override_host); - migration_test_add("/migration/multifd/tcp/tls/x509/mismatch-host", - test_multifd_tcp_tls_x509_mismatch_host); - migration_test_add("/migration/multifd/tcp/tls/x509/allow-anon-client", - test_multifd_tcp_tls_x509_allow_anon_client); - migration_test_add("/migration/multifd/tcp/tls/x509/reject-anon-client", - test_multifd_tcp_tls_x509_reject_anon_client); -#endif /* CONFIG_TASN1 */ -#endif /* CONFIG_GNUTLS */ - - if (g_str_equal(arch, "x86_64") && has_kvm && kvm_dirty_ring_supported()) { - migration_test_add("/migration/dirty_ring", - test_precopy_unix_dirty_ring); - if (qtest_has_machine("pc")) { - migration_test_add("/migration/vcpu_dirty_limit", - test_vcpu_dirty_limit); - } - } + migration_test_add_tls(env); + migration_test_add_compression(env); + migration_test_add_postcopy(env); + migration_test_add_file(env); + migration_test_add_precopy(env); + migration_test_add_cpr(env); + migration_test_add_misc(env); ret = g_test_run(); g_assert_cmpint(ret, ==, 0); - bootfile_delete(); - ret = rmdir(tmpfs); - if (ret != 0) { - g_test_message("unable to rmdir: path (%s): %s", - tmpfs, strerror(errno)); - } - g_free(tmpfs); + ret = migration_env_clean(env); return ret; } diff --git a/tests/migration/Makefile b/tests/qtest/migration/Makefile index 2c5ee28..2c5ee28 100644 --- a/tests/migration/Makefile +++ b/tests/qtest/migration/Makefile diff --git a/tests/migration/aarch64/Makefile b/tests/qtest/migration/aarch64/Makefile index 9c4fa18..9c4fa18 100644 --- a/tests/migration/aarch64/Makefile +++ b/tests/qtest/migration/aarch64/Makefile diff --git a/tests/migration/aarch64/a-b-kernel.S b/tests/qtest/migration/aarch64/a-b-kernel.S index a4103ec..a4103ec 100644 --- a/tests/migration/aarch64/a-b-kernel.S +++ b/tests/qtest/migration/aarch64/a-b-kernel.S diff --git a/tests/migration/aarch64/a-b-kernel.h b/tests/qtest/migration/aarch64/a-b-kernel.h index 34e518d..34e518d 100644 --- a/tests/migration/aarch64/a-b-kernel.h +++ b/tests/qtest/migration/aarch64/a-b-kernel.h diff --git a/tests/qtest/migration/bootfile.c b/tests/qtest/migration/bootfile.c new file mode 100644 index 0000000..fac059d --- /dev/null +++ b/tests/qtest/migration/bootfile.c @@ -0,0 +1,70 @@ +/* + * Guest code setup for migration tests + * + * Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates + * based on the vhost-user-test.c that is: + * Copyright (c) 2014 Virtual Open Systems Sarl. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#include "qemu/osdep.h" + +/* + * The boot file modifies memory area in [start_address, end_address) + * repeatedly. It outputs a 'B' at a fixed rate while it's still running. + */ +#include "bootfile.h" +#include "i386/a-b-bootblock.h" +#include "aarch64/a-b-kernel.h" +#include "ppc64/a-b-kernel.h" +#include "s390x/a-b-bios.h" + +static char *bootpath; + +void bootfile_delete(void) +{ + if (!bootpath) { + return; + } + unlink(bootpath); + g_free(bootpath); + bootpath = NULL; +} + +char *bootfile_create(const char *arch, const char *dir, bool suspend_me) +{ + unsigned char *content; + size_t len; + + bootfile_delete(); + bootpath = g_strdup_printf("%s/bootsect", dir); + if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { + /* the assembled x86 boot sector should be exactly one sector large */ + g_assert(sizeof(x86_bootsect) == 512); + x86_bootsect[SYM_suspend_me - SYM_start] = suspend_me; + content = x86_bootsect; + len = sizeof(x86_bootsect); + } else if (g_str_equal(arch, "s390x")) { + content = s390x_elf; + len = sizeof(s390x_elf); + } else if (strcmp(arch, "ppc64") == 0) { + content = ppc64_kernel; + len = sizeof(ppc64_kernel); + } else if (strcmp(arch, "aarch64") == 0) { + content = aarch64_kernel; + len = sizeof(aarch64_kernel); + g_assert(sizeof(aarch64_kernel) <= ARM_TEST_MAX_KERNEL_SIZE); + } else { + g_assert_not_reached(); + } + + FILE *bootfile = fopen(bootpath, "wb"); + + g_assert_cmpint(fwrite(content, len, 1, bootfile), ==, 1); + fclose(bootfile); + + return bootpath; +} diff --git a/tests/migration/migration-test.h b/tests/qtest/migration/bootfile.h index 194df7d..6d6a673 100644 --- a/tests/migration/migration-test.h +++ b/tests/qtest/migration/bootfile.h @@ -5,8 +5,8 @@ * See the COPYING file in the top-level directory. */ -#ifndef MIGRATION_TEST_H -#define MIGRATION_TEST_H +#ifndef BOOTFILE_H +#define BOOTFILE_H /* Common */ #define TEST_MEM_PAGE_SIZE 4096 @@ -33,4 +33,7 @@ */ #define ARM_TEST_MAX_KERNEL_SIZE (512 * 1024) -#endif /* MIGRATION_TEST_H */ +void bootfile_delete(void); +char *bootfile_create(const char *arch, const char *dir, bool suspend_me); + +#endif /* BOOTFILE_H */ diff --git a/tests/qtest/migration/compression-tests.c b/tests/qtest/migration/compression-tests.c new file mode 100644 index 0000000..b827665 --- /dev/null +++ b/tests/qtest/migration/compression-tests.c @@ -0,0 +1,226 @@ +/* + * QTest testcases for migration compression + * + * Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates + * based on the vhost-user-test.c that is: + * Copyright (c) 2014 Virtual Open Systems Sarl. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "migration/framework.h" +#include "migration/migration-qmp.h" +#include "migration/migration-util.h" +#include "qemu/module.h" + + +static char *tmpfs; + +#ifdef CONFIG_ZSTD +static void * +migrate_hook_start_precopy_tcp_multifd_zstd(QTestState *from, + QTestState *to) +{ + migrate_set_parameter_int(from, "multifd-zstd-level", 2); + migrate_set_parameter_int(to, "multifd-zstd-level", 2); + + return migrate_hook_start_precopy_tcp_multifd_common(from, to, "zstd"); +} + +static void test_multifd_tcp_zstd(void) +{ + MigrateCommon args = { + .listen_uri = "defer", + .start = { + .caps[MIGRATION_CAPABILITY_MULTIFD] = true, + }, + .start_hook = migrate_hook_start_precopy_tcp_multifd_zstd, + }; + test_precopy_common(&args); +} + +static void test_multifd_postcopy_tcp_zstd(void) +{ + MigrateCommon args = { + .listen_uri = "defer", + .start = { + .caps[MIGRATION_CAPABILITY_MULTIFD] = true, + .caps[MIGRATION_CAPABILITY_POSTCOPY_RAM] = true, + }, + .start_hook = migrate_hook_start_precopy_tcp_multifd_zstd, + }; + + test_precopy_common(&args); +} +#endif /* CONFIG_ZSTD */ + +#ifdef CONFIG_QATZIP +static void * +migrate_hook_start_precopy_tcp_multifd_qatzip(QTestState *from, + QTestState *to) +{ + migrate_set_parameter_int(from, "multifd-qatzip-level", 2); + migrate_set_parameter_int(to, "multifd-qatzip-level", 2); + + return migrate_hook_start_precopy_tcp_multifd_common(from, to, "qatzip"); +} + +static void test_multifd_tcp_qatzip(void) +{ + MigrateCommon args = { + .listen_uri = "defer", + .start = { + .caps[MIGRATION_CAPABILITY_MULTIFD] = true, + }, + .start_hook = migrate_hook_start_precopy_tcp_multifd_qatzip, + }; + test_precopy_common(&args); +} +#endif + +#ifdef CONFIG_QPL +static void * +migrate_hook_start_precopy_tcp_multifd_qpl(QTestState *from, + QTestState *to) +{ + return migrate_hook_start_precopy_tcp_multifd_common(from, to, "qpl"); +} + +static void test_multifd_tcp_qpl(void) +{ + MigrateCommon args = { + .listen_uri = "defer", + .start = { + .caps[MIGRATION_CAPABILITY_MULTIFD] = true, + }, + .start_hook = migrate_hook_start_precopy_tcp_multifd_qpl, + }; + test_precopy_common(&args); +} +#endif /* CONFIG_QPL */ + +#ifdef CONFIG_UADK +static void * +migrate_hook_start_precopy_tcp_multifd_uadk(QTestState *from, + QTestState *to) +{ + return migrate_hook_start_precopy_tcp_multifd_common(from, to, "uadk"); +} + +static void test_multifd_tcp_uadk(void) +{ + MigrateCommon args = { + .listen_uri = "defer", + .start = { + .caps[MIGRATION_CAPABILITY_MULTIFD] = true, + }, + .start_hook = migrate_hook_start_precopy_tcp_multifd_uadk, + }; + test_precopy_common(&args); +} +#endif /* CONFIG_UADK */ + +static void * +migrate_hook_start_xbzrle(QTestState *from, + QTestState *to) +{ + migrate_set_parameter_int(from, "xbzrle-cache-size", 33554432); + return NULL; +} + +static void test_precopy_unix_xbzrle(void) +{ + g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); + MigrateCommon args = { + .connect_uri = uri, + .listen_uri = uri, + .start_hook = migrate_hook_start_xbzrle, + .iterations = 2, + .start = { + .caps[MIGRATION_CAPABILITY_XBZRLE] = true, + }, + /* + * XBZRLE needs pages to be modified when doing the 2nd+ round + * iteration to have real data pushed to the stream. + */ + .live = true, + }; + + test_precopy_common(&args); +} + +static void * +migrate_hook_start_precopy_tcp_multifd_zlib(QTestState *from, + QTestState *to) +{ + /* + * Overloading this test to also check that set_parameter does not error. + * This is also done in the tests for the other compression methods. + */ + migrate_set_parameter_int(from, "multifd-zlib-level", 2); + migrate_set_parameter_int(to, "multifd-zlib-level", 2); + + return migrate_hook_start_precopy_tcp_multifd_common(from, to, "zlib"); +} + +static void test_multifd_tcp_zlib(void) +{ + MigrateCommon args = { + .listen_uri = "defer", + .start = { + .caps[MIGRATION_CAPABILITY_MULTIFD] = true, + }, + .start_hook = migrate_hook_start_precopy_tcp_multifd_zlib, + }; + test_precopy_common(&args); +} + +static void migration_test_add_compression_smoke(MigrationTestEnv *env) +{ + migration_test_add("/migration/multifd/tcp/plain/zlib", + test_multifd_tcp_zlib); +} + +void migration_test_add_compression(MigrationTestEnv *env) +{ + tmpfs = env->tmpfs; + + migration_test_add_compression_smoke(env); + + if (!env->full_set) { + return; + } + +#ifdef CONFIG_ZSTD + migration_test_add("/migration/multifd/tcp/plain/zstd", + test_multifd_tcp_zstd); + if (env->has_uffd) { + migration_test_add("/migration/multifd+postcopy/tcp/plain/zstd", + test_multifd_postcopy_tcp_zstd); + } +#endif + +#ifdef CONFIG_QATZIP + migration_test_add("/migration/multifd/tcp/plain/qatzip", + test_multifd_tcp_qatzip); +#endif + +#ifdef CONFIG_QPL + migration_test_add("/migration/multifd/tcp/plain/qpl", + test_multifd_tcp_qpl); +#endif + +#ifdef CONFIG_UADK + migration_test_add("/migration/multifd/tcp/plain/uadk", + test_multifd_tcp_uadk); +#endif + + if (g_test_slow()) { + migration_test_add("/migration/precopy/unix/xbzrle", + test_precopy_unix_xbzrle); + } +} diff --git a/tests/qtest/migration/cpr-tests.c b/tests/qtest/migration/cpr-tests.c new file mode 100644 index 0000000..5e764a6 --- /dev/null +++ b/tests/qtest/migration/cpr-tests.c @@ -0,0 +1,136 @@ +/* + * QTest testcases for CPR + * + * Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates + * based on the vhost-user-test.c that is: + * Copyright (c) 2014 Virtual Open Systems Sarl. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "migration/framework.h" +#include "migration/migration-qmp.h" +#include "migration/migration-util.h" + + +static char *tmpfs; + +static void *migrate_hook_start_mode_reboot(QTestState *from, QTestState *to) +{ + migrate_set_parameter_str(from, "mode", "cpr-reboot"); + migrate_set_parameter_str(to, "mode", "cpr-reboot"); + + return NULL; +} + +static void test_mode_reboot(void) +{ + g_autofree char *uri = g_strdup_printf("file:%s/%s", tmpfs, + FILE_TEST_FILENAME); + MigrateCommon args = { + .start.use_shmem = true, + .connect_uri = uri, + .listen_uri = "defer", + .start_hook = migrate_hook_start_mode_reboot, + .start = { + .caps[MIGRATION_CAPABILITY_X_IGNORE_SHARED] = true, + }, + }; + + 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); + g_autofree char *opts_target = NULL; + + 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 *connect_channels = g_strdup_printf( + "[ { 'channel-type': 'main'," + " 'addr': { 'transport': 'socket'," + " 'type': 'unix'," + " 'path': '%s' } } ]", + mig_path); + + /* + * Set up a UNIX domain socket for the CPR channel before + * launching the destination VM, to avoid timing issues + * during connection setup. + */ + int cpr_sockfd = qtest_socket_server(cpr_path); + g_assert(cpr_sockfd >= 0); + + opts_target = g_strdup_printf("-incoming cpr,addr.transport=socket," + "addr.type=fd,addr.str=%d %s", + cpr_sockfd, opts); + 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; + + /* no tests in the smoke set for now */ + + if (!env->full_set) { + return; + } + + /* + * Our CI system has problems with shared memory. + * Don't run this test until we find a workaround. + */ + 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/file-tests.c b/tests/qtest/migration/file-tests.c new file mode 100644 index 0000000..4d78ce0 --- /dev/null +++ b/tests/qtest/migration/file-tests.c @@ -0,0 +1,341 @@ +/* + * QTest testcases for migration to file + * + * Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates + * based on the vhost-user-test.c that is: + * Copyright (c) 2014 Virtual Open Systems Sarl. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "migration/framework.h" +#include "migration/migration-qmp.h" +#include "migration/migration-util.h" +#include "qobject/qlist.h" + + +static char *tmpfs; + +static void test_precopy_file(void) +{ + g_autofree char *uri = g_strdup_printf("file:%s/%s", tmpfs, + FILE_TEST_FILENAME); + MigrateCommon args = { + .connect_uri = uri, + .listen_uri = "defer", + }; + + test_file_common(&args, true); +} + +#ifndef _WIN32 +static void fdset_add_fds(QTestState *qts, const char *file, int flags, + int num_fds, bool direct_io) +{ + for (int i = 0; i < num_fds; i++) { + int fd; + +#ifdef O_DIRECT + /* only secondary channels can use direct-io */ + if (direct_io && i != 0) { + flags |= O_DIRECT; + } +#endif + + fd = open(file, flags, 0660); + assert(fd != -1); + + qtest_qmp_fds_assert_success(qts, &fd, 1, "{'execute': 'add-fd', " + "'arguments': {'fdset-id': 1}}"); + close(fd); + } +} + +static void *migrate_hook_start_file_offset_fdset(QTestState *from, + QTestState *to) +{ + g_autofree char *file = g_strdup_printf("%s/%s", tmpfs, FILE_TEST_FILENAME); + + fdset_add_fds(from, file, O_WRONLY, 1, false); + fdset_add_fds(to, file, O_RDONLY, 1, false); + + return NULL; +} + +static void test_precopy_file_offset_fdset(void) +{ + g_autofree char *uri = g_strdup_printf("file:/dev/fdset/1,offset=%d", + FILE_TEST_OFFSET); + MigrateCommon args = { + .connect_uri = uri, + .listen_uri = "defer", + .start_hook = migrate_hook_start_file_offset_fdset, + }; + + test_file_common(&args, false); +} +#endif + +static void test_precopy_file_offset(void) +{ + g_autofree char *uri = g_strdup_printf("file:%s/%s,offset=%d", tmpfs, + FILE_TEST_FILENAME, + FILE_TEST_OFFSET); + MigrateCommon args = { + .connect_uri = uri, + .listen_uri = "defer", + }; + + test_file_common(&args, false); +} + +static void test_precopy_file_offset_bad(void) +{ + /* using a value not supported by qemu_strtosz() */ + g_autofree char *uri = g_strdup_printf("file:%s/%s,offset=0x20M", + tmpfs, FILE_TEST_FILENAME); + MigrateCommon args = { + .connect_uri = uri, + .listen_uri = "defer", + .result = MIG_TEST_QMP_ERROR, + }; + + test_file_common(&args, false); +} + +static void test_precopy_file_mapped_ram_live(void) +{ + g_autofree char *uri = g_strdup_printf("file:%s/%s", tmpfs, + FILE_TEST_FILENAME); + MigrateCommon args = { + .connect_uri = uri, + .listen_uri = "defer", + .start = { + .caps[MIGRATION_CAPABILITY_MAPPED_RAM] = true, + }, + }; + + test_file_common(&args, false); +} + +static void test_precopy_file_mapped_ram(void) +{ + g_autofree char *uri = g_strdup_printf("file:%s/%s", tmpfs, + FILE_TEST_FILENAME); + MigrateCommon args = { + .connect_uri = uri, + .listen_uri = "defer", + .start = { + .caps[MIGRATION_CAPABILITY_MAPPED_RAM] = true, + }, + }; + + test_file_common(&args, true); +} + +static void test_multifd_file_mapped_ram_live(void) +{ + g_autofree char *uri = g_strdup_printf("file:%s/%s", tmpfs, + FILE_TEST_FILENAME); + MigrateCommon args = { + .connect_uri = uri, + .listen_uri = "defer", + .start = { + .caps[MIGRATION_CAPABILITY_MULTIFD] = true, + .caps[MIGRATION_CAPABILITY_MAPPED_RAM] = true, + }, + }; + + test_file_common(&args, false); +} + +static void test_multifd_file_mapped_ram(void) +{ + g_autofree char *uri = g_strdup_printf("file:%s/%s", tmpfs, + FILE_TEST_FILENAME); + MigrateCommon args = { + .connect_uri = uri, + .listen_uri = "defer", + .start = { + .caps[MIGRATION_CAPABILITY_MULTIFD] = true, + .caps[MIGRATION_CAPABILITY_MAPPED_RAM] = true, + }, + }; + + test_file_common(&args, true); +} + +static void *migrate_hook_start_multifd_mapped_ram_dio(QTestState *from, + QTestState *to) +{ + migrate_set_parameter_bool(from, "direct-io", true); + migrate_set_parameter_bool(to, "direct-io", true); + + return NULL; +} + +static void test_multifd_file_mapped_ram_dio(void) +{ + g_autofree char *uri = g_strdup_printf("file:%s/%s", tmpfs, + FILE_TEST_FILENAME); + MigrateCommon args = { + .connect_uri = uri, + .listen_uri = "defer", + .start_hook = migrate_hook_start_multifd_mapped_ram_dio, + .start = { + .caps[MIGRATION_CAPABILITY_MAPPED_RAM] = true, + .caps[MIGRATION_CAPABILITY_MULTIFD] = true, + }, + }; + + if (!probe_o_direct_support(tmpfs)) { + g_test_skip("Filesystem does not support O_DIRECT"); + return; + } + + test_file_common(&args, true); +} + +#ifndef _WIN32 +static void migrate_hook_end_multifd_mapped_ram_fdset(QTestState *from, + QTestState *to, + void *opaque) +{ + QDict *resp; + QList *fdsets; + + /* + * Remove the fdsets after migration, otherwise a second migration + * would fail due fdset reuse. + */ + qtest_qmp_assert_success(from, "{'execute': 'remove-fd', " + "'arguments': { 'fdset-id': 1}}"); + + /* + * Make sure no fdsets are left after migration, otherwise a + * second migration would fail due fdset reuse. + */ + resp = qtest_qmp(from, "{'execute': 'query-fdsets', " + "'arguments': {}}"); + g_assert(qdict_haskey(resp, "return")); + fdsets = qdict_get_qlist(resp, "return"); + g_assert(fdsets && qlist_empty(fdsets)); + qobject_unref(resp); +} + +static void *migrate_hook_start_multifd_mapped_ram_fdset_dio(QTestState *from, + QTestState *to) +{ + g_autofree char *file = g_strdup_printf("%s/%s", tmpfs, FILE_TEST_FILENAME); + + fdset_add_fds(from, file, O_WRONLY, 2, true); + fdset_add_fds(to, file, O_RDONLY, 2, true); + + migrate_set_parameter_bool(from, "direct-io", true); + migrate_set_parameter_bool(to, "direct-io", true); + + return NULL; +} + +static void *migrate_hook_start_multifd_mapped_ram_fdset(QTestState *from, + QTestState *to) +{ + g_autofree char *file = g_strdup_printf("%s/%s", tmpfs, FILE_TEST_FILENAME); + + fdset_add_fds(from, file, O_WRONLY, 2, false); + fdset_add_fds(to, file, O_RDONLY, 2, false); + + return NULL; +} + +static void test_multifd_file_mapped_ram_fdset(void) +{ + g_autofree char *uri = g_strdup_printf("file:/dev/fdset/1,offset=%d", + FILE_TEST_OFFSET); + MigrateCommon args = { + .connect_uri = uri, + .listen_uri = "defer", + .start_hook = migrate_hook_start_multifd_mapped_ram_fdset, + .end_hook = migrate_hook_end_multifd_mapped_ram_fdset, + .start = { + .caps[MIGRATION_CAPABILITY_MAPPED_RAM] = true, + .caps[MIGRATION_CAPABILITY_MULTIFD] = true, + }, + }; + + test_file_common(&args, true); +} + +static void test_multifd_file_mapped_ram_fdset_dio(void) +{ + g_autofree char *uri = g_strdup_printf("file:/dev/fdset/1,offset=%d", + FILE_TEST_OFFSET); + MigrateCommon args = { + .connect_uri = uri, + .listen_uri = "defer", + .start_hook = migrate_hook_start_multifd_mapped_ram_fdset_dio, + .end_hook = migrate_hook_end_multifd_mapped_ram_fdset, + .start = { + .caps[MIGRATION_CAPABILITY_MAPPED_RAM] = true, + .caps[MIGRATION_CAPABILITY_MULTIFD] = true, + }, + }; + + if (!probe_o_direct_support(tmpfs)) { + g_test_skip("Filesystem does not support O_DIRECT"); + return; + } + + test_file_common(&args, true); +} +#endif /* !_WIN32 */ + +static void migration_test_add_file_smoke(MigrationTestEnv *env) +{ + migration_test_add("/migration/precopy/file", + test_precopy_file); + + migration_test_add("/migration/multifd/file/mapped-ram/dio", + test_multifd_file_mapped_ram_dio); +} + +void migration_test_add_file(MigrationTestEnv *env) +{ + tmpfs = env->tmpfs; + + migration_test_add_file_smoke(env); + + if (!env->full_set) { + return; + } + + migration_test_add("/migration/precopy/file/offset", + test_precopy_file_offset); +#ifndef _WIN32 + migration_test_add("/migration/precopy/file/offset/fdset", + test_precopy_file_offset_fdset); +#endif + migration_test_add("/migration/precopy/file/offset/bad", + test_precopy_file_offset_bad); + + migration_test_add("/migration/precopy/file/mapped-ram", + test_precopy_file_mapped_ram); + migration_test_add("/migration/precopy/file/mapped-ram/live", + test_precopy_file_mapped_ram_live); + + migration_test_add("/migration/multifd/file/mapped-ram", + test_multifd_file_mapped_ram); + migration_test_add("/migration/multifd/file/mapped-ram/live", + test_multifd_file_mapped_ram_live); + +#ifndef _WIN32 + migration_test_add("/migration/multifd/file/mapped-ram/fdset", + test_multifd_file_mapped_ram_fdset); + migration_test_add("/migration/multifd/file/mapped-ram/fdset/dio", + test_multifd_file_mapped_ram_fdset_dio); +#endif +} diff --git a/tests/qtest/migration/framework.c b/tests/qtest/migration/framework.c new file mode 100644 index 0000000..407c902 --- /dev/null +++ b/tests/qtest/migration/framework.c @@ -0,0 +1,1066 @@ +/* + * Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates + * based on the vhost-user-test.c that is: + * Copyright (c) 2014 Virtual Open Systems Sarl. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#include "qemu/osdep.h" + +#include "chardev/char.h" +#include "crypto/tlscredspsk.h" +#include "libqtest.h" +#include "migration/bootfile.h" +#include "migration/framework.h" +#include "migration/migration-qmp.h" +#include "migration/migration-util.h" +#include "ppc-util.h" +#include "qapi/error.h" +#include "qobject/qjson.h" +#include "qobject/qlist.h" +#include "qemu/module.h" +#include "qemu/option.h" +#include "qemu/range.h" +#include "qemu/sockets.h" + + +#define QEMU_VM_FILE_MAGIC 0x5145564d +#define QEMU_ENV_SRC "QTEST_QEMU_BINARY_SRC" +#define QEMU_ENV_DST "QTEST_QEMU_BINARY_DST" +#define MULTIFD_TEST_CHANNELS 4 + +unsigned start_address; +unsigned end_address; +static QTestMigrationState src_state; +static QTestMigrationState dst_state; +static char *tmpfs; + +/* + * An initial 3 MB offset is used as that corresponds + * to ~1 sec of data transfer with our bandwidth setting. + */ +#define MAGIC_OFFSET_BASE (3 * 1024 * 1024) +/* + * A further 1k is added to ensure we're not a multiple + * of TEST_MEM_PAGE_SIZE, thus avoid clash with writes + * from the migration guest workload. + */ +#define MAGIC_OFFSET_SHUFFLE 1024 +#define MAGIC_OFFSET (MAGIC_OFFSET_BASE + MAGIC_OFFSET_SHUFFLE) +#define MAGIC_MARKER 0xFEED12345678CAFEULL + + +/* + * Wait for some output in the serial output file, + * we get an 'A' followed by an endless string of 'B's + * but on the destination we won't have the A (unless we enabled suspend/resume) + */ +void wait_for_serial(const char *side) +{ + g_autofree char *serialpath = g_strdup_printf("%s/%s", tmpfs, side); + FILE *serialfile = fopen(serialpath, "r"); + + do { + int readvalue = fgetc(serialfile); + + switch (readvalue) { + case 'A': + /* Fine */ + break; + + case 'B': + /* It's alive! */ + fclose(serialfile); + return; + + case EOF: + fseek(serialfile, 0, SEEK_SET); + usleep(1000); + break; + + default: + fprintf(stderr, "Unexpected %d on %s serial\n", readvalue, side); + g_assert_not_reached(); + } + } while (true); +} + +void migrate_prepare_for_dirty_mem(QTestState *from) +{ + /* + * The guest workflow iterates from start_address to + * end_address, writing 1 byte every TEST_MEM_PAGE_SIZE + * bytes. + * + * IOW, if we write to mem at a point which is NOT + * a multiple of TEST_MEM_PAGE_SIZE, our write won't + * conflict with the migration workflow. + * + * We put in a marker here, that we'll use to determine + * when the data has been transferred to the dst. + */ + qtest_writeq(from, start_address + MAGIC_OFFSET, MAGIC_MARKER); +} + +void migrate_wait_for_dirty_mem(QTestState *from, QTestState *to) +{ + uint64_t watch_address = start_address + MAGIC_OFFSET_BASE; + uint64_t marker_address = start_address + MAGIC_OFFSET; + uint8_t watch_byte; + + /* + * Wait for the MAGIC_MARKER to get transferred, as an + * indicator that a migration pass has made some known + * amount of progress. + */ + do { + usleep(1000 * 10); + } while (qtest_readq(to, marker_address) != MAGIC_MARKER); + + + /* If suspended, src only iterates once, and watch_byte may never change */ + if (src_state.suspend_me) { + return; + } + + /* + * Now ensure that already transferred bytes are + * dirty again from the guest workload. Note the + * guest byte value will wrap around and by chance + * match the original watch_byte. This is harmless + * as we'll eventually see a different value if we + * keep watching + */ + watch_byte = qtest_readb(from, watch_address); + do { + usleep(1000 * 10); + } while (qtest_readb(from, watch_address) == watch_byte); +} + +static void check_guests_ram(QTestState *who) +{ + /* + * Our ASM test will have been incrementing one byte from each page from + * start_address to < end_address in order. This gives us a constraint + * that any page's byte should be equal or less than the previous pages + * byte (mod 256); and they should all be equal except for one transition + * at the point where we meet the incrementer. (We're running this with + * the guest stopped). + */ + unsigned address; + uint8_t first_byte; + uint8_t last_byte; + bool hit_edge = false; + int bad = 0; + + qtest_memread(who, start_address, &first_byte, 1); + last_byte = first_byte; + + for (address = start_address + TEST_MEM_PAGE_SIZE; address < end_address; + address += TEST_MEM_PAGE_SIZE) + { + uint8_t b; + qtest_memread(who, address, &b, 1); + if (b != last_byte) { + if (((b + 1) % 256) == last_byte && !hit_edge) { + /* + * This is OK, the guest stopped at the point of + * incrementing the previous page but didn't get + * to us yet. + */ + hit_edge = true; + last_byte = b; + } else { + bad++; + if (bad <= 10) { + fprintf(stderr, "Memory content inconsistency at %x" + " first_byte = %x last_byte = %x current = %x" + " hit_edge = %x\n", + address, first_byte, last_byte, b, hit_edge); + } + } + } + } + if (bad >= 10) { + fprintf(stderr, "and in another %d pages", bad - 10); + } + g_assert(bad == 0); +} + +static void cleanup(const char *filename) +{ + g_autofree char *path = g_strdup_printf("%s/%s", tmpfs, filename); + + unlink(path); +} + +static QList *migrate_start_get_qmp_capabilities(const MigrateStart *args) +{ + QList *capabilities = NULL; + + if (args->oob) { + capabilities = qlist_new(); + qlist_append_str(capabilities, "oob"); + } + return capabilities; +} + +static void migrate_start_set_capabilities(QTestState *from, QTestState *to, + MigrateStart *args) +{ + /* + * MigrationCapability_lookup and MIGRATION_CAPABILITY_ constants + * are from qapi-types-migration.h. + */ + for (uint8_t i = 0; i < MIGRATION_CAPABILITY__MAX; i++) { + if (!args->caps[i]) { + continue; + } + if (from) { + migrate_set_capability(from, + MigrationCapability_lookup.array[i], true); + } + if (to) { + migrate_set_capability(to, + MigrationCapability_lookup.array[i], true); + } + } + + /* + * Always enable migration events. Libvirt always uses it, let's try + * to mimic as closer as that. + */ + migrate_set_capability(from, "events", true); + if (!args->defer_target_connect) { + migrate_set_capability(to, "events", true); + } + + /* + * Default number of channels should be fine for most + * tests. Individual tests can override by calling + * migrate_set_parameter() directly. + */ + if (args->caps[MIGRATION_CAPABILITY_MULTIFD]) { + migrate_set_parameter_int(from, "multifd-channels", + MULTIFD_TEST_CHANNELS); + migrate_set_parameter_int(to, "multifd-channels", + MULTIFD_TEST_CHANNELS); + } + + return; +} + +int migrate_start(QTestState **from, QTestState **to, const char *uri, + MigrateStart *args) +{ + /* options for source and target */ + g_autofree gchar *arch_opts = NULL; + g_autofree gchar *cmd_source = NULL; + g_autofree gchar *cmd_target = NULL; + const gchar *ignore_stderr; + g_autofree char *shmem_opts = NULL; + g_autofree char *shmem_path = NULL; + const char *kvm_opts = NULL; + const char *arch = qtest_get_arch(); + const char *memory_size; + const char *machine_alias, *machine_opts = ""; + 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)) { + g_test_skip("/dev/shm is not supported"); + return -1; + } + } + + dst_state = (QTestMigrationState) { }; + src_state = (QTestMigrationState) { }; + bootpath = bootfile_create(arch, tmpfs, args->suspend_me); + src_state.suspend_me = args->suspend_me; + + if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { + memory_size = "150M"; + + if (g_str_equal(arch, "i386")) { + machine_alias = "pc"; + } else { + machine_alias = "q35"; + } + arch_opts = g_strdup_printf( + "-drive if=none,id=d0,file=%s,format=raw " + "-device ide-hd,drive=d0,secs=1,cyls=1,heads=1", bootpath); + start_address = X86_TEST_MEM_START; + end_address = X86_TEST_MEM_END; + } else if (g_str_equal(arch, "s390x")) { + memory_size = "128M"; + machine_alias = "s390-ccw-virtio"; + arch_opts = g_strdup_printf("-bios %s", bootpath); + start_address = S390_TEST_MEM_START; + end_address = S390_TEST_MEM_END; + } else if (strcmp(arch, "ppc64") == 0) { + memory_size = "256M"; + start_address = PPC_TEST_MEM_START; + end_address = PPC_TEST_MEM_END; + machine_alias = "pseries"; + machine_opts = "vsmt=8"; + arch_opts = g_strdup_printf( + "-nodefaults -machine " PSERIES_DEFAULT_CAPABILITIES " " + "-bios %s", bootpath); + } else if (strcmp(arch, "aarch64") == 0) { + memory_size = "150M"; + machine_alias = "virt"; + machine_opts = "gic-version=3"; + arch_opts = g_strdup_printf("-cpu max -kernel %s", bootpath); + start_address = ARM_TEST_MEM_START; + end_address = ARM_TEST_MEM_END; + } else { + g_assert_not_reached(); + } + + if (!getenv("QTEST_LOG") && args->hide_stderr) { +#ifndef _WIN32 + ignore_stderr = "2>/dev/null"; +#else + /* + * On Windows the QEMU executable is created via CreateProcess() and + * IO redirection does not work, so don't bother adding IO redirection + * to the command line. + */ + ignore_stderr = ""; +#endif + } else { + ignore_stderr = ""; + } + + if (args->use_shmem) { + shmem_path = g_strdup_printf("/dev/shm/qemu-%d", getpid()); + shmem_opts = g_strdup_printf( + "-object memory-backend-file,id=mem0,size=%s" + ",mem-path=%s,share=on -numa node,memdev=mem0", + 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"; + } + + if (!qtest_has_machine(machine_alias)) { + g_autofree char *msg = g_strdup_printf("machine %s not supported", machine_alias); + g_test_skip(msg); + return -1; + } + + machine = resolve_machine_version(machine_alias, QEMU_ENV_SRC, + QEMU_ENV_DST); + + g_test_message("Using machine type: %s", machine); + + cmd_source = g_strdup_printf("-accel kvm%s -accel tcg " + "-machine %s,%s " + "-name source,debug-threads=on " + "%s " + "-serial file:%s/src_serial " + "%s %s %s %s", + kvm_opts ? kvm_opts : "", + machine, machine_opts, + 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_ext(QEMU_ENV_SRC, cmd_source, 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 " + "%s " + "-serial file:%s/dest_serial " + "-incoming %s " + "%s %s %s %s %s", + kvm_opts ? kvm_opts : "", + machine, machine_opts, + memory_backend, tmpfs, uri, + events, + arch_opts ? arch_opts : "", + shmem_opts ? shmem_opts : "", + args->opts_target ? args->opts_target : "", + ignore_stderr); + *to = qtest_init_ext(QEMU_ENV_DST, cmd_target, capabilities, + !args->defer_target_connect); + qtest_qmp_set_event_callback(*to, + migrate_watch_for_events, + &dst_state); + + /* + * Remove shmem file immediately to avoid memory leak in test failed case. + * It's valid because QEMU has already opened this file + */ + if (args->use_shmem) { + unlink(shmem_path); + } + + migrate_start_set_capabilities(*from, *to, args); + + return 0; +} + +void migrate_end(QTestState *from, QTestState *to, bool test_dest) +{ + unsigned char dest_byte_a, dest_byte_b, dest_byte_c, dest_byte_d; + + qtest_quit(from); + + if (test_dest) { + qtest_memread(to, start_address, &dest_byte_a, 1); + + /* Destination still running, wait for a byte to change */ + do { + qtest_memread(to, start_address, &dest_byte_b, 1); + usleep(1000 * 10); + } while (dest_byte_a == dest_byte_b); + + qtest_qmp_assert_success(to, "{ 'execute' : 'stop'}"); + + /* With it stopped, check nothing changes */ + qtest_memread(to, start_address, &dest_byte_c, 1); + usleep(1000 * 200); + qtest_memread(to, start_address, &dest_byte_d, 1); + g_assert_cmpint(dest_byte_c, ==, dest_byte_d); + + check_guests_ram(to); + } + + qtest_quit(to); + + cleanup("migsocket"); + cleanup("cpr.sock"); + cleanup("src_serial"); + cleanup("dest_serial"); + cleanup(FILE_TEST_FILENAME); +} + +static int migrate_postcopy_prepare(QTestState **from_ptr, + QTestState **to_ptr, + MigrateCommon *args) +{ + QTestState *from, *to; + + /* set postcopy capabilities */ + args->start.caps[MIGRATION_CAPABILITY_POSTCOPY_BLOCKTIME] = true; + args->start.caps[MIGRATION_CAPABILITY_POSTCOPY_RAM] = true; + + if (migrate_start(&from, &to, "defer", &args->start)) { + return -1; + } + + if (args->start_hook) { + args->postcopy_data = args->start_hook(from, to); + } + + migrate_ensure_non_converge(from); + migrate_prepare_for_dirty_mem(from); + qtest_qmp_assert_success(to, "{ 'execute': 'migrate-incoming'," + " 'arguments': { " + " 'channels': [ { 'channel-type': 'main'," + " 'addr': { 'transport': 'socket'," + " 'type': 'inet'," + " 'host': '127.0.0.1'," + " 'port': '0' } } ] } }"); + + /* Wait for the first serial output from the source */ + wait_for_serial("src_serial"); + wait_for_suspend(from, &src_state); + + migrate_qmp(from, to, NULL, NULL, "{}"); + + migrate_wait_for_dirty_mem(from, to); + + *from_ptr = from; + *to_ptr = to; + + return 0; +} + +static void migrate_postcopy_complete(QTestState *from, QTestState *to, + MigrateCommon *args) +{ + MigrationTestEnv *env = migration_get_env(); + + wait_for_migration_complete(from); + + if (args->start.suspend_me) { + /* wakeup succeeds only if guest is suspended */ + qtest_qmp_assert_success(to, "{'execute': 'system_wakeup'}"); + } + + /* Make sure we get at least one "B" on destination */ + wait_for_serial("dest_serial"); + + if (env->uffd_feature_thread_id) { + read_blocktime(to); + } + + if (args->end_hook) { + args->end_hook(from, to, args->postcopy_data); + args->postcopy_data = NULL; + } + + migrate_end(from, to, true); +} + +void test_postcopy_common(MigrateCommon *args) +{ + QTestState *from, *to; + + if (migrate_postcopy_prepare(&from, &to, args)) { + return; + } + migrate_postcopy_start(from, to, &src_state); + migrate_postcopy_complete(from, to, args); +} + +static void wait_for_postcopy_status(QTestState *one, const char *status) +{ + wait_for_migration_status(one, status, + (const char * []) { + "failed", "active", + "completed", NULL + }); +} + +static void postcopy_recover_fail(QTestState *from, QTestState *to, + PostcopyRecoveryFailStage stage) +{ +#ifndef _WIN32 + bool fail_early = (stage == POSTCOPY_FAIL_CHANNEL_ESTABLISH); + int ret, pair1[2], pair2[2]; + char c; + + g_assert(stage > POSTCOPY_FAIL_NONE && stage < POSTCOPY_FAIL_MAX); + + /* Create two unrelated socketpairs */ + ret = qemu_socketpair(PF_LOCAL, SOCK_STREAM, 0, pair1); + g_assert_cmpint(ret, ==, 0); + + ret = qemu_socketpair(PF_LOCAL, SOCK_STREAM, 0, pair2); + g_assert_cmpint(ret, ==, 0); + + /* + * Give the guests unpaired ends of the sockets, so they'll all blocked + * at reading. This mimics a wrong channel established. + */ + qtest_qmp_fds_assert_success(from, &pair1[0], 1, + "{ 'execute': 'getfd'," + " 'arguments': { 'fdname': 'fd-mig' }}"); + qtest_qmp_fds_assert_success(to, &pair2[0], 1, + "{ 'execute': 'getfd'," + " 'arguments': { 'fdname': 'fd-mig' }}"); + + /* + * Write the 1st byte as QEMU_VM_COMMAND (0x8) for the dest socket, to + * emulate the 1st byte of a real recovery, but stops from there to + * keep dest QEMU in RECOVER. This is needed so that we can kick off + * the recover process on dest QEMU (by triggering the G_IO_IN event). + * + * NOTE: this trick is not needed on src QEMUs, because src doesn't + * rely on an pre-existing G_IO_IN event, so it will always trigger the + * upcoming recovery anyway even if it can read nothing. + */ +#define QEMU_VM_COMMAND 0x08 + c = QEMU_VM_COMMAND; + ret = send(pair2[1], &c, 1, 0); + g_assert_cmpint(ret, ==, 1); + + if (stage == POSTCOPY_FAIL_CHANNEL_ESTABLISH) { + /* + * This will make src QEMU to fail at an early stage when trying to + * resume later, where it shouldn't reach RECOVER stage at all. + */ + close(pair1[1]); + } + + migrate_recover(to, "fd:fd-mig"); + migrate_qmp(from, to, "fd:fd-mig", NULL, "{'resume': true}"); + + /* + * Source QEMU has an extra RECOVER_SETUP phase, dest doesn't have it. + * Make sure it appears along the way. + */ + migration_event_wait(from, "postcopy-recover-setup"); + + if (fail_early) { + /* + * When fails at reconnection, src QEMU will automatically goes + * back to PAUSED state. Making sure there is an event in this + * case: Libvirt relies on this to detect early reconnection + * errors. + */ + migration_event_wait(from, "postcopy-paused"); + } else { + /* + * We want to test "fail later" at RECOVER stage here. Make sure + * both QEMU instances will go into RECOVER stage first, then test + * kicking them out using migrate-pause. + * + * Explicitly check the RECOVER event on src, that's what Libvirt + * relies on, rather than polling. + */ + migration_event_wait(from, "postcopy-recover"); + wait_for_postcopy_status(from, "postcopy-recover"); + + /* Need an explicit kick on src QEMU in this case */ + migrate_pause(from); + } + + /* + * For all failure cases, we'll reach such states on both sides now. + * Check them. + */ + wait_for_postcopy_status(from, "postcopy-paused"); + wait_for_postcopy_status(to, "postcopy-recover"); + + /* + * Kick dest QEMU out too. This is normally not needed in reality + * because when the channel is shutdown it should also happen on src. + * However here we used separate socket pairs so we need to do that + * explicitly. + */ + migrate_pause(to); + wait_for_postcopy_status(to, "postcopy-paused"); + + close(pair1[0]); + close(pair2[0]); + close(pair2[1]); + + if (stage != POSTCOPY_FAIL_CHANNEL_ESTABLISH) { + close(pair1[1]); + } +#endif +} + +void test_postcopy_recovery_common(MigrateCommon *args) +{ + QTestState *from, *to; + g_autofree char *uri = NULL; + + /* + * Always enable OOB QMP capability for recovery tests, migrate-recover is + * executed out-of-band + */ + args->start.oob = true; + + /* Always hide errors for postcopy recover tests since they're expected */ + args->start.hide_stderr = true; + + if (migrate_postcopy_prepare(&from, &to, args)) { + return; + } + + /* Turn postcopy speed down, 4K/s is slow enough on any machines */ + migrate_set_parameter_int(from, "max-postcopy-bandwidth", 4096); + + /* Now we start the postcopy */ + migrate_postcopy_start(from, to, &src_state); + + /* + * Wait until postcopy is really started; we can only run the + * migrate-pause command during a postcopy + */ + wait_for_migration_status(from, "postcopy-active", NULL); + + /* + * Manually stop the postcopy migration. This emulates a network + * failure with the migration socket + */ + migrate_pause(from); + + /* + * Wait for destination side to reach postcopy-paused state. The + * migrate-recover command can only succeed if destination machine + * is in the paused state + */ + wait_for_postcopy_status(to, "postcopy-paused"); + wait_for_postcopy_status(from, "postcopy-paused"); + + if (args->postcopy_recovery_fail_stage) { + /* + * Test when a wrong socket specified for recover, and then the + * ability to kick it out, and continue with a correct socket. + */ + postcopy_recover_fail(from, to, args->postcopy_recovery_fail_stage); + /* continue with a good recovery */ + } + + /* + * Create a new socket to emulate a new channel that is different + * from the broken migration channel; tell the destination to + * listen to the new port + */ + uri = g_strdup_printf("unix:%s/migsocket-recover", tmpfs); + migrate_recover(to, uri); + + /* + * Try to rebuild the migration channel using the resume flag and + * the newly created channel + */ + migrate_qmp(from, to, uri, NULL, "{'resume': true}"); + + /* Restore the postcopy bandwidth to unlimited */ + migrate_set_parameter_int(from, "max-postcopy-bandwidth", 0); + + migrate_postcopy_complete(from, to, args); +} + +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; + } + + if (args->start_hook) { + data_hook = args->start_hook(from, to); + } + + /* Wait for the first serial output from the source */ + if (args->result == MIG_TEST_SUCCEED) { + wait_for_serial("src_serial"); + wait_for_suspend(from, &src_state); + } + + if (args->live) { + migrate_ensure_non_converge(from); + migrate_prepare_for_dirty_mem(from); + } else { + /* + * Testing non-live migration, we allow it to run at + * full speed to ensure short test case duration. + * For tests expected to fail, we don't need to + * change anything. + */ + if (args->result == MIG_TEST_SUCCEED) { + qtest_qmp_assert_success(from, "{ 'execute' : 'stop'}"); + wait_for_stop(from, &src_state); + migrate_ensure_converge(from); + } + } + + /* + * 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, out_channels, "{}"); + goto finish; + } + + 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; + wait_for_migration_fail(from, allow_active); + + if (args->result == MIG_TEST_FAIL_DEST_QUIT_ERR) { + qtest_set_expected_status(to, EXIT_FAILURE); + } + } else { + if (args->live) { + /* + * For initial iteration(s) we must do a full pass, + * but for the final iteration, we need only wait + * for some dirty mem before switching to converge + */ + while (args->iterations > 1) { + wait_for_migration_pass(from, &src_state); + args->iterations--; + } + migrate_wait_for_dirty_mem(from, to); + + migrate_ensure_converge(from); + + /* + * We do this first, as it has a timeout to stop us + * hanging forever if migration didn't converge + */ + wait_for_migration_complete(from); + + wait_for_stop(from, &src_state); + + } else { + wait_for_migration_complete(from); + /* + * Must wait for dst to finish reading all incoming + * data on the socket before issuing 'cont' otherwise + * it'll be ignored + */ + wait_for_migration_complete(to); + + qtest_qmp_assert_success(to, "{ 'execute' : 'cont'}"); + } + + wait_for_resume(to, &dst_state); + + if (args->start.suspend_me) { + /* wakeup succeeds only if guest is suspended */ + qtest_qmp_assert_success(to, "{'execute': 'system_wakeup'}"); + } + + wait_for_serial("dest_serial"); + } + +finish: + if (args->end_hook) { + args->end_hook(from, to, data_hook); + } + + migrate_end(from, to, args->result == MIG_TEST_SUCCEED); +} + +static void file_dirty_offset_region(void) +{ + g_autofree char *path = g_strdup_printf("%s/%s", tmpfs, FILE_TEST_FILENAME); + size_t size = FILE_TEST_OFFSET; + g_autofree char *data = g_new0(char, size); + + memset(data, FILE_TEST_MARKER, size); + g_assert(g_file_set_contents(path, data, size, NULL)); +} + +static void file_check_offset_region(void) +{ + g_autofree char *path = g_strdup_printf("%s/%s", tmpfs, FILE_TEST_FILENAME); + size_t size = FILE_TEST_OFFSET; + g_autofree char *expected = g_new0(char, size); + g_autofree char *actual = NULL; + uint64_t *stream_start; + + /* + * Ensure the skipped offset region's data has not been touched + * and the migration stream starts at the right place. + */ + + memset(expected, FILE_TEST_MARKER, size); + + g_assert(g_file_get_contents(path, &actual, NULL, NULL)); + g_assert(!memcmp(actual, expected, size)); + + stream_start = (uint64_t *)(actual + size); + g_assert_cmpint(cpu_to_be64(*stream_start) >> 32, ==, QEMU_VM_FILE_MAGIC); +} + +void test_file_common(MigrateCommon *args, bool stop_src) +{ + QTestState *from, *to; + void *data_hook = NULL; + bool check_offset = false; + + if (migrate_start(&from, &to, args->listen_uri, &args->start)) { + return; + } + + /* + * File migration is never live. We can keep the source VM running + * during migration, but the destination will not be running + * concurrently. + */ + g_assert_false(args->live); + + if (g_strrstr(args->connect_uri, "offset=")) { + check_offset = true; + /* + * This comes before the start_hook because it's equivalent to + * a management application creating the file and writing to + * it so hooks should expect the file to be already present. + */ + file_dirty_offset_region(); + } + + if (args->start_hook) { + data_hook = args->start_hook(from, to); + } + + migrate_ensure_converge(from); + wait_for_serial("src_serial"); + + if (stop_src) { + qtest_qmp_assert_success(from, "{ 'execute' : 'stop'}"); + wait_for_stop(from, &src_state); + } + + if (args->result == MIG_TEST_QMP_ERROR) { + migrate_qmp_fail(from, args->connect_uri, NULL, "{}"); + goto finish; + } + + migrate_qmp(from, to, args->connect_uri, NULL, "{}"); + wait_for_migration_complete(from); + + /* + * We need to wait for the source to finish before starting the + * destination. + */ + migrate_incoming_qmp(to, args->connect_uri, NULL, "{}"); + wait_for_migration_complete(to); + + if (stop_src) { + qtest_qmp_assert_success(to, "{ 'execute' : 'cont'}"); + } + wait_for_resume(to, &dst_state); + + wait_for_serial("dest_serial"); + + if (check_offset) { + file_check_offset_region(); + } + +finish: + if (args->end_hook) { + args->end_hook(from, to, data_hook); + } + + migrate_end(from, to, args->result == MIG_TEST_SUCCEED); +} + +void *migrate_hook_start_precopy_tcp_multifd_common(QTestState *from, + QTestState *to, + const char *method) +{ + migrate_set_parameter_str(from, "multifd-compression", method); + migrate_set_parameter_str(to, "multifd-compression", method); + + /* Start incoming migration from the 1st socket */ + migrate_incoming_qmp(to, "tcp:127.0.0.1:0", NULL, "{}"); + + return NULL; +} + +QTestMigrationState *get_src(void) +{ + return &src_state; +} + +MigrationTestEnv *migration_get_env(void) +{ + static MigrationTestEnv *env; + g_autoptr(GError) err = NULL; + + if (env) { + return env; + } + + env = g_new0(MigrationTestEnv, 1); + env->qemu_src = getenv(QEMU_ENV_SRC); + env->qemu_dst = getenv(QEMU_ENV_DST); + + /* + * The default QTEST_QEMU_BINARY must always be provided because + * that is what helpers use to query the accel type and + * architecture. + */ + if (env->qemu_src && env->qemu_dst) { + g_test_message("Only one of %s, %s is allowed", + QEMU_ENV_SRC, QEMU_ENV_DST); + exit(1); + } + + env->has_kvm = qtest_has_accel("kvm"); + env->has_tcg = qtest_has_accel("tcg"); + + if (!env->has_tcg && !env->has_kvm) { + g_test_skip("No KVM or TCG accelerator available"); + return env; + } + + env->has_dirty_ring = kvm_dirty_ring_supported(); + env->has_uffd = ufd_version_check(&env->uffd_feature_thread_id); + env->arch = qtest_get_arch(); + env->is_x86 = !strcmp(env->arch, "i386") || !strcmp(env->arch, "x86_64"); + + env->tmpfs = g_dir_make_tmp("migration-test-XXXXXX", &err); + if (!env->tmpfs) { + g_test_message("Can't create temporary directory in %s: %s", + g_get_tmp_dir(), err->message); + } + g_assert(env->tmpfs); + + tmpfs = env->tmpfs; + + return env; +} + +int migration_env_clean(MigrationTestEnv *env) +{ + char *tmpfs; + int ret = 0; + + if (!env) { + return ret; + } + + bootfile_delete(); + + tmpfs = env->tmpfs; + ret = rmdir(tmpfs); + if (ret != 0) { + g_test_message("unable to rmdir: path (%s): %s", + tmpfs, strerror(errno)); + } + g_free(tmpfs); + + return ret; +} diff --git a/tests/qtest/migration/framework.h b/tests/qtest/migration/framework.h new file mode 100644 index 0000000..01e425e --- /dev/null +++ b/tests/qtest/migration/framework.h @@ -0,0 +1,251 @@ +/* + * Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates + * based on the vhost-user-test.c that is: + * Copyright (c) 2014 Virtual Open Systems Sarl. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#ifndef TEST_FRAMEWORK_H +#define TEST_FRAMEWORK_H + +#include "libqtest.h" +#include <qapi/qapi-types-migration.h> + +#define FILE_TEST_FILENAME "migfile" +#define FILE_TEST_OFFSET 0x1000 +#define FILE_TEST_MARKER 'X' + +typedef struct MigrationTestEnv { + bool has_kvm; + bool has_tcg; + bool has_uffd; + bool uffd_feature_thread_id; + bool has_dirty_ring; + bool is_x86; + bool full_set; + const char *arch; + const char *qemu_src; + const char *qemu_dst; + char *tmpfs; +} MigrationTestEnv; + +MigrationTestEnv *migration_get_env(void); +int migration_env_clean(MigrationTestEnv *env); + +/* + * A hook that runs after the src and dst QEMUs have been + * created, but before the migration is started. This can + * be used to set migration parameters and capabilities. + * + * Returns: NULL, or a pointer to opaque state to be + * later passed to the TestMigrateEndHook + */ +typedef void * (*TestMigrateStartHook)(QTestState *from, + QTestState *to); + +/* + * A hook that runs after the migration has finished, + * regardless of whether it succeeded or failed, but + * before QEMU has terminated (unless it self-terminated + * due to migration error) + * + * @opaque is a pointer to state previously returned + * by the TestMigrateStartHook if any, or NULL. + */ +typedef void (*TestMigrateEndHook)(QTestState *from, + QTestState *to, + void *opaque); + +/* + * Our goal is to ensure that we run a single full migration + * iteration, and also dirty memory, ensuring that at least + * one further iteration is required. + * + * We can't directly synchronize with the start of a migration + * so we have to apply some tricks monitoring memory that is + * transferred. + * + * Initially we set the migration bandwidth to an insanely + * low value, with tiny max downtime too. This basically + * guarantees migration will never complete. + * + * This will result in a test that is unacceptably slow though, + * so we can't let the entire migration pass run at this speed. + * Our intent is to let it run just long enough that we can + * prove data prior to the marker has been transferred *AND* + * also prove this transferred data is dirty again. + * + * Before migration starts, we write a 64-bit magic marker + * into a fixed location in the src VM RAM. + * + * Then watch dst memory until the marker appears. This is + * proof that start_address -> MAGIC_OFFSET_BASE has been + * transferred. + * + * Finally we go back to the source and read a byte just + * before the marker until we see it flip in value. This + * is proof that start_address -> MAGIC_OFFSET_BASE + * is now dirty again. + * + * IOW, we're guaranteed at least a 2nd migration pass + * at this point. + * + * We can now let migration run at full speed to finish + * the test + */ +typedef struct { + /* + * QTEST_LOG=1 may override this. When QTEST_LOG=1, we always dump errors + * unconditionally, because it means the user would like to be verbose. + */ + bool hide_stderr; + bool use_shmem; + /* only launch the target process */ + bool only_target; + /* Use dirty ring if true; dirty logging otherwise */ + bool use_dirty_ring; + const char *opts_source; + const char *opts_target; + /* suspend the src before migrating to dest. */ + 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; + + /* + * Migration capabilities to be set in both source and + * destination. For unilateral capabilities, use + * migration_set_capabilities(). + */ + bool caps[MIGRATION_CAPABILITY__MAX]; +} MigrateStart; + +typedef enum PostcopyRecoveryFailStage { + /* + * "no failure" must be 0 as it's the default. OTOH, real failure + * cases must be >0 to make sure they trigger by a "if" test. + */ + POSTCOPY_FAIL_NONE = 0, + POSTCOPY_FAIL_CHANNEL_ESTABLISH, + POSTCOPY_FAIL_RECOVERY, + POSTCOPY_FAIL_MAX +} PostcopyRecoveryFailStage; + +typedef struct { + /* Optional: fine tune start parameters */ + MigrateStart start; + + /* Required: the URI for the dst QEMU to listen on */ + const char *listen_uri; + + /* + * Optional: the URI for the src QEMU to connect to + * If NULL, then it will query the dst QEMU for its actual + * listening address and use that as the connect address. + * This allows for dynamically picking a free TCP port. + */ + const char *connect_uri; + + /* + * Optional: JSON-formatted list of src QEMU URIs. If a port is + * defined as '0' in any QDict key a value of '0' will be + * automatically converted to the correct destination port. + */ + 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 */ + TestMigrateEndHook end_hook; + + /* + * Optional: normally we expect the migration process to complete. + * + * There can be a variety of reasons and stages in which failure + * can happen during tests. + * + * If a failure is expected to happen at time of establishing + * the connection, then MIG_TEST_FAIL will indicate that the dst + * QEMU is expected to stay running and accept future migration + * connections. + * + * If a failure is expected to happen while processing the + * migration stream, then MIG_TEST_FAIL_DEST_QUIT_ERR will indicate + * that the dst QEMU is expected to quit with non-zero exit status + */ + enum { + /* This test should succeed, the default */ + MIG_TEST_SUCCEED = 0, + /* This test should fail, dest qemu should keep alive */ + MIG_TEST_FAIL, + /* This test should fail, dest qemu should fail with abnormal status */ + MIG_TEST_FAIL_DEST_QUIT_ERR, + /* The QMP command for this migration should fail with an error */ + MIG_TEST_QMP_ERROR, + } result; + + /* + * Optional: set number of migration passes to wait for, if live==true. + * If zero, then merely wait for a few MB of dirty data + */ + unsigned int iterations; + + /* + * Optional: whether the guest CPUs should be running during a precopy + * migration test. We used to always run with live but it took much + * longer so we reduced live tests to only the ones that have solid + * reason to be tested live-only. For each of the new test cases for + * precopy please provide justifications to use live explicitly (please + * refer to existing ones with live=true), or use live=off by default. + */ + bool live; + + /* Postcopy specific fields */ + void *postcopy_data; + PostcopyRecoveryFailStage postcopy_recovery_fail_stage; +} MigrateCommon; + +void wait_for_serial(const char *side); +void migrate_prepare_for_dirty_mem(QTestState *from); +void migrate_wait_for_dirty_mem(QTestState *from, QTestState *to); +int migrate_start(QTestState **from, QTestState **to, const char *uri, + MigrateStart *args); +void migrate_end(QTestState *from, QTestState *to, bool test_dest); + +void test_postcopy_common(MigrateCommon *args); +void test_postcopy_recovery_common(MigrateCommon *args); +void test_precopy_common(MigrateCommon *args); +void test_file_common(MigrateCommon *args, bool stop_src); +void *migrate_hook_start_precopy_tcp_multifd_common(QTestState *from, + QTestState *to, + const char *method); + +typedef struct QTestMigrationState QTestMigrationState; +QTestMigrationState *get_src(void); + +#ifdef CONFIG_GNUTLS +void migration_test_add_tls(MigrationTestEnv *env); +#else +static inline void migration_test_add_tls(MigrationTestEnv *env) {}; +#endif +void migration_test_add_compression(MigrationTestEnv *env); +void migration_test_add_postcopy(MigrationTestEnv *env); +void migration_test_add_file(MigrationTestEnv *env); +void migration_test_add_precopy(MigrationTestEnv *env); +void migration_test_add_cpr(MigrationTestEnv *env); +void migration_test_add_misc(MigrationTestEnv *env); + +#endif /* TEST_FRAMEWORK_H */ diff --git a/tests/migration/i386/Makefile b/tests/qtest/migration/i386/Makefile index 37a72ae..37a72ae 100644 --- a/tests/migration/i386/Makefile +++ b/tests/qtest/migration/i386/Makefile diff --git a/tests/migration/i386/a-b-bootblock.S b/tests/qtest/migration/i386/a-b-bootblock.S index 6f39eb6..6f39eb6 100644 --- a/tests/migration/i386/a-b-bootblock.S +++ b/tests/qtest/migration/i386/a-b-bootblock.S diff --git a/tests/migration/i386/a-b-bootblock.h b/tests/qtest/migration/i386/a-b-bootblock.h index c83f871..c83f871 100644 --- a/tests/migration/i386/a-b-bootblock.h +++ b/tests/qtest/migration/i386/a-b-bootblock.h diff --git a/tests/qtest/migration/migration-qmp.c b/tests/qtest/migration/migration-qmp.c new file mode 100644 index 0000000..fb59741 --- /dev/null +++ b/tests/qtest/migration/migration-qmp.c @@ -0,0 +1,520 @@ +/* + * QTest QMP helpers for migration + * + * Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates + * based on the vhost-user-test.c that is: + * Copyright (c) 2014 Virtual Open Systems Sarl. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#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 "qobject/qdict.h" +#include "qobject/qjson.h" +#include "qobject/qlist.h" +#include "qapi/qobject-input-visitor.h" +#include "qapi/qobject-output-visitor.h" + +/* + * Number of seconds we wait when looking for migration + * status changes, to avoid test suite hanging forever + * when things go wrong. Needs to be higher enough to + * avoid false positives on loaded hosts. + */ +#define MIGRATION_STATUS_WAIT_TIMEOUT 120 + +/* + * Wait for a "MIGRATION" event. This is what Libvirt uses to track + * migration status changes. + */ +void migration_event_wait(QTestState *s, const char *target) +{ + QDict *response, *data; + const char *status; + bool found; + + do { + response = qtest_qmp_eventwait_ref(s, "MIGRATION"); + data = qdict_get_qdict(response, "data"); + g_assert(data); + status = qdict_get_str(data, "status"); + found = (strcmp(status, target) == 0); + qobject_unref(response); + } 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, + QObject *channels, const char *fmt, ...) +{ + va_list ap; + QDict *args, *err; + + va_start(ap, fmt); + args = qdict_from_vjsonf_nofail(fmt, ap); + va_end(ap); + + g_assert(!qdict_haskey(args, "uri")); + if (uri) { + qdict_put_str(args, "uri", uri); + } + + g_assert(!qdict_haskey(args, "channels")); + if (channels) { + qdict_put_obj(args, "channels", channels); + } + + err = qtest_qmp_assert_failure_ref( + who, "{ 'execute': 'migrate', 'arguments': %p}", args); + + g_assert(qdict_haskey(err, "desc")); + + qobject_unref(err); +} + +/* + * Send QMP command "migrate". + * Arguments are built from @fmt... (formatted like + * qobject_from_jsonf_nofail()) with "uri": @uri spliced in. + */ +void migrate_qmp(QTestState *who, QTestState *to, const char *uri, + QObject *channels, const char *fmt, ...) +{ + va_list ap; + QDict *args; + g_autofree char *connect_uri = NULL; + + va_start(ap, fmt); + args = qdict_from_vjsonf_nofail(fmt, ap); + va_end(ap); + + g_assert(!qdict_haskey(args, "uri")); + if (uri) { + qdict_put_str(args, "uri", uri); + } else if (!channels) { + connect_uri = migrate_get_connect_uri(to); + qdict_put_str(args, "uri", connect_uri); + } + + g_assert(!qdict_haskey(args, "channels")); + if (channels) { + QList *channel_list = qobject_to(QList, channels); + migrate_set_ports(to, channel_list); + qdict_put_obj(args, "channels", channels); + } + + qtest_qmp_assert_success(who, + "{ 'execute': 'migrate', 'arguments': %p}", args); +} + +void migrate_set_capability(QTestState *who, const char *capability, + bool value) +{ + qtest_qmp_assert_success(who, + "{ 'execute': 'migrate-set-capabilities'," + "'arguments': { " + "'capabilities': [ { " + "'capability': %s, 'state': %i } ] } }", + capability, value); +} + +void migrate_incoming_qmp(QTestState *to, const char *uri, QObject *channels, + const char *fmt, ...) +{ + va_list ap; + QDict *args, *rsp; + + va_start(ap, fmt); + args = qdict_from_vjsonf_nofail(fmt, ap); + va_end(ap); + + g_assert(!qdict_haskey(args, "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); + + rsp = qtest_qmp(to, "{ 'execute': 'migrate-incoming', 'arguments': %p}", + args); + + if (!qdict_haskey(rsp, "return")) { + g_autoptr(GString) s = qobject_to_json_pretty(QOBJECT(rsp), true); + g_test_message("%s", s->str); + } + + g_assert(qdict_haskey(rsp, "return")); + qobject_unref(rsp); + + migration_event_wait(to, "setup"); +} + +static bool check_migration_status(QTestState *who, const char *goal, + const char **ungoals) +{ + bool ready; + char *current_status; + const char **ungoal; + + current_status = migrate_query_status(who); + ready = strcmp(current_status, goal) == 0; + if (!ungoals) { + g_assert_cmpstr(current_status, !=, "failed"); + /* + * If looking for a state other than completed, + * completion of migration would cause the test to + * hang. + */ + if (strcmp(goal, "completed") != 0) { + g_assert_cmpstr(current_status, !=, "completed"); + } + } else { + for (ungoal = ungoals; *ungoal; ungoal++) { + g_assert_cmpstr(current_status, !=, *ungoal); + } + } + g_free(current_status); + return ready; +} + +void wait_for_migration_status(QTestState *who, + const char *goal, const char **ungoals) +{ + g_test_timer_start(); + while (!check_migration_status(who, goal, ungoals)) { + usleep(1000); + + g_assert(g_test_timer_elapsed() < MIGRATION_STATUS_WAIT_TIMEOUT); + } +} + +void wait_for_migration_complete(QTestState *who) +{ + wait_for_migration_status(who, "completed", NULL); +} + +void wait_for_migration_fail(QTestState *from, bool allow_active) +{ + g_test_timer_start(); + QDict *rsp_return; + char *status; + bool failed; + + do { + status = migrate_query_status(from); + bool result = !strcmp(status, "setup") || !strcmp(status, "failed") || + (allow_active && !strcmp(status, "active")); + if (!result) { + fprintf(stderr, "%s: unexpected status status=%s allow_active=%d\n", + __func__, status, allow_active); + } + g_assert(result); + failed = !strcmp(status, "failed"); + g_free(status); + + g_assert(g_test_timer_elapsed() < MIGRATION_STATUS_WAIT_TIMEOUT); + } while (!failed); + + /* Is the machine currently running? */ + rsp_return = qtest_qmp_assert_success_ref(from, + "{ 'execute': 'query-status' }"); + g_assert(qdict_haskey(rsp_return, "running")); + g_assert(qdict_get_bool(rsp_return, "running")); + qobject_unref(rsp_return); +} + +void wait_for_stop(QTestState *who, QTestMigrationState *state) +{ + if (!state->stop_seen) { + qtest_qmp_eventwait(who, "STOP"); + } +} + +void wait_for_resume(QTestState *who, QTestMigrationState *state) +{ + if (!state->resume_seen) { + qtest_qmp_eventwait(who, "RESUME"); + } +} + +void wait_for_suspend(QTestState *who, QTestMigrationState *state) +{ + if (state->suspend_me && !state->suspend_seen) { + qtest_qmp_eventwait(who, "SUSPEND"); + } +} + +/* + * Note: caller is responsible to free the returned object via + * qobject_unref() after use + */ +QDict *migrate_query(QTestState *who) +{ + return qtest_qmp_assert_success_ref(who, "{ 'execute': 'query-migrate' }"); +} + +QDict *migrate_query_not_failed(QTestState *who) +{ + const char *status; + QDict *rsp = migrate_query(who); + status = qdict_get_str(rsp, "status"); + if (g_str_equal(status, "failed")) { + g_printerr("query-migrate shows failed migration: %s\n", + qdict_get_str(rsp, "error-desc")); + } + g_assert(!g_str_equal(status, "failed")); + return rsp; +} + +/* + * Note: caller is responsible to free the returned object via + * g_free() after use + */ +gchar *migrate_query_status(QTestState *who) +{ + QDict *rsp_return = migrate_query(who); + gchar *status = g_strdup(qdict_get_str(rsp_return, "status")); + + g_assert(status); + qobject_unref(rsp_return); + + return status; +} + +int64_t read_ram_property_int(QTestState *who, const char *property) +{ + QDict *rsp_return, *rsp_ram; + int64_t result; + + rsp_return = migrate_query_not_failed(who); + if (!qdict_haskey(rsp_return, "ram")) { + /* Still in setup */ + result = 0; + } else { + rsp_ram = qdict_get_qdict(rsp_return, "ram"); + result = qdict_get_try_int(rsp_ram, property, 0); + } + qobject_unref(rsp_return); + return result; +} + +int64_t read_migrate_property_int(QTestState *who, const char *property) +{ + QDict *rsp_return; + int64_t result; + + rsp_return = migrate_query_not_failed(who); + result = qdict_get_try_int(rsp_return, property, 0); + qobject_unref(rsp_return); + return result; +} + +uint64_t get_migration_pass(QTestState *who) +{ + return read_ram_property_int(who, "dirty-sync-count"); +} + +void read_blocktime(QTestState *who) +{ + QDict *rsp_return; + + rsp_return = migrate_query_not_failed(who); + g_assert(qdict_haskey(rsp_return, "postcopy-blocktime")); + qobject_unref(rsp_return); +} + +/* + * Wait for two changes in the migration pass count, but bail if we stop. + */ +void wait_for_migration_pass(QTestState *who, QTestMigrationState *src_state) +{ + uint64_t pass, prev_pass = 0, changes = 0; + + while (changes < 2 && !src_state->stop_seen && !src_state->suspend_seen) { + usleep(1000); + pass = get_migration_pass(who); + changes += (pass != prev_pass); + prev_pass = pass; + } +} + +static long long migrate_get_parameter_int(QTestState *who, + const char *parameter) +{ + QDict *rsp; + long long result; + + rsp = qtest_qmp_assert_success_ref( + who, "{ 'execute': 'query-migrate-parameters' }"); + result = qdict_get_int(rsp, parameter); + qobject_unref(rsp); + return result; +} + +static void migrate_check_parameter_int(QTestState *who, const char *parameter, + long long value) +{ + long long result; + + result = migrate_get_parameter_int(who, parameter); + g_assert_cmpint(result, ==, value); +} + +void migrate_set_parameter_int(QTestState *who, const char *parameter, + long long value) +{ + qtest_qmp_assert_success(who, + "{ 'execute': 'migrate-set-parameters'," + "'arguments': { %s: %lld } }", + parameter, value); + migrate_check_parameter_int(who, parameter, value); +} + +static char *migrate_get_parameter_str(QTestState *who, const char *parameter) +{ + QDict *rsp; + char *result; + + rsp = qtest_qmp_assert_success_ref( + who, "{ 'execute': 'query-migrate-parameters' }"); + result = g_strdup(qdict_get_str(rsp, parameter)); + qobject_unref(rsp); + return result; +} + +static void migrate_check_parameter_str(QTestState *who, const char *parameter, + const char *value) +{ + g_autofree char *result = migrate_get_parameter_str(who, parameter); + g_assert_cmpstr(result, ==, value); +} + +void migrate_set_parameter_str(QTestState *who, const char *parameter, + const char *value) +{ + qtest_qmp_assert_success(who, + "{ 'execute': 'migrate-set-parameters'," + "'arguments': { %s: %s } }", + parameter, value); + migrate_check_parameter_str(who, parameter, value); +} + +static long long migrate_get_parameter_bool(QTestState *who, + const char *parameter) +{ + QDict *rsp; + int result; + + rsp = qtest_qmp_assert_success_ref( + who, "{ 'execute': 'query-migrate-parameters' }"); + result = qdict_get_bool(rsp, parameter); + qobject_unref(rsp); + return !!result; +} + +static void migrate_check_parameter_bool(QTestState *who, const char *parameter, + int value) +{ + int result; + + result = migrate_get_parameter_bool(who, parameter); + g_assert_cmpint(result, ==, value); +} + +void migrate_set_parameter_bool(QTestState *who, const char *parameter, + int value) +{ + qtest_qmp_assert_success(who, + "{ 'execute': 'migrate-set-parameters'," + "'arguments': { %s: %i } }", + parameter, value); + migrate_check_parameter_bool(who, parameter, value); +} + +void migrate_ensure_non_converge(QTestState *who) +{ + /* Can't converge with 1ms downtime + 3 mbs bandwidth limit */ + migrate_set_parameter_int(who, "max-bandwidth", 3 * 1000 * 1000); + migrate_set_parameter_int(who, "downtime-limit", 1); +} + +void migrate_ensure_converge(QTestState *who) +{ + /* Should converge with 30s downtime + 1 gbs bandwidth limit */ + migrate_set_parameter_int(who, "max-bandwidth", 1 * 1000 * 1000 * 1000); + migrate_set_parameter_int(who, "downtime-limit", 30 * 1000); +} + +void migrate_pause(QTestState *who) +{ + qtest_qmp_assert_success(who, "{ 'execute': 'migrate-pause' }"); +} + +void migrate_continue(QTestState *who, const char *state) +{ + qtest_qmp_assert_success(who, + "{ 'execute': 'migrate-continue'," + " 'arguments': { 'state': %s } }", + state); +} + +void migrate_recover(QTestState *who, const char *uri) +{ + qtest_qmp_assert_success(who, + "{ 'exec-oob': 'migrate-recover', " + " 'id': 'recover-cmd', " + " 'arguments': { 'uri': %s } }", + uri); +} + +void migrate_cancel(QTestState *who) +{ + qtest_qmp_assert_success(who, "{ 'execute': 'migrate_cancel' }"); +} + +void migrate_postcopy_start(QTestState *from, QTestState *to, + QTestMigrationState *src_state) +{ + qtest_qmp_assert_success(from, "{ 'execute': 'migrate-start-postcopy' }"); + + wait_for_stop(from, src_state); + qtest_qmp_eventwait(to, "RESUME"); +} diff --git a/tests/qtest/migration/migration-qmp.h b/tests/qtest/migration/migration-qmp.h new file mode 100644 index 0000000..faa8181 --- /dev/null +++ b/tests/qtest/migration/migration-qmp.h @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +#ifndef MIGRATION_QMP_H +#define MIGRATION_QMP_H + +#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, + QObject *channels, const char *fmt, ...); + +G_GNUC_PRINTF(5, 6) +void migrate_qmp(QTestState *who, QTestState *to, const char *uri, + QObject *channels, const char *fmt, ...); + +G_GNUC_PRINTF(4, 5) +void migrate_incoming_qmp(QTestState *who, const char *uri, + QObject *channels, const char *fmt, ...); + +void migration_event_wait(QTestState *s, const char *target); +void migrate_set_capability(QTestState *who, const char *capability, + bool value); +int64_t read_ram_property_int(QTestState *who, const char *property); +void migrate_set_parameter_int(QTestState *who, const char *parameter, + long long value); +void wait_for_stop(QTestState *who, QTestMigrationState *state); +void wait_for_resume(QTestState *who, QTestMigrationState *state); +void wait_for_suspend(QTestState *who, QTestMigrationState *state); +gchar *migrate_query_status(QTestState *who); +int64_t read_migrate_property_int(QTestState *who, const char *property); +uint64_t get_migration_pass(QTestState *who); +void read_blocktime(QTestState *who); +void wait_for_migration_pass(QTestState *who, QTestMigrationState *src_state); +void migrate_set_parameter_str(QTestState *who, const char *parameter, + const char *value); +void migrate_set_parameter_bool(QTestState *who, const char *parameter, + int value); +void migrate_ensure_non_converge(QTestState *who); +void migrate_ensure_converge(QTestState *who); +void migrate_pause(QTestState *who); +void migrate_continue(QTestState *who, const char *state); +void migrate_recover(QTestState *who, const char *uri); +void migrate_cancel(QTestState *who); +void migrate_postcopy_start(QTestState *from, QTestState *to, + QTestMigrationState *src_state); + +#endif /* MIGRATION_QMP_H */ diff --git a/tests/qtest/migration/migration-util.c b/tests/qtest/migration/migration-util.c new file mode 100644 index 0000000..642cf50 --- /dev/null +++ b/tests/qtest/migration/migration-util.c @@ -0,0 +1,398 @@ +/* + * QTest migration utilities + * + * Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates + * based on the vhost-user-test.c that is: + * Copyright (c) 2014 Virtual Open Systems Sarl. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#include "qemu/osdep.h" +#include "qemu/ctype.h" +#include "qapi/qapi-visit-sockets.h" +#include "qapi/qobject-input-visitor.h" +#include "qapi/error.h" +#include "qobject/qlist.h" +#include "qemu/cutils.h" +#include "qemu/memalign.h" + +#include "migration/bootfile.h" +#include "migration/migration-util.h" + +#if defined(__linux__) +#include <sys/ioctl.h> +#include <sys/syscall.h> +#endif + +/* for uffd_version_check() */ +#if defined(__linux__) && defined(__NR_userfaultfd) && defined(CONFIG_EVENTFD) +#include <sys/eventfd.h> +#include "qemu/userfaultfd.h" +#endif + +/* For dirty ring test; so far only x86_64 is supported */ +#if defined(__linux__) && defined(HOST_X86_64) +#include "linux/kvm.h" +#endif + + +static char *SocketAddress_to_str(SocketAddress *addr) +{ + switch (addr->type) { + case SOCKET_ADDRESS_TYPE_INET: + return g_strdup_printf("tcp:%s:%s", + addr->u.inet.host, + addr->u.inet.port); + case SOCKET_ADDRESS_TYPE_UNIX: + return g_strdup_printf("unix:%s", + addr->u.q_unix.path); + case SOCKET_ADDRESS_TYPE_FD: + return g_strdup_printf("fd:%s", addr->u.fd.str); + case SOCKET_ADDRESS_TYPE_VSOCK: + return g_strdup_printf("vsock:%s:%s", + addr->u.vsock.cid, + addr->u.vsock.port); + default: + return g_strdup("unknown address type"); + } +} + +static QDict *SocketAddress_to_qdict(SocketAddress *addr) +{ + QDict *dict = qdict_new(); + + switch (addr->type) { + case SOCKET_ADDRESS_TYPE_INET: + qdict_put_str(dict, "type", "inet"); + qdict_put_str(dict, "host", addr->u.inet.host); + qdict_put_str(dict, "port", addr->u.inet.port); + break; + case SOCKET_ADDRESS_TYPE_UNIX: + qdict_put_str(dict, "type", "unix"); + qdict_put_str(dict, "path", addr->u.q_unix.path); + break; + case SOCKET_ADDRESS_TYPE_FD: + qdict_put_str(dict, "type", "fd"); + qdict_put_str(dict, "str", addr->u.fd.str); + break; + case SOCKET_ADDRESS_TYPE_VSOCK: + qdict_put_str(dict, "type", "vsock"); + qdict_put_str(dict, "cid", addr->u.vsock.cid); + qdict_put_str(dict, "port", addr->u.vsock.port); + break; + default: + g_assert_not_reached(); + } + + return dict; +} + +static SocketAddressList *migrate_get_socket_address(QTestState *who) +{ + QDict *rsp; + SocketAddressList *addrs; + Visitor *iv = NULL; + QObject *object; + + rsp = migrate_query(who); + object = qdict_get(rsp, "socket-address"); + + iv = qobject_input_visitor_new(object); + visit_type_SocketAddressList(iv, NULL, &addrs, &error_abort); + visit_free(iv); + + qobject_unref(rsp); + return addrs; +} + +char *migrate_get_connect_uri(QTestState *who) +{ + SocketAddressList *addrs; + char *connect_uri; + + addrs = migrate_get_socket_address(who); + connect_uri = SocketAddress_to_str(addrs->value); + + qapi_free_SocketAddressList(addrs); + return connect_uri; +} + +static QDict * +migrate_get_connect_qdict(QTestState *who) +{ + SocketAddressList *addrs; + QDict *connect_qdict; + + addrs = migrate_get_socket_address(who); + connect_qdict = SocketAddress_to_qdict(addrs->value); + + qapi_free_SocketAddressList(addrs); + return connect_qdict; +} + +void migrate_set_ports(QTestState *to, QList *channel_list) +{ + g_autoptr(QDict) addr = NULL; + QListEntry *entry; + const char *addr_port = NULL; + + 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") || + 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); + } + } +} + +bool migrate_watch_for_events(QTestState *who, const char *name, + QDict *event, void *opaque) +{ + QTestMigrationState *state = opaque; + + if (g_str_equal(name, "STOP")) { + state->stop_seen = true; + return true; + } else if (g_str_equal(name, "SUSPEND")) { + state->suspend_seen = true; + return true; + } else if (g_str_equal(name, "RESUME")) { + state->resume_seen = true; + return true; + } + + return false; +} + +char *find_common_machine_version(const char *mtype, const char *var1, + const char *var2) +{ + g_autofree char *type1 = qtest_resolve_machine_alias(var1, mtype); + g_autofree char *type2 = qtest_resolve_machine_alias(var2, mtype); + + g_assert(type1 && type2); + + if (g_str_equal(type1, type2)) { + /* either can be used */ + return g_strdup(type1); + } + + if (qtest_has_machine_with_env(var2, type1)) { + return g_strdup(type1); + } + + if (qtest_has_machine_with_env(var1, type2)) { + return g_strdup(type2); + } + + g_test_message("No common machine version for machine type '%s' between " + "binaries %s and %s", mtype, getenv(var1), getenv(var2)); + g_assert_not_reached(); +} + +char *resolve_machine_version(const char *alias, const char *var1, + const char *var2) +{ + const char *mname = g_getenv("QTEST_QEMU_MACHINE_TYPE"); + g_autofree char *machine_name = NULL; + + if (mname) { + const char *dash = strrchr(mname, '-'); + const char *dot = strrchr(mname, '.'); + + machine_name = g_strdup(mname); + + if (dash && dot) { + assert(qtest_has_machine(machine_name)); + return g_steal_pointer(&machine_name); + } + /* else: probably an alias, let it be resolved below */ + } else { + /* use the hardcoded alias */ + machine_name = g_strdup(alias); + } + + return find_common_machine_version(machine_name, var1, var2); +} + +typedef struct { + char *name; + void (*func)(void); + void (*func_full)(void *); +} MigrationTest; + +static void migration_test_destroy(gpointer data) +{ + MigrationTest *test = (MigrationTest *)data; + + g_free(test->name); + g_free(test); +} + +static void migration_test_wrapper(const void *data) +{ + MigrationTest *test = (MigrationTest *)data; + + g_test_message("Running /%s%s", qtest_get_arch(), test->name); + test->func(); +} + +void migration_test_add(const char *path, void (*fn)(void)) +{ + MigrationTest *test = g_new0(MigrationTest, 1); + + test->func = fn; + test->name = g_strdup(path); + + qtest_add_data_func_full(path, test, migration_test_wrapper, + migration_test_destroy); +} + +static void migration_test_wrapper_full(const void *data) +{ + MigrationTest *test = (MigrationTest *)data; + + g_test_message("Running /%s%s", qtest_get_arch(), test->name); + test->func_full(test->name); +} + +void migration_test_add_suffix(const char *path, const char *suffix, + void (*fn)(void *)) +{ + MigrationTest *test = g_new0(MigrationTest, 1); + + g_assert(g_str_has_suffix(path, "/")); + g_assert(!g_str_has_prefix(suffix, "/")); + + test->func_full = fn; + test->name = g_strconcat(path, suffix, NULL); + + qtest_add_data_func_full(test->name, test, migration_test_wrapper_full, + migration_test_destroy); +} + +#ifdef O_DIRECT +/* + * Probe for O_DIRECT support on the filesystem. Since this is used + * for tests, be conservative, if anything fails, assume it's + * unsupported. + */ +bool probe_o_direct_support(const char *tmpfs) +{ + g_autofree char *filename = g_strdup_printf("%s/probe-o-direct", tmpfs); + int fd, flags = O_CREAT | O_RDWR | O_TRUNC | O_DIRECT; + void *buf; + ssize_t ret, len; + uint64_t offset; + + fd = open(filename, flags, 0660); + if (fd < 0) { + unlink(filename); + return false; + } + + /* + * Using 1MB alignment as conservative choice to satisfy any + * plausible architecture default page size, and/or filesystem + * alignment restrictions. + */ + len = 0x100000; + offset = 0x100000; + + buf = qemu_try_memalign(len, len); + g_assert(buf); + memset(buf, 0, len); + + ret = pwrite(fd, buf, len, offset); + unlink(filename); + g_free(buf); + + if (ret < 0) { + return false; + } + + return true; +} +#endif + +#if defined(__linux__) && defined(__NR_userfaultfd) && defined(CONFIG_EVENTFD) +bool ufd_version_check(bool *uffd_feature_thread_id) +{ + struct uffdio_api api_struct; + uint64_t ioctl_mask; + + int ufd = uffd_open(O_CLOEXEC); + + if (ufd == -1) { + g_test_message("Skipping test: userfaultfd not available"); + return false; + } + + api_struct.api = UFFD_API; + api_struct.features = 0; + if (ioctl(ufd, UFFDIO_API, &api_struct)) { + g_test_message("Skipping test: UFFDIO_API failed"); + return false; + } + + if (uffd_feature_thread_id) { + *uffd_feature_thread_id = api_struct.features & UFFD_FEATURE_THREAD_ID; + } + + ioctl_mask = (1ULL << _UFFDIO_REGISTER | + 1ULL << _UFFDIO_UNREGISTER); + if ((api_struct.ioctls & ioctl_mask) != ioctl_mask) { + g_test_message("Skipping test: Missing userfault feature"); + return false; + } + + return true; +} +#else +bool ufd_version_check(bool *uffd_feature_thread_id) +{ + g_test_message("Skipping test: Userfault not available (builtdtime)"); + return false; +} +#endif + +bool kvm_dirty_ring_supported(void) +{ +#if defined(__linux__) && defined(HOST_X86_64) + int ret, kvm_fd = open("/dev/kvm", O_RDONLY); + + if (kvm_fd < 0) { + return false; + } + + ret = ioctl(kvm_fd, KVM_CHECK_EXTENSION, KVM_CAP_DIRTY_LOG_RING); + close(kvm_fd); + + /* We test with 4096 slots */ + if (ret < 4096) { + return false; + } + + return true; +#else + return false; +#endif +} diff --git a/tests/qtest/migration-helpers.h b/tests/qtest/migration/migration-util.h index 72dba36..44815e9 100644 --- a/tests/qtest/migration-helpers.h +++ b/tests/qtest/migration/migration-util.h @@ -10,8 +10,8 @@ * */ -#ifndef MIGRATION_HELPERS_H -#define MIGRATION_HELPERS_H +#ifndef MIGRATION_UTIL_H +#define MIGRATION_UTIL_H #include "libqtest.h" @@ -25,21 +25,6 @@ typedef struct QTestMigrationState { bool migrate_watch_for_events(QTestState *who, const char *name, QDict *event, void *opaque); -G_GNUC_PRINTF(5, 6) -void migrate_qmp(QTestState *who, QTestState *to, const char *uri, - const char *channels, const char *fmt, ...); - -G_GNUC_PRINTF(3, 4) -void migrate_incoming_qmp(QTestState *who, const char *uri, - const char *fmt, ...); - -G_GNUC_PRINTF(4, 5) -void migrate_qmp_fail(QTestState *who, const char *uri, - const char *channels, const char *fmt, ...); - -void migrate_set_capability(QTestState *who, const char *capability, - bool value); - QDict *migrate_query(QTestState *who); QDict *migrate_query_not_failed(QTestState *who); @@ -62,7 +47,13 @@ static inline bool probe_o_direct_support(const char *tmpfs) return false; } #endif + +bool ufd_version_check(bool *uffd_feature_thread_id); +bool kvm_dirty_ring_supported(void); void migration_test_add(const char *path, void (*fn)(void)); -void migration_event_wait(QTestState *s, const char *target); +void migration_test_add_suffix(const char *path, const char *suffix, + void (*fn)(void *)); +char *migrate_get_connect_uri(QTestState *who); +void migrate_set_ports(QTestState *to, QList *channel_list); -#endif /* MIGRATION_HELPERS_H */ +#endif /* MIGRATION_UTIL_H */ diff --git a/tests/qtest/migration/misc-tests.c b/tests/qtest/migration/misc-tests.c new file mode 100644 index 0000000..5499525 --- /dev/null +++ b/tests/qtest/migration/misc-tests.c @@ -0,0 +1,297 @@ +/* + * QTest testcases for migration + * + * Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates + * based on the vhost-user-test.c that is: + * Copyright (c) 2014 Virtual Open Systems Sarl. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qobject/qjson.h" +#include "libqtest.h" +#include "migration/framework.h" +#include "migration/migration-qmp.h" +#include "migration/migration-util.h" + +#define ANALYZE_SCRIPT "scripts/analyze-migration.py" + +static char *tmpfs; + +static void test_baddest(void) +{ + MigrateStart args = { + .hide_stderr = true + }; + QTestState *from, *to; + + if (migrate_start(&from, &to, "tcp:127.0.0.1:0", &args)) { + return; + } + migrate_qmp(from, to, "tcp:127.0.0.1:0", NULL, "{}"); + wait_for_migration_fail(from, false); + migrate_end(from, to, false); +} + +#ifndef _WIN32 +static void test_analyze_script(void) +{ + MigrateStart args = { + .opts_source = "-uuid 11111111-1111-1111-1111-111111111111", + }; + QTestState *from, *to; + g_autofree char *uri = NULL; + g_autofree char *file = NULL; + int pid, wstatus; + const char *python = g_getenv("PYTHON"); + + if (!python) { + g_test_skip("PYTHON variable not set"); + return; + } + + /* dummy url */ + if (migrate_start(&from, &to, "tcp:127.0.0.1:0", &args)) { + return; + } + + /* + * Setting these two capabilities causes the "configuration" + * vmstate to include subsections for them. The script needs to + * parse those subsections properly. + */ + migrate_set_capability(from, "validate-uuid", true); + migrate_set_capability(from, "x-ignore-shared", true); + + file = g_strdup_printf("%s/migfile", tmpfs); + uri = g_strdup_printf("exec:cat > %s", file); + + migrate_ensure_converge(from); + migrate_qmp(from, to, uri, NULL, "{}"); + wait_for_migration_complete(from); + + pid = fork(); + if (!pid) { + close(1); + open("/dev/null", O_WRONLY); + execl(python, python, ANALYZE_SCRIPT, "-f", file, NULL); + g_assert_not_reached(); + } + + g_assert(waitpid(pid, &wstatus, 0) == pid); + if (!WIFEXITED(wstatus) || WEXITSTATUS(wstatus) != 0) { + g_test_message("Failed to analyze the migration stream"); + g_test_fail(); + } + migrate_end(from, to, false); + unlink(file); +} +#endif + +static void test_ignore_shared(void) +{ + g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); + QTestState *from, *to; + MigrateStart args = { + .use_shmem = true, + .caps[MIGRATION_CAPABILITY_X_IGNORE_SHARED] = true, + }; + + if (migrate_start(&from, &to, uri, &args)) { + return; + } + + migrate_ensure_non_converge(from); + migrate_prepare_for_dirty_mem(from); + + /* Wait for the first serial output from the source */ + wait_for_serial("src_serial"); + + migrate_qmp(from, to, uri, NULL, "{}"); + + migrate_wait_for_dirty_mem(from, to); + + wait_for_stop(from, get_src()); + + qtest_qmp_eventwait(to, "RESUME"); + + wait_for_serial("dest_serial"); + wait_for_migration_complete(from); + + /* Check whether shared RAM has been really skipped */ + g_assert_cmpint( + read_ram_property_int(from, "transferred"), <, 4 * 1024 * 1024); + + migrate_end(from, to, true); +} + +static void do_test_validate_uuid(MigrateStart *args, bool should_fail) +{ + g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); + QTestState *from, *to; + + if (migrate_start(&from, &to, uri, args)) { + return; + } + + /* + * UUID validation is at the begin of migration. So, the main process of + * migration is not interesting for us here. Thus, set huge downtime for + * very fast migration. + */ + migrate_set_parameter_int(from, "downtime-limit", 1000000); + migrate_set_capability(from, "validate-uuid", true); + + /* Wait for the first serial output from the source */ + wait_for_serial("src_serial"); + + migrate_qmp(from, to, uri, NULL, "{}"); + + if (should_fail) { + qtest_set_expected_status(to, EXIT_FAILURE); + wait_for_migration_fail(from, true); + } else { + wait_for_migration_complete(from); + } + + migrate_end(from, to, false); +} + +static void test_validate_uuid(void) +{ + MigrateStart args = { + .opts_source = "-uuid 11111111-1111-1111-1111-111111111111", + .opts_target = "-uuid 11111111-1111-1111-1111-111111111111", + }; + + do_test_validate_uuid(&args, false); +} + +static void test_validate_uuid_error(void) +{ + MigrateStart args = { + .opts_source = "-uuid 11111111-1111-1111-1111-111111111111", + .opts_target = "-uuid 22222222-2222-2222-2222-222222222222", + .hide_stderr = true, + }; + + do_test_validate_uuid(&args, true); +} + +static void test_validate_uuid_src_not_set(void) +{ + MigrateStart args = { + .opts_target = "-uuid 22222222-2222-2222-2222-222222222222", + .hide_stderr = true, + }; + + do_test_validate_uuid(&args, false); +} + +static void test_validate_uuid_dst_not_set(void) +{ + MigrateStart args = { + .opts_source = "-uuid 11111111-1111-1111-1111-111111111111", + .hide_stderr = true, + }; + + do_test_validate_uuid(&args, false); +} + +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; + } + + /* Wait for the first serial output from the source */ + wait_for_serial("src_serial"); + + /* + * 'uri' and 'channels' validation is checked even before the migration + * starts. + */ + 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); +} + +static void test_validate_uri_channels_both_set(void) +{ + MigrateCommon args = { + .start = { + .hide_stderr = true, + }, + .listen_uri = "defer", + .connect_uri = "tcp:127.0.0.1:0", + .connect_channels = ("[ { ""'channel-type': 'main'," + " 'addr': { 'transport': 'socket'," + " 'type': 'inet'," + " 'host': '127.0.0.1'," + " 'port': '0' } } ]"), + }; + + do_test_validate_uri_channel(&args); +} + +static void test_validate_uri_channels_none_set(void) +{ + MigrateCommon args = { + .start = { + .hide_stderr = true, + }, + .listen_uri = "defer", + }; + + do_test_validate_uri_channel(&args); +} + +static void migration_test_add_misc_smoke(MigrationTestEnv *env) +{ +#ifndef _WIN32 + migration_test_add("/migration/analyze-script", test_analyze_script); +#endif +} + +void migration_test_add_misc(MigrationTestEnv *env) +{ + tmpfs = env->tmpfs; + + migration_test_add_misc_smoke(env); + + if (!env->full_set) { + return; + } + + migration_test_add("/migration/bad_dest", test_baddest); + + /* + * Our CI system has problems with shared memory. + * Don't run this test until we find a workaround. + */ + if (getenv("QEMU_TEST_FLAKY_TESTS")) { + migration_test_add("/migration/ignore-shared", test_ignore_shared); + } + + migration_test_add("/migration/validate_uuid", test_validate_uuid); + migration_test_add("/migration/validate_uuid_error", + test_validate_uuid_error); + migration_test_add("/migration/validate_uuid_src_not_set", + test_validate_uuid_src_not_set); + migration_test_add("/migration/validate_uuid_dst_not_set", + test_validate_uuid_dst_not_set); + migration_test_add("/migration/validate_uri/channels/both_set", + test_validate_uri_channels_both_set); + migration_test_add("/migration/validate_uri/channels/none_set", + test_validate_uri_channels_none_set); +} diff --git a/tests/qtest/migration/postcopy-tests.c b/tests/qtest/migration/postcopy-tests.c new file mode 100644 index 0000000..3773525 --- /dev/null +++ b/tests/qtest/migration/postcopy-tests.c @@ -0,0 +1,149 @@ +/* + * QTest testcases for postcopy migration + * + * Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates + * based on the vhost-user-test.c that is: + * Copyright (c) 2014 Virtual Open Systems Sarl. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "migration/framework.h" +#include "migration/migration-util.h" +#include "qobject/qlist.h" +#include "qemu/module.h" +#include "qemu/option.h" +#include "qemu/range.h" +#include "qemu/sockets.h" + +static void test_postcopy(void) +{ + MigrateCommon args = { }; + + test_postcopy_common(&args); +} + +static void test_postcopy_suspend(void) +{ + MigrateCommon args = { + .start.suspend_me = true, + }; + + test_postcopy_common(&args); +} + +static void test_postcopy_preempt(void) +{ + MigrateCommon args = { + .start = { + .caps[MIGRATION_CAPABILITY_POSTCOPY_PREEMPT] = true, + }, + }; + + test_postcopy_common(&args); +} + +static void test_postcopy_recovery(void) +{ + MigrateCommon args = { }; + + test_postcopy_recovery_common(&args); +} + +static void test_postcopy_recovery_fail_handshake(void) +{ + MigrateCommon args = { + .postcopy_recovery_fail_stage = POSTCOPY_FAIL_RECOVERY, + }; + + test_postcopy_recovery_common(&args); +} + +static void test_postcopy_recovery_fail_reconnect(void) +{ + MigrateCommon args = { + .postcopy_recovery_fail_stage = POSTCOPY_FAIL_CHANNEL_ESTABLISH, + }; + + test_postcopy_recovery_common(&args); +} + +static void test_postcopy_preempt_recovery(void) +{ + MigrateCommon args = { + .start = { + .caps[MIGRATION_CAPABILITY_POSTCOPY_PREEMPT] = true, + }, + }; + + test_postcopy_recovery_common(&args); +} + +static void migration_test_add_postcopy_smoke(MigrationTestEnv *env) +{ + if (env->has_uffd) { + migration_test_add("/migration/postcopy/plain", test_postcopy); + migration_test_add("/migration/postcopy/recovery/plain", + test_postcopy_recovery); + migration_test_add("/migration/postcopy/preempt/plain", + test_postcopy_preempt); + } +} + +static void test_multifd_postcopy(void) +{ + MigrateCommon args = { + .start = { + .caps[MIGRATION_CAPABILITY_MULTIFD] = true, + }, + }; + + test_postcopy_common(&args); +} + +static void test_multifd_postcopy_preempt(void) +{ + MigrateCommon args = { + .start = { + .caps[MIGRATION_CAPABILITY_MULTIFD] = true, + .caps[MIGRATION_CAPABILITY_POSTCOPY_PREEMPT] = true, + }, + }; + + test_postcopy_common(&args); +} + +void migration_test_add_postcopy(MigrationTestEnv *env) +{ + migration_test_add_postcopy_smoke(env); + + if (!env->full_set) { + return; + } + + if (env->has_uffd) { + migration_test_add("/migration/postcopy/preempt/recovery/plain", + test_postcopy_preempt_recovery); + + migration_test_add( + "/migration/postcopy/recovery/double-failures/handshake", + test_postcopy_recovery_fail_handshake); + + migration_test_add( + "/migration/postcopy/recovery/double-failures/reconnect", + test_postcopy_recovery_fail_reconnect); + + migration_test_add("/migration/multifd+postcopy/plain", + test_multifd_postcopy); + migration_test_add("/migration/multifd+postcopy/preempt/plain", + test_multifd_postcopy_preempt); + if (env->is_x86) { + migration_test_add("/migration/postcopy/suspend", + test_postcopy_suspend); + } + } +} diff --git a/tests/migration/ppc64/Makefile b/tests/qtest/migration/ppc64/Makefile index a3a2d98..a3a2d98 100644 --- a/tests/migration/ppc64/Makefile +++ b/tests/qtest/migration/ppc64/Makefile diff --git a/tests/migration/ppc64/a-b-kernel.S b/tests/qtest/migration/ppc64/a-b-kernel.S index 0613a8d..0613a8d 100644 --- a/tests/migration/ppc64/a-b-kernel.S +++ b/tests/qtest/migration/ppc64/a-b-kernel.S diff --git a/tests/migration/ppc64/a-b-kernel.h b/tests/qtest/migration/ppc64/a-b-kernel.h index 673317e..673317e 100644 --- a/tests/migration/ppc64/a-b-kernel.h +++ b/tests/qtest/migration/ppc64/a-b-kernel.h diff --git a/tests/qtest/migration/precopy-tests.c b/tests/qtest/migration/precopy-tests.c new file mode 100644 index 0000000..bb38292 --- /dev/null +++ b/tests/qtest/migration/precopy-tests.c @@ -0,0 +1,1337 @@ +/* + * QTest testcase for precopy migration + * + * Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates + * based on the vhost-user-test.c that is: + * Copyright (c) 2014 Virtual Open Systems Sarl. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#include "qemu/osdep.h" +#include "chardev/char.h" +#include "crypto/tlscredspsk.h" +#include "libqtest.h" +#include "migration/bootfile.h" +#include "migration/framework.h" +#include "migration/migration-qmp.h" +#include "migration/migration-util.h" +#include "ppc-util.h" +#include "qobject/qlist.h" +#include "qapi-types-migration.h" +#include "qemu/module.h" +#include "qemu/option.h" +#include "qemu/range.h" +#include "qemu/sockets.h" + + +/* + * Dirtylimit stop working if dirty page rate error + * value less than DIRTYLIMIT_TOLERANCE_RANGE + */ +#define DIRTYLIMIT_TOLERANCE_RANGE 25 /* MB/s */ + +static char *tmpfs; + +static void test_precopy_unix_plain(void) +{ + g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); + MigrateCommon args = { + .listen_uri = uri, + .connect_uri = uri, + /* + * The simplest use case of precopy, covering smoke tests of + * get-dirty-log dirty tracking. + */ + .live = true, + }; + + test_precopy_common(&args); +} + +static void test_precopy_unix_suspend_live(void) +{ + g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); + MigrateCommon args = { + .listen_uri = uri, + .connect_uri = uri, + /* + * despite being live, the test is fast because the src + * suspends immediately. + */ + .live = true, + .start.suspend_me = true, + }; + + test_precopy_common(&args); +} + +static void test_precopy_unix_suspend_notlive(void) +{ + g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); + MigrateCommon args = { + .listen_uri = uri, + .connect_uri = uri, + .start.suspend_me = true, + }; + + test_precopy_common(&args); +} + +static void test_precopy_unix_dirty_ring(void) +{ + g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); + MigrateCommon args = { + .start = { + .use_dirty_ring = true, + }, + .listen_uri = uri, + .connect_uri = uri, + /* + * Besides the precopy/unix basic test, cover dirty ring interface + * rather than get-dirty-log. + */ + .live = true, + }; + + test_precopy_common(&args); +} + +#ifdef CONFIG_RDMA + +#include <sys/resource.h> + +/* + * During migration over RDMA, it will try to pin portions of guest memory, + * typically exceeding 100MB in this test, while the remainder will be + * transmitted as compressed zero pages. + * + * REQUIRED_MEMLOCK_SZ indicates the minimal mlock size in the current context. + */ +#define REQUIRED_MEMLOCK_SZ (128 << 20) /* 128MB */ + +/* check 'ulimit -l' */ +static bool mlock_check(void) +{ + uid_t uid; + struct rlimit rlim; + + uid = getuid(); + if (uid == 0) { + return true; + } + + if (getrlimit(RLIMIT_MEMLOCK, &rlim) != 0) { + return false; + } + + return rlim.rlim_cur >= REQUIRED_MEMLOCK_SZ; +} + +#define RDMA_MIGRATION_HELPER "scripts/rdma-migration-helper.sh" +static int new_rdma_link(char *buffer, bool ipv6) +{ + char cmd[256]; + bool verbose = g_getenv("QTEST_LOG"); + + snprintf(cmd, sizeof(cmd), "IP_FAMILY=%s %s detect %s", + ipv6 ? "ipv6" : "ipv4", RDMA_MIGRATION_HELPER, + verbose ? "" : "2>/dev/null"); + + FILE *pipe = popen(cmd, "r"); + if (pipe == NULL) { + perror("Failed to run script"); + return -1; + } + + int idx = 0; + while (fgets(buffer + idx, 128 - idx, pipe) != NULL) { + idx += strlen(buffer); + } + + int status = pclose(pipe); + if (status == -1) { + perror("Error reported by pclose()"); + return -1; + } else if (WIFEXITED(status)) { + return WEXITSTATUS(status); + } + + return -1; +} + +static void __test_precopy_rdma_plain(bool ipv6) +{ + char buffer[128] = {}; + + if (!mlock_check()) { + g_test_skip("'ulimit -l' is too small, require >=128M"); + return; + } + + if (new_rdma_link(buffer, ipv6)) { + g_test_skip("No rdma link available\n" + "# To enable the test:\n" + "# Run \'" RDMA_MIGRATION_HELPER " setup\' with root to " + "setup a new rdma/rxe link and rerun the test\n" + "# Optional: run 'scripts/rdma-migration-helper.sh clean' " + "to revert the 'setup'"); + return; + } + + /* + * TODO: query a free port instead of hard code. + * 29200=('R'+'D'+'M'+'A')*100 + **/ + g_autofree char *uri = g_strdup_printf("rdma:%s:29200", buffer); + + MigrateCommon args = { + .listen_uri = uri, + .connect_uri = uri, + }; + + test_precopy_common(&args); +} + +static void test_precopy_rdma_plain(void) +{ + __test_precopy_rdma_plain(false); +} + +static void test_precopy_rdma_plain_ipv6(void) +{ + __test_precopy_rdma_plain(true); +} +#endif + +static void test_precopy_tcp_plain(void) +{ + MigrateCommon args = { + .listen_uri = "tcp:127.0.0.1:0", + }; + + test_precopy_common(&args); +} + +static void test_precopy_tcp_switchover_ack(void) +{ + MigrateCommon args = { + .listen_uri = "tcp:127.0.0.1:0", + .start = { + .caps[MIGRATION_CAPABILITY_RETURN_PATH] = true, + .caps[MIGRATION_CAPABILITY_SWITCHOVER_ACK] = true, + }, + /* + * Source VM must be running in order to consider the switchover ACK + * when deciding to do switchover or not. + */ + .live = true, + }; + + test_precopy_common(&args); +} + +#ifndef _WIN32 +static void *migrate_hook_start_fd(QTestState *from, + QTestState *to) +{ + int ret; + int pair[2]; + + /* Create two connected sockets for migration */ + ret = qemu_socketpair(PF_LOCAL, SOCK_STREAM, 0, pair); + g_assert_cmpint(ret, ==, 0); + + /* Send the 1st socket to the target */ + qtest_qmp_fds_assert_success(to, &pair[0], 1, + "{ 'execute': 'getfd'," + " 'arguments': { 'fdname': 'fd-mig' }}"); + close(pair[0]); + + /* Start incoming migration from the 1st socket */ + migrate_incoming_qmp(to, "fd:fd-mig", NULL, "{}"); + + /* Send the 2nd socket to the target */ + qtest_qmp_fds_assert_success(from, &pair[1], 1, + "{ 'execute': 'getfd'," + " 'arguments': { 'fdname': 'fd-mig' }}"); + close(pair[1]); + + return NULL; +} + +static void migrate_hook_end_fd(QTestState *from, + QTestState *to, + void *opaque) +{ + QDict *rsp; + const char *error_desc; + + /* Test closing fds */ + /* + * We assume, that QEMU removes named fd from its list, + * so this should fail. + */ + rsp = qtest_qmp(from, + "{ 'execute': 'closefd'," + " 'arguments': { 'fdname': 'fd-mig' }}"); + g_assert_true(qdict_haskey(rsp, "error")); + error_desc = qdict_get_str(qdict_get_qdict(rsp, "error"), "desc"); + g_assert_cmpstr(error_desc, ==, "File descriptor named 'fd-mig' not found"); + qobject_unref(rsp); + + rsp = qtest_qmp(to, + "{ 'execute': 'closefd'," + " 'arguments': { 'fdname': 'fd-mig' }}"); + g_assert_true(qdict_haskey(rsp, "error")); + error_desc = qdict_get_str(qdict_get_qdict(rsp, "error"), "desc"); + g_assert_cmpstr(error_desc, ==, "File descriptor named 'fd-mig' not found"); + qobject_unref(rsp); +} + +static void test_precopy_fd_socket(void) +{ + MigrateCommon args = { + .listen_uri = "defer", + .connect_uri = "fd:fd-mig", + .start_hook = migrate_hook_start_fd, + .end_hook = migrate_hook_end_fd, + }; + test_precopy_common(&args); +} + +static void *migrate_hook_start_precopy_fd_file(QTestState *from, + QTestState *to) +{ + g_autofree char *file = g_strdup_printf("%s/%s", tmpfs, FILE_TEST_FILENAME); + int src_flags = O_CREAT | O_RDWR; + int dst_flags = O_CREAT | O_RDWR; + int fds[2]; + + fds[0] = open(file, src_flags, 0660); + assert(fds[0] != -1); + + fds[1] = open(file, dst_flags, 0660); + assert(fds[1] != -1); + + + qtest_qmp_fds_assert_success(to, &fds[0], 1, + "{ 'execute': 'getfd'," + " 'arguments': { 'fdname': 'fd-mig' }}"); + + qtest_qmp_fds_assert_success(from, &fds[1], 1, + "{ 'execute': 'getfd'," + " 'arguments': { 'fdname': 'fd-mig' }}"); + + close(fds[0]); + close(fds[1]); + + return NULL; +} + +static void test_precopy_fd_file(void) +{ + MigrateCommon args = { + .listen_uri = "defer", + .connect_uri = "fd:fd-mig", + .start_hook = migrate_hook_start_precopy_fd_file, + .end_hook = migrate_hook_end_fd, + }; + test_file_common(&args, true); +} +#endif /* _WIN32 */ + +/* + * The way auto_converge works, we need to do too many passes to + * run this test. Auto_converge logic is only run once every + * three iterations, so: + * + * - 3 iterations without auto_converge enabled + * - 3 iterations with pct = 5 + * - 3 iterations with pct = 30 + * - 3 iterations with pct = 55 + * - 3 iterations with pct = 80 + * - 3 iterations with pct = 95 (max(95, 80 + 25)) + * + * To make things even worse, we need to run the initial stage at + * 3MB/s so we enter autoconverge even when host is (over)loaded. + */ +static void test_auto_converge(void) +{ + g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); + MigrateStart args = {}; + QTestState *from, *to; + int64_t percentage; + + /* + * We want the test to be stable and as fast as possible. + * E.g., with 1Gb/s bandwidth migration may pass without throttling, + * so we need to decrease a bandwidth. + */ + const int64_t init_pct = 5, inc_pct = 25, max_pct = 95; + uint64_t prev_dirty_sync_cnt, dirty_sync_cnt; + int max_try_count, hit = 0; + + if (migrate_start(&from, &to, uri, &args)) { + return; + } + + migrate_set_capability(from, "auto-converge", true); + migrate_set_parameter_int(from, "cpu-throttle-initial", init_pct); + migrate_set_parameter_int(from, "cpu-throttle-increment", inc_pct); + migrate_set_parameter_int(from, "max-cpu-throttle", max_pct); + + /* + * Set the initial parameters so that the migration could not converge + * without throttling. + */ + migrate_ensure_non_converge(from); + + /* To check remaining size after precopy */ + migrate_set_capability(from, "pause-before-switchover", true); + + /* Wait for the first serial output from the source */ + wait_for_serial("src_serial"); + + migrate_qmp(from, to, uri, NULL, "{}"); + + /* Wait for throttling begins */ + percentage = 0; + do { + percentage = read_migrate_property_int(from, "cpu-throttle-percentage"); + if (percentage != 0) { + break; + } + usleep(20); + g_assert_false(get_src()->stop_seen); + } while (true); + /* The first percentage of throttling should be at least init_pct */ + g_assert_cmpint(percentage, >=, init_pct); + + /* + * End the loop when the dirty sync count greater than 1. + */ + while ((dirty_sync_cnt = get_migration_pass(from)) < 2) { + usleep(1000 * 1000); + } + + prev_dirty_sync_cnt = dirty_sync_cnt; + + /* + * The RAMBlock dirty sync count must changes in 5 seconds, here we set + * the timeout to 10 seconds to ensure it changes. + * + * Note that migrate_ensure_non_converge set the max-bandwidth to 3MB/s, + * while the qtest mem is >= 100MB, one iteration takes at least 33s (100/3) + * to complete; this ensures that the RAMBlock dirty sync occurs. + */ + max_try_count = 10; + while (--max_try_count) { + dirty_sync_cnt = get_migration_pass(from); + if (dirty_sync_cnt != prev_dirty_sync_cnt) { + hit = 1; + break; + } + prev_dirty_sync_cnt = dirty_sync_cnt; + sleep(1); + } + g_assert_cmpint(hit, ==, 1); + + /* Now, when we tested that throttling works, let it converge */ + migrate_ensure_converge(from); + + /* + * Wait for pre-switchover status to check last throttle percentage + * and remaining. These values will be zeroed later + */ + wait_for_migration_status(from, "pre-switchover", NULL); + + /* The final percentage of throttling shouldn't be greater than max_pct */ + percentage = read_migrate_property_int(from, "cpu-throttle-percentage"); + g_assert_cmpint(percentage, <=, max_pct); + migrate_continue(from, "pre-switchover"); + + qtest_qmp_eventwait(to, "RESUME"); + + wait_for_serial("dest_serial"); + wait_for_migration_complete(from); + + migrate_end(from, to, true); +} + +static void * +migrate_hook_start_precopy_tcp_multifd(QTestState *from, + QTestState *to) +{ + return migrate_hook_start_precopy_tcp_multifd_common(from, to, "none"); +} + +static void * +migrate_hook_start_precopy_tcp_multifd_zero_page_legacy(QTestState *from, + QTestState *to) +{ + migrate_hook_start_precopy_tcp_multifd_common(from, to, "none"); + migrate_set_parameter_str(from, "zero-page-detection", "legacy"); + return NULL; +} + +static void * +migrate_hook_start_precopy_tcp_multifd_no_zero_page(QTestState *from, + QTestState *to) +{ + migrate_hook_start_precopy_tcp_multifd_common(from, to, "none"); + migrate_set_parameter_str(from, "zero-page-detection", "none"); + return NULL; +} + +static void test_multifd_tcp_uri_none(void) +{ + MigrateCommon args = { + .listen_uri = "defer", + .start_hook = migrate_hook_start_precopy_tcp_multifd, + .start = { + .caps[MIGRATION_CAPABILITY_MULTIFD] = true, + }, + /* + * Multifd is more complicated than most of the features, it + * directly takes guest page buffers when sending, make sure + * everything will work alright even if guest page is changing. + */ + .live = true, + }; + test_precopy_common(&args); +} + +static void test_multifd_tcp_zero_page_legacy(void) +{ + MigrateCommon args = { + .listen_uri = "defer", + .start_hook = migrate_hook_start_precopy_tcp_multifd_zero_page_legacy, + .start = { + .caps[MIGRATION_CAPABILITY_MULTIFD] = true, + }, + /* + * Multifd is more complicated than most of the features, it + * directly takes guest page buffers when sending, make sure + * everything will work alright even if guest page is changing. + */ + .live = true, + }; + test_precopy_common(&args); +} + +static void test_multifd_tcp_no_zero_page(void) +{ + MigrateCommon args = { + .listen_uri = "defer", + .start_hook = migrate_hook_start_precopy_tcp_multifd_no_zero_page, + .start = { + .caps[MIGRATION_CAPABILITY_MULTIFD] = true, + }, + /* + * Multifd is more complicated than most of the features, it + * directly takes guest page buffers when sending, make sure + * everything will work alright even if guest page is changing. + */ + .live = true, + }; + test_precopy_common(&args); +} + +static void test_multifd_tcp_channels_none(void) +{ + MigrateCommon args = { + .listen_uri = "defer", + .start_hook = migrate_hook_start_precopy_tcp_multifd, + .live = true, + .start = { + .caps[MIGRATION_CAPABILITY_MULTIFD] = true, + }, + .connect_channels = ("[ { 'channel-type': 'main'," + " 'addr': { 'transport': 'socket'," + " 'type': 'inet'," + " 'host': '127.0.0.1'," + " 'port': '0' } } ]"), + }; + test_precopy_common(&args); +} + +/* + * This test does: + * source target + * migrate_incoming + * migrate + * migrate_cancel + * launch another target + * migrate + * + * And see that it works + */ +static void test_multifd_tcp_cancel(bool postcopy_ram) +{ + MigrateStart args = { + .hide_stderr = true, + }; + QTestState *from, *to, *to2; + + if (migrate_start(&from, &to, "defer", &args)) { + return; + } + + migrate_ensure_non_converge(from); + migrate_prepare_for_dirty_mem(from); + + if (postcopy_ram) { + migrate_set_capability(from, "postcopy-ram", true); + migrate_set_capability(to, "postcopy-ram", true); + } + + migrate_set_parameter_int(from, "multifd-channels", 16); + migrate_set_parameter_int(to, "multifd-channels", 16); + + migrate_set_capability(from, "multifd", true); + migrate_set_capability(to, "multifd", true); + + /* Start incoming migration from the 1st socket */ + 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"); + + migrate_qmp(from, to, NULL, NULL, "{}"); + + migrate_wait_for_dirty_mem(from, to); + + migrate_cancel(from); + + /* Make sure QEMU process "to" exited */ + qtest_set_expected_status(to, EXIT_FAILURE); + qtest_wait_qemu(to); + qtest_quit(to); + + /* + * Ensure the source QEMU finishes its cancellation process before we + * proceed with the setup of the next migration. The migrate_start() + * function and others might want to interact with the source in a way that + * is not possible while the migration is not canceled properly. For + * example, setting migration capabilities when the migration is still + * running leads to an error. + */ + wait_for_migration_status(from, "cancelled", NULL); + + args = (MigrateStart){ + .only_target = true, + }; + + if (migrate_start(&from, &to2, "defer", &args)) { + return; + } + + if (postcopy_ram) { + migrate_set_capability(to2, "postcopy-ram", true); + } + + migrate_set_parameter_int(to2, "multifd-channels", 16); + + migrate_set_capability(to2, "multifd", true); + + /* Start incoming migration from the 1st socket */ + migrate_incoming_qmp(to2, "tcp:127.0.0.1:0", NULL, "{}"); + + migrate_ensure_non_converge(from); + + migrate_qmp(from, to2, NULL, NULL, "{}"); + + migrate_wait_for_dirty_mem(from, to2); + + migrate_ensure_converge(from); + + wait_for_stop(from, get_src()); + qtest_qmp_eventwait(to2, "RESUME"); + + wait_for_serial("dest_serial"); + wait_for_migration_complete(from); + migrate_end(from, to2, true); +} + +static void test_multifd_precopy_tcp_cancel(void) +{ + test_multifd_tcp_cancel(false); +} + +static void test_multifd_postcopy_tcp_cancel(void) +{ + test_multifd_tcp_cancel(true); +} + +static void test_cancel_src_after_failed(QTestState *from, QTestState *to, + const char *uri, const char *phase) +{ + /* + * No migrate_incoming_qmp() at the start to force source into + * failed state during migrate_qmp(). + */ + + wait_for_serial("src_serial"); + migrate_ensure_converge(from); + + migrate_qmp(from, to, uri, NULL, "{}"); + + migration_event_wait(from, phase); + migrate_cancel(from); + + /* cancelling will not move the migration out of 'failed' */ + + wait_for_migration_status(from, "failed", + (const char * []) { "completed", NULL }); + + /* + * Not waiting for the destination because it never started + * migration. + */ +} + +static void test_cancel_src_after_cancelled(QTestState *from, QTestState *to, + const char *uri, const char *phase) +{ + migrate_incoming_qmp(to, uri, NULL, "{ 'exit-on-error': false }"); + + wait_for_serial("src_serial"); + migrate_ensure_converge(from); + + migrate_qmp(from, to, uri, NULL, "{}"); + + /* To move to cancelled/cancelling */ + migrate_cancel(from); + migration_event_wait(from, phase); + + /* The migrate_cancel under test */ + migrate_cancel(from); + + wait_for_migration_status(from, "cancelled", + (const char * []) { "completed", NULL }); + + wait_for_migration_status(to, "failed", + (const char * []) { "completed", NULL }); +} + +static void test_cancel_src_after_complete(QTestState *from, QTestState *to, + const char *uri, const char *phase) +{ + migrate_incoming_qmp(to, uri, NULL, "{ 'exit-on-error': false }"); + + wait_for_serial("src_serial"); + migrate_ensure_converge(from); + + migrate_qmp(from, to, uri, NULL, "{}"); + + migration_event_wait(from, phase); + migrate_cancel(from); + + /* + * qmp_migrate_cancel() exits early if migration is not running + * anymore, the status will not change to cancelled. + */ + wait_for_migration_complete(from); + wait_for_migration_complete(to); +} + +static void test_cancel_src_after_none(QTestState *from, QTestState *to, + const char *uri, const char *phase) +{ + /* + * Test that cancelling without a migration happening does not + * affect subsequent migrations + */ + migrate_cancel(to); + + wait_for_serial("src_serial"); + migrate_cancel(from); + + migrate_incoming_qmp(to, uri, NULL, "{ 'exit-on-error': false }"); + + migrate_ensure_converge(from); + migrate_qmp(from, to, uri, NULL, "{}"); + + wait_for_migration_complete(from); + wait_for_migration_complete(to); +} + +static void test_cancel_src_pre_switchover(QTestState *from, QTestState *to, + const char *uri, const char *phase) +{ + migrate_set_capability(from, "pause-before-switchover", true); + migrate_set_capability(to, "pause-before-switchover", true); + + migrate_set_capability(from, "multifd", true); + migrate_set_capability(to, "multifd", true); + + migrate_incoming_qmp(to, uri, NULL, "{ 'exit-on-error': false }"); + + wait_for_serial("src_serial"); + migrate_ensure_converge(from); + + migrate_qmp(from, to, uri, NULL, "{}"); + + migration_event_wait(from, phase); + migrate_cancel(from); + migration_event_wait(from, "cancelling"); + + wait_for_migration_status(from, "cancelled", + (const char * []) { "completed", NULL }); + + wait_for_migration_status(to, "failed", + (const char * []) { "completed", NULL }); +} + +static void test_cancel_src_after_status(void *opaque) +{ + const char *test_path = opaque; + g_autofree char *phase = g_path_get_basename(test_path); + g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); + QTestState *from, *to; + MigrateStart args = { + .hide_stderr = true, + }; + + if (migrate_start(&from, &to, "defer", &args)) { + return; + } + + if (g_str_equal(phase, "cancelling") || + g_str_equal(phase, "cancelled")) { + test_cancel_src_after_cancelled(from, to, uri, phase); + + } else if (g_str_equal(phase, "completed")) { + test_cancel_src_after_complete(from, to, uri, phase); + + } else if (g_str_equal(phase, "failed")) { + test_cancel_src_after_failed(from, to, uri, phase); + + } else if (g_str_equal(phase, "none")) { + test_cancel_src_after_none(from, to, uri, phase); + + } else { + /* any state that comes before pre-switchover */ + test_cancel_src_pre_switchover(from, to, uri, phase); + } + + migrate_end(from, to, false); +} + +static void calc_dirty_rate(QTestState *who, uint64_t calc_time) +{ + qtest_qmp_assert_success(who, + "{ 'execute': 'calc-dirty-rate'," + "'arguments': { " + "'calc-time': %" PRIu64 "," + "'mode': 'dirty-ring' }}", + calc_time); +} + +static QDict *query_dirty_rate(QTestState *who) +{ + return qtest_qmp_assert_success_ref(who, + "{ 'execute': 'query-dirty-rate' }"); +} + +static void dirtylimit_set_all(QTestState *who, uint64_t dirtyrate) +{ + qtest_qmp_assert_success(who, + "{ 'execute': 'set-vcpu-dirty-limit'," + "'arguments': { " + "'dirty-rate': %" PRIu64 " } }", + dirtyrate); +} + +static void cancel_vcpu_dirty_limit(QTestState *who) +{ + qtest_qmp_assert_success(who, + "{ 'execute': 'cancel-vcpu-dirty-limit' }"); +} + +static QDict *query_vcpu_dirty_limit(QTestState *who) +{ + QDict *rsp; + + rsp = qtest_qmp(who, "{ 'execute': 'query-vcpu-dirty-limit' }"); + g_assert(!qdict_haskey(rsp, "error")); + g_assert(qdict_haskey(rsp, "return")); + + return rsp; +} + +static bool calc_dirtyrate_ready(QTestState *who) +{ + QDict *rsp_return; + const char *status; + bool ready; + + rsp_return = query_dirty_rate(who); + g_assert(rsp_return); + + status = qdict_get_str(rsp_return, "status"); + g_assert(status); + ready = g_strcmp0(status, "measuring"); + qobject_unref(rsp_return); + + return ready; +} + +static void wait_for_calc_dirtyrate_complete(QTestState *who, + int64_t time_s) +{ + int max_try_count = 10000; + usleep(time_s * 1000000); + + while (!calc_dirtyrate_ready(who) && max_try_count--) { + usleep(1000); + } + + /* + * Set the timeout with 10 s(max_try_count * 1000us), + * if dirtyrate measurement not complete, fail test. + */ + g_assert_cmpint(max_try_count, !=, 0); +} + +static int64_t get_dirty_rate(QTestState *who) +{ + QDict *rsp_return; + const char *status; + QList *rates; + const QListEntry *entry; + QDict *rate; + int64_t dirtyrate; + + rsp_return = query_dirty_rate(who); + g_assert(rsp_return); + + status = qdict_get_str(rsp_return, "status"); + g_assert(status); + g_assert_cmpstr(status, ==, "measured"); + + rates = qdict_get_qlist(rsp_return, "vcpu-dirty-rate"); + g_assert(rates && !qlist_empty(rates)); + + entry = qlist_first(rates); + g_assert(entry); + + rate = qobject_to(QDict, qlist_entry_obj(entry)); + g_assert(rate); + + dirtyrate = qdict_get_try_int(rate, "dirty-rate", -1); + + qobject_unref(rsp_return); + return dirtyrate; +} + +static int64_t get_limit_rate(QTestState *who) +{ + QDict *rsp_return; + QList *rates; + const QListEntry *entry; + QDict *rate; + int64_t dirtyrate; + + rsp_return = query_vcpu_dirty_limit(who); + g_assert(rsp_return); + + rates = qdict_get_qlist(rsp_return, "return"); + g_assert(rates && !qlist_empty(rates)); + + entry = qlist_first(rates); + g_assert(entry); + + rate = qobject_to(QDict, qlist_entry_obj(entry)); + g_assert(rate); + + dirtyrate = qdict_get_try_int(rate, "limit-rate", -1); + + qobject_unref(rsp_return); + return dirtyrate; +} + +static QTestState *dirtylimit_start_vm(void) +{ + QTestState *vm = NULL; + g_autofree gchar *cmd = NULL; + const char *bootpath; + + bootpath = bootfile_create(qtest_get_arch(), tmpfs, false); + cmd = g_strdup_printf("-accel kvm,dirty-ring-size=4096 " + "-name dirtylimit-test,debug-threads=on " + "-m 150M -smp 1 " + "-serial file:%s/vm_serial " + "-drive file=%s,format=raw ", + tmpfs, bootpath); + + vm = qtest_init(cmd); + return vm; +} + +static void dirtylimit_stop_vm(QTestState *vm) +{ + g_autofree char *path = g_strdup_printf("%s/%s", tmpfs, "vm_serial"); + + qtest_quit(vm); + unlink(path); +} + +static void test_vcpu_dirty_limit(void) +{ + QTestState *vm; + int64_t origin_rate; + int64_t quota_rate; + int64_t rate ; + int max_try_count = 20; + int hit = 0; + + /* Start vm for vcpu dirtylimit test */ + vm = dirtylimit_start_vm(); + + /* Wait for the first serial output from the vm*/ + wait_for_serial("vm_serial"); + + /* Do dirtyrate measurement with calc time equals 1s */ + calc_dirty_rate(vm, 1); + + /* Sleep calc time and wait for calc dirtyrate complete */ + wait_for_calc_dirtyrate_complete(vm, 1); + + /* Query original dirty page rate */ + origin_rate = get_dirty_rate(vm); + + /* VM booted from bootsect should dirty memory steadily */ + assert(origin_rate != 0); + + /* Setup quota dirty page rate at half of origin */ + quota_rate = origin_rate / 2; + + /* Set dirtylimit */ + dirtylimit_set_all(vm, quota_rate); + + /* + * Check if set-vcpu-dirty-limit and query-vcpu-dirty-limit + * works literally + */ + g_assert_cmpint(quota_rate, ==, get_limit_rate(vm)); + + /* Sleep a bit to check if it take effect */ + usleep(2000000); + + /* + * Check if dirtylimit take effect realistically, set the + * timeout with 20 s(max_try_count * 1s), if dirtylimit + * doesn't take effect, fail test. + */ + while (--max_try_count) { + calc_dirty_rate(vm, 1); + wait_for_calc_dirtyrate_complete(vm, 1); + rate = get_dirty_rate(vm); + + /* + * Assume hitting if current rate is less + * than quota rate (within accepting error) + */ + if (rate < (quota_rate + DIRTYLIMIT_TOLERANCE_RANGE)) { + hit = 1; + break; + } + } + + g_assert_cmpint(hit, ==, 1); + + hit = 0; + max_try_count = 20; + + /* Check if dirtylimit cancellation take effect */ + cancel_vcpu_dirty_limit(vm); + while (--max_try_count) { + calc_dirty_rate(vm, 1); + wait_for_calc_dirtyrate_complete(vm, 1); + rate = get_dirty_rate(vm); + + /* + * Assume dirtylimit be canceled if current rate is + * greater than quota rate (within accepting error) + */ + if (rate > (quota_rate + DIRTYLIMIT_TOLERANCE_RANGE)) { + hit = 1; + break; + } + } + + g_assert_cmpint(hit, ==, 1); + dirtylimit_stop_vm(vm); +} + +static void migrate_dirty_limit_wait_showup(QTestState *from, + const int64_t period, + const int64_t value) +{ + /* Enable dirty limit capability */ + migrate_set_capability(from, "dirty-limit", true); + + /* Set dirty limit parameters */ + migrate_set_parameter_int(from, "x-vcpu-dirty-limit-period", period); + migrate_set_parameter_int(from, "vcpu-dirty-limit", value); + + /* Make sure migrate can't converge */ + migrate_ensure_non_converge(from); + + /* To check limit rate after precopy */ + migrate_set_capability(from, "pause-before-switchover", true); + + /* Wait for the serial output from the source */ + wait_for_serial("src_serial"); +} + +/* + * This test does: + * source destination + * start vm + * start incoming vm + * migrate + * wait dirty limit to begin + * cancel migrate + * cancellation check + * restart incoming vm + * migrate + * wait dirty limit to begin + * wait pre-switchover event + * convergence condition check + * + * And see if dirty limit migration works correctly. + * This test case involves many passes, so it runs in slow mode only. + */ +static void test_dirty_limit(void) +{ + g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); + QTestState *from, *to; + int64_t remaining; + uint64_t throttle_us_per_full; + /* + * We want the test to be stable and as fast as possible. + * E.g., with 1Gb/s bandwidth migration may pass without dirty limit, + * so we need to decrease a bandwidth. + */ + const int64_t dirtylimit_period = 1000, dirtylimit_value = 50; + const int64_t max_bandwidth = 400000000; /* ~400Mb/s */ + const int64_t downtime_limit = 250; /* 250ms */ + /* + * We migrate through unix-socket (> 500Mb/s). + * Thus, expected migration speed ~= bandwidth limit (< 500Mb/s). + * So, we can predict expected_threshold + */ + const int64_t expected_threshold = max_bandwidth * downtime_limit / 1000; + int max_try_count = 10; + MigrateCommon args = { + .start = { + .hide_stderr = true, + .use_dirty_ring = true, + }, + .listen_uri = uri, + .connect_uri = uri, + }; + + /* Start src, dst vm */ + if (migrate_start(&from, &to, args.listen_uri, &args.start)) { + return; + } + + /* Prepare for dirty limit migration and wait src vm show up */ + migrate_dirty_limit_wait_showup(from, dirtylimit_period, dirtylimit_value); + + /* Start migrate */ + migrate_qmp(from, to, args.connect_uri, NULL, "{}"); + + /* Wait for dirty limit throttle begin */ + throttle_us_per_full = 0; + while (throttle_us_per_full == 0) { + throttle_us_per_full = + read_migrate_property_int(from, + "dirty-limit-throttle-time-per-round"); + usleep(100); + g_assert_false(get_src()->stop_seen); + } + + /* Now cancel migrate and wait for dirty limit throttle switch off */ + migrate_cancel(from); + wait_for_migration_status(from, "cancelled", NULL); + + /* destination always fails after cancel */ + migration_event_wait(to, "failed"); + qtest_set_expected_status(to, EXIT_FAILURE); + qtest_quit(to); + + /* Check if dirty limit throttle switched off, set timeout 1ms */ + do { + throttle_us_per_full = + read_migrate_property_int(from, + "dirty-limit-throttle-time-per-round"); + usleep(100); + g_assert_false(get_src()->stop_seen); + } while (throttle_us_per_full != 0 && --max_try_count); + + /* Assert dirty limit is not in service */ + g_assert_cmpint(throttle_us_per_full, ==, 0); + + args = (MigrateCommon) { + .start = { + .only_target = true, + .use_dirty_ring = true, + }, + .listen_uri = uri, + .connect_uri = uri, + }; + + /* Restart dst vm, src vm already show up so we needn't wait anymore */ + if (migrate_start(&from, &to, args.listen_uri, &args.start)) { + return; + } + + /* Start migrate */ + migrate_qmp(from, to, args.connect_uri, NULL, "{}"); + + /* Wait for dirty limit throttle begin */ + throttle_us_per_full = 0; + while (throttle_us_per_full == 0) { + throttle_us_per_full = + read_migrate_property_int(from, + "dirty-limit-throttle-time-per-round"); + usleep(100); + g_assert_false(get_src()->stop_seen); + } + + /* + * The dirty limit rate should equals the return value of + * query-vcpu-dirty-limit if dirty limit cap set + */ + g_assert_cmpint(dirtylimit_value, ==, get_limit_rate(from)); + + /* Now, we have tested if dirty limit works, let it converge */ + migrate_set_parameter_int(from, "downtime-limit", downtime_limit); + migrate_set_parameter_int(from, "max-bandwidth", max_bandwidth); + + /* + * Wait for pre-switchover status to check if migration + * satisfy the convergence condition + */ + wait_for_migration_status(from, "pre-switchover", NULL); + + remaining = read_ram_property_int(from, "remaining"); + g_assert_cmpint(remaining, <, + (expected_threshold + expected_threshold / 100)); + + migrate_continue(from, "pre-switchover"); + + qtest_qmp_eventwait(to, "RESUME"); + + wait_for_serial("dest_serial"); + wait_for_migration_complete(from); + + migrate_end(from, to, true); +} + +static void migration_test_add_precopy_smoke(MigrationTestEnv *env) +{ + if (env->is_x86) { + migration_test_add("/migration/precopy/unix/suspend/live", + test_precopy_unix_suspend_live); + migration_test_add("/migration/precopy/unix/suspend/notlive", + test_precopy_unix_suspend_notlive); + } + + migration_test_add("/migration/precopy/unix/plain", + test_precopy_unix_plain); + + migration_test_add("/migration/precopy/tcp/plain", test_precopy_tcp_plain); + migration_test_add("/migration/multifd/tcp/uri/plain/none", + test_multifd_tcp_uri_none); + migration_test_add("/migration/multifd/tcp/plain/cancel", + test_multifd_precopy_tcp_cancel); + if (env->has_uffd) { + migration_test_add("/migration/multifd+postcopy/tcp/plain/cancel", + test_multifd_postcopy_tcp_cancel); + } + +#ifdef CONFIG_RDMA + migration_test_add("/migration/precopy/rdma/plain", + test_precopy_rdma_plain); + migration_test_add("/migration/precopy/rdma/plain/ipv6", + test_precopy_rdma_plain_ipv6); +#endif +} + +void migration_test_add_precopy(MigrationTestEnv *env) +{ + tmpfs = env->tmpfs; + + migration_test_add_precopy_smoke(env); + + if (!env->full_set) { + return; + } + + migration_test_add("/migration/precopy/tcp/plain/switchover-ack", + test_precopy_tcp_switchover_ack); + +#ifndef _WIN32 + migration_test_add("/migration/precopy/fd/tcp", + test_precopy_fd_socket); + migration_test_add("/migration/precopy/fd/file", + test_precopy_fd_file); +#endif + + /* + * See explanation why this test is slow on function definition + */ + if (g_test_slow()) { + migration_test_add("/migration/auto_converge", + test_auto_converge); + if (g_str_equal(env->arch, "x86_64") && + env->has_kvm && env->has_dirty_ring) { + migration_test_add("/dirty_limit", + test_dirty_limit); + } + } + migration_test_add("/migration/multifd/tcp/channels/plain/none", + test_multifd_tcp_channels_none); + migration_test_add("/migration/multifd/tcp/plain/zero-page/legacy", + test_multifd_tcp_zero_page_legacy); + migration_test_add("/migration/multifd/tcp/plain/zero-page/none", + test_multifd_tcp_no_zero_page); + if (g_str_equal(env->arch, "x86_64") + && env->has_kvm && env->has_dirty_ring) { + + migration_test_add("/migration/dirty_ring", + test_precopy_unix_dirty_ring); + if (qtest_has_machine("pc") && g_test_slow()) { + migration_test_add("/migration/vcpu_dirty_limit", + test_vcpu_dirty_limit); + } + } + + /* ensure new status don't go unnoticed */ + assert(MIGRATION_STATUS__MAX == 15); + + for (int i = MIGRATION_STATUS_NONE; i < MIGRATION_STATUS__MAX; i++) { + switch (i) { + case MIGRATION_STATUS_DEVICE: /* happens too fast */ + case MIGRATION_STATUS_WAIT_UNPLUG: /* no support in tests */ + case MIGRATION_STATUS_COLO: /* no support in tests */ + case MIGRATION_STATUS_POSTCOPY_ACTIVE: /* postcopy can't be cancelled */ + case MIGRATION_STATUS_POSTCOPY_PAUSED: + case MIGRATION_STATUS_POSTCOPY_RECOVER_SETUP: + case MIGRATION_STATUS_POSTCOPY_RECOVER: + continue; + default: + migration_test_add_suffix("/migration/cancel/src/after/", + MigrationStatus_str(i), + test_cancel_src_after_status); + } + } +} diff --git a/tests/migration/s390x/Makefile b/tests/qtest/migration/s390x/Makefile index 6671de2..6671de2 100644 --- a/tests/migration/s390x/Makefile +++ b/tests/qtest/migration/s390x/Makefile diff --git a/tests/migration/s390x/a-b-bios.c b/tests/qtest/migration/s390x/a-b-bios.c index ff99a3e..ff99a3e 100644 --- a/tests/migration/s390x/a-b-bios.c +++ b/tests/qtest/migration/s390x/a-b-bios.c diff --git a/tests/migration/s390x/a-b-bios.h b/tests/qtest/migration/s390x/a-b-bios.h index 96103da..96103da 100644 --- a/tests/migration/s390x/a-b-bios.h +++ b/tests/qtest/migration/s390x/a-b-bios.h diff --git a/tests/qtest/migration/tls-tests.c b/tests/qtest/migration/tls-tests.c new file mode 100644 index 0000000..21e9fec --- /dev/null +++ b/tests/qtest/migration/tls-tests.c @@ -0,0 +1,871 @@ +/* + * QTest testcases for TLS migration + * + * Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates + * based on the vhost-user-test.c that is: + * Copyright (c) 2014 Virtual Open Systems Sarl. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + */ + +#include "qemu/osdep.h" +#include "crypto/tlscredspsk.h" +#include "libqtest.h" +#include "migration/framework.h" +#include "migration/migration-qmp.h" +#include "migration/migration-util.h" + +#include "tests/unit/crypto-tls-psk-helpers.h" +#ifdef CONFIG_TASN1 +# include "tests/unit/crypto-tls-x509-helpers.h" +#endif /* CONFIG_TASN1 */ + + +struct TestMigrateTLSPSKData { + char *workdir; + char *workdiralt; + char *pskfile; + char *pskfilealt; +}; + +static char *tmpfs; + +static void * +migrate_hook_start_tls_psk_common(QTestState *from, + QTestState *to, + bool mismatch) +{ + struct TestMigrateTLSPSKData *data = + g_new0(struct TestMigrateTLSPSKData, 1); + + data->workdir = g_strdup_printf("%s/tlscredspsk0", tmpfs); + data->pskfile = g_strdup_printf("%s/%s", data->workdir, + QCRYPTO_TLS_CREDS_PSKFILE); + g_mkdir_with_parents(data->workdir, 0700); + test_tls_psk_init(data->pskfile); + + if (mismatch) { + data->workdiralt = g_strdup_printf("%s/tlscredspskalt0", tmpfs); + data->pskfilealt = g_strdup_printf("%s/%s", data->workdiralt, + QCRYPTO_TLS_CREDS_PSKFILE); + g_mkdir_with_parents(data->workdiralt, 0700); + test_tls_psk_init_alt(data->pskfilealt); + } + + qtest_qmp_assert_success(from, + "{ 'execute': 'object-add'," + " 'arguments': { 'qom-type': 'tls-creds-psk'," + " 'id': 'tlscredspsk0'," + " 'endpoint': 'client'," + " 'dir': %s," + " 'username': 'qemu'} }", + data->workdir); + + qtest_qmp_assert_success(to, + "{ 'execute': 'object-add'," + " 'arguments': { 'qom-type': 'tls-creds-psk'," + " 'id': 'tlscredspsk0'," + " 'endpoint': 'server'," + " 'dir': %s } }", + mismatch ? data->workdiralt : data->workdir); + + migrate_set_parameter_str(from, "tls-creds", "tlscredspsk0"); + migrate_set_parameter_str(to, "tls-creds", "tlscredspsk0"); + + return data; +} + +static void * +migrate_hook_start_tls_psk_match(QTestState *from, + QTestState *to) +{ + return migrate_hook_start_tls_psk_common(from, to, false); +} + +static void * +migrate_hook_start_tls_psk_mismatch(QTestState *from, + QTestState *to) +{ + return migrate_hook_start_tls_psk_common(from, to, true); +} + +static void +migrate_hook_end_tls_psk(QTestState *from, + QTestState *to, + void *opaque) +{ + struct TestMigrateTLSPSKData *data = opaque; + + test_tls_psk_cleanup(data->pskfile); + if (data->pskfilealt) { + test_tls_psk_cleanup(data->pskfilealt); + } + rmdir(data->workdir); + if (data->workdiralt) { + rmdir(data->workdiralt); + } + + g_free(data->workdiralt); + g_free(data->pskfilealt); + g_free(data->workdir); + g_free(data->pskfile); + g_free(data); +} + +#ifdef CONFIG_TASN1 +typedef struct { + char *workdir; + char *keyfile; + char *cacert; + char *servercert; + char *serverkey; + char *clientcert; + char *clientkey; +} TestMigrateTLSX509Data; + +typedef struct { + bool verifyclient; + bool clientcert; + bool hostileclient; + bool authzclient; + const char *certhostname; + const char *certipaddr; +} TestMigrateTLSX509; + +static void * +migrate_hook_start_tls_x509_common(QTestState *from, + QTestState *to, + TestMigrateTLSX509 *args) +{ + TestMigrateTLSX509Data *data = g_new0(TestMigrateTLSX509Data, 1); + + data->workdir = g_strdup_printf("%s/tlscredsx5090", tmpfs); + data->keyfile = g_strdup_printf("%s/key.pem", data->workdir); + + data->cacert = g_strdup_printf("%s/ca-cert.pem", data->workdir); + data->serverkey = g_strdup_printf("%s/server-key.pem", data->workdir); + data->servercert = g_strdup_printf("%s/server-cert.pem", data->workdir); + if (args->clientcert) { + data->clientkey = g_strdup_printf("%s/client-key.pem", data->workdir); + data->clientcert = g_strdup_printf("%s/client-cert.pem", data->workdir); + } + + g_mkdir_with_parents(data->workdir, 0700); + + test_tls_init(data->keyfile); +#ifndef _WIN32 + g_assert(link(data->keyfile, data->serverkey) == 0); +#else + g_assert(CreateHardLink(data->serverkey, data->keyfile, NULL) != 0); +#endif + if (args->clientcert) { +#ifndef _WIN32 + g_assert(link(data->keyfile, data->clientkey) == 0); +#else + g_assert(CreateHardLink(data->clientkey, data->keyfile, NULL) != 0); +#endif + } + + TLS_ROOT_REQ_SIMPLE(cacertreq, data->cacert); + if (args->clientcert) { + TLS_CERT_REQ_SIMPLE_CLIENT(servercertreq, cacertreq, + args->hostileclient ? + QCRYPTO_TLS_TEST_CLIENT_HOSTILE_NAME : + QCRYPTO_TLS_TEST_CLIENT_NAME, + data->clientcert); + test_tls_deinit_cert(&servercertreq); + } + + TLS_CERT_REQ_SIMPLE_SERVER(clientcertreq, cacertreq, + data->servercert, + args->certhostname, + args->certipaddr); + test_tls_deinit_cert(&clientcertreq); + test_tls_deinit_cert(&cacertreq); + + qtest_qmp_assert_success(from, + "{ 'execute': 'object-add'," + " 'arguments': { 'qom-type': 'tls-creds-x509'," + " 'id': 'tlscredsx509client0'," + " 'endpoint': 'client'," + " 'dir': %s," + " 'sanity-check': true," + " 'verify-peer': true} }", + data->workdir); + migrate_set_parameter_str(from, "tls-creds", "tlscredsx509client0"); + if (args->certhostname) { + migrate_set_parameter_str(from, "tls-hostname", args->certhostname); + } + + qtest_qmp_assert_success(to, + "{ 'execute': 'object-add'," + " 'arguments': { 'qom-type': 'tls-creds-x509'," + " 'id': 'tlscredsx509server0'," + " 'endpoint': 'server'," + " 'dir': %s," + " 'sanity-check': true," + " 'verify-peer': %i} }", + data->workdir, args->verifyclient); + migrate_set_parameter_str(to, "tls-creds", "tlscredsx509server0"); + + if (args->authzclient) { + qtest_qmp_assert_success(to, + "{ 'execute': 'object-add'," + " 'arguments': { 'qom-type': 'authz-simple'," + " 'id': 'tlsauthz0'," + " 'identity': %s} }", + "CN=" QCRYPTO_TLS_TEST_CLIENT_NAME); + migrate_set_parameter_str(to, "tls-authz", "tlsauthz0"); + } + + return data; +} + +/* + * The normal case: match server's cert hostname against + * whatever host we were telling QEMU to connect to (if any) + */ +static void * +migrate_hook_start_tls_x509_default_host(QTestState *from, + QTestState *to) +{ + TestMigrateTLSX509 args = { + .verifyclient = true, + .clientcert = true, + .certipaddr = "127.0.0.1" + }; + return migrate_hook_start_tls_x509_common(from, to, &args); +} + +/* + * The unusual case: the server's cert is different from + * the address we're telling QEMU to connect to (if any), + * so we must give QEMU an explicit hostname to validate + */ +static void * +migrate_hook_start_tls_x509_override_host(QTestState *from, + QTestState *to) +{ + TestMigrateTLSX509 args = { + .verifyclient = true, + .clientcert = true, + .certhostname = "qemu.org", + }; + return migrate_hook_start_tls_x509_common(from, to, &args); +} + +/* + * The unusual case: the server's cert is different from + * the address we're telling QEMU to connect to, and so we + * expect the client to reject the server + */ +static void * +migrate_hook_start_tls_x509_mismatch_host(QTestState *from, + QTestState *to) +{ + TestMigrateTLSX509 args = { + .verifyclient = true, + .clientcert = true, + .certipaddr = "10.0.0.1", + }; + return migrate_hook_start_tls_x509_common(from, to, &args); +} + +static void * +migrate_hook_start_tls_x509_friendly_client(QTestState *from, + QTestState *to) +{ + TestMigrateTLSX509 args = { + .verifyclient = true, + .clientcert = true, + .authzclient = true, + .certipaddr = "127.0.0.1", + }; + return migrate_hook_start_tls_x509_common(from, to, &args); +} + +static void * +migrate_hook_start_tls_x509_hostile_client(QTestState *from, + QTestState *to) +{ + TestMigrateTLSX509 args = { + .verifyclient = true, + .clientcert = true, + .hostileclient = true, + .authzclient = true, + .certipaddr = "127.0.0.1", + }; + return migrate_hook_start_tls_x509_common(from, to, &args); +} + +/* + * The case with no client certificate presented, + * and no server verification + */ +static void * +migrate_hook_start_tls_x509_allow_anon_client(QTestState *from, + QTestState *to) +{ + TestMigrateTLSX509 args = { + .certipaddr = "127.0.0.1", + }; + return migrate_hook_start_tls_x509_common(from, to, &args); +} + +/* + * The case with no client certificate presented, + * and server verification rejecting + */ +static void * +migrate_hook_start_tls_x509_reject_anon_client(QTestState *from, + QTestState *to) +{ + TestMigrateTLSX509 args = { + .verifyclient = true, + .certipaddr = "127.0.0.1", + }; + return migrate_hook_start_tls_x509_common(from, to, &args); +} + +static void +migrate_hook_end_tls_x509(QTestState *from, + QTestState *to, + void *opaque) +{ + TestMigrateTLSX509Data *data = opaque; + + test_tls_cleanup(data->keyfile); + g_free(data->keyfile); + + unlink(data->cacert); + g_free(data->cacert); + unlink(data->servercert); + g_free(data->servercert); + unlink(data->serverkey); + g_free(data->serverkey); + + if (data->clientcert) { + unlink(data->clientcert); + g_free(data->clientcert); + } + if (data->clientkey) { + unlink(data->clientkey); + g_free(data->clientkey); + } + + rmdir(data->workdir); + g_free(data->workdir); + + g_free(data); +} +#endif /* CONFIG_TASN1 */ + +static void test_postcopy_tls_psk(void) +{ + MigrateCommon args = { + .start_hook = migrate_hook_start_tls_psk_match, + .end_hook = migrate_hook_end_tls_psk, + }; + + test_postcopy_common(&args); +} + +static void test_postcopy_preempt_tls_psk(void) +{ + MigrateCommon args = { + .start_hook = migrate_hook_start_tls_psk_match, + .end_hook = migrate_hook_end_tls_psk, + .start = { + .caps[MIGRATION_CAPABILITY_POSTCOPY_PREEMPT] = true, + }, + }; + + test_postcopy_common(&args); +} + +static void test_postcopy_recovery_tls_psk(void) +{ + MigrateCommon args = { + .start_hook = migrate_hook_start_tls_psk_match, + .end_hook = migrate_hook_end_tls_psk, + }; + + test_postcopy_recovery_common(&args); +} + +static void test_multifd_postcopy_recovery_tls_psk(void) +{ + MigrateCommon args = { + .start_hook = migrate_hook_start_tls_psk_match, + .end_hook = migrate_hook_end_tls_psk, + .start = { + .caps[MIGRATION_CAPABILITY_MULTIFD] = true, + }, + }; + + test_postcopy_recovery_common(&args); +} + +/* This contains preempt+recovery+tls test altogether */ +static void test_postcopy_preempt_all(void) +{ + MigrateCommon args = { + .start_hook = migrate_hook_start_tls_psk_match, + .end_hook = migrate_hook_end_tls_psk, + .start = { + .caps[MIGRATION_CAPABILITY_POSTCOPY_PREEMPT] = true, + }, + }; + + test_postcopy_recovery_common(&args); +} + +static void test_multifd_postcopy_preempt_recovery_tls_psk(void) +{ + MigrateCommon args = { + .start_hook = migrate_hook_start_tls_psk_match, + .end_hook = migrate_hook_end_tls_psk, + .start = { + .caps[MIGRATION_CAPABILITY_MULTIFD] = true, + .caps[MIGRATION_CAPABILITY_POSTCOPY_PREEMPT] = true, + }, + }; + + test_postcopy_recovery_common(&args); +} + +static void test_precopy_unix_tls_psk(void) +{ + g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); + MigrateCommon args = { + .connect_uri = uri, + .listen_uri = uri, + .start_hook = migrate_hook_start_tls_psk_match, + .end_hook = migrate_hook_end_tls_psk, + }; + + test_precopy_common(&args); +} + +#ifdef CONFIG_TASN1 +static void test_precopy_unix_tls_x509_default_host(void) +{ + g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); + MigrateCommon args = { + .start = { + .hide_stderr = true, + }, + .connect_uri = uri, + .listen_uri = uri, + .start_hook = migrate_hook_start_tls_x509_default_host, + .end_hook = migrate_hook_end_tls_x509, + .result = MIG_TEST_FAIL_DEST_QUIT_ERR, + }; + + test_precopy_common(&args); +} + +static void test_precopy_unix_tls_x509_override_host(void) +{ + g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); + MigrateCommon args = { + .connect_uri = uri, + .listen_uri = uri, + .start_hook = migrate_hook_start_tls_x509_override_host, + .end_hook = migrate_hook_end_tls_x509, + }; + + test_precopy_common(&args); +} +#endif /* CONFIG_TASN1 */ + +static void test_precopy_tcp_tls_psk_match(void) +{ + MigrateCommon args = { + .listen_uri = "tcp:127.0.0.1:0", + .start_hook = migrate_hook_start_tls_psk_match, + .end_hook = migrate_hook_end_tls_psk, + }; + + test_precopy_common(&args); +} + +static void test_precopy_tcp_tls_psk_mismatch(void) +{ + MigrateCommon args = { + .start = { + .hide_stderr = true, + }, + .listen_uri = "tcp:127.0.0.1:0", + .start_hook = migrate_hook_start_tls_psk_mismatch, + .end_hook = migrate_hook_end_tls_psk, + .result = MIG_TEST_FAIL, + }; + + test_precopy_common(&args); +} + +#ifdef CONFIG_TASN1 +static void test_precopy_tcp_tls_x509_default_host(void) +{ + MigrateCommon args = { + .listen_uri = "tcp:127.0.0.1:0", + .start_hook = migrate_hook_start_tls_x509_default_host, + .end_hook = migrate_hook_end_tls_x509, + }; + + test_precopy_common(&args); +} + +static void test_precopy_tcp_tls_x509_override_host(void) +{ + MigrateCommon args = { + .listen_uri = "tcp:127.0.0.1:0", + .start_hook = migrate_hook_start_tls_x509_override_host, + .end_hook = migrate_hook_end_tls_x509, + }; + + test_precopy_common(&args); +} + +static void test_precopy_tcp_tls_x509_mismatch_host(void) +{ + MigrateCommon args = { + .start = { + .hide_stderr = true, + }, + .listen_uri = "tcp:127.0.0.1:0", + .start_hook = migrate_hook_start_tls_x509_mismatch_host, + .end_hook = migrate_hook_end_tls_x509, + .result = MIG_TEST_FAIL_DEST_QUIT_ERR, + }; + + test_precopy_common(&args); +} + +static void test_precopy_tcp_tls_x509_friendly_client(void) +{ + MigrateCommon args = { + .listen_uri = "tcp:127.0.0.1:0", + .start_hook = migrate_hook_start_tls_x509_friendly_client, + .end_hook = migrate_hook_end_tls_x509, + }; + + test_precopy_common(&args); +} + +static void test_precopy_tcp_tls_x509_hostile_client(void) +{ + MigrateCommon args = { + .start = { + .hide_stderr = true, + }, + .listen_uri = "tcp:127.0.0.1:0", + .start_hook = migrate_hook_start_tls_x509_hostile_client, + .end_hook = migrate_hook_end_tls_x509, + .result = MIG_TEST_FAIL, + }; + + test_precopy_common(&args); +} + +static void test_precopy_tcp_tls_x509_allow_anon_client(void) +{ + MigrateCommon args = { + .listen_uri = "tcp:127.0.0.1:0", + .start_hook = migrate_hook_start_tls_x509_allow_anon_client, + .end_hook = migrate_hook_end_tls_x509, + }; + + test_precopy_common(&args); +} + +static void test_precopy_tcp_tls_x509_reject_anon_client(void) +{ + MigrateCommon args = { + .start = { + .hide_stderr = true, + }, + .listen_uri = "tcp:127.0.0.1:0", + .start_hook = migrate_hook_start_tls_x509_reject_anon_client, + .end_hook = migrate_hook_end_tls_x509, + .result = MIG_TEST_FAIL, + }; + + test_precopy_common(&args); +} +#endif /* CONFIG_TASN1 */ + +static void * +migrate_hook_start_multifd_tcp_tls_psk_match(QTestState *from, + QTestState *to) +{ + migrate_hook_start_precopy_tcp_multifd_common(from, to, "none"); + return migrate_hook_start_tls_psk_match(from, to); +} + +static void * +migrate_hook_start_multifd_tcp_tls_psk_mismatch(QTestState *from, + QTestState *to) +{ + migrate_hook_start_precopy_tcp_multifd_common(from, to, "none"); + return migrate_hook_start_tls_psk_mismatch(from, to); +} + +#ifdef CONFIG_TASN1 +static void * +migrate_hook_start_multifd_tls_x509_default_host(QTestState *from, + QTestState *to) +{ + migrate_hook_start_precopy_tcp_multifd_common(from, to, "none"); + return migrate_hook_start_tls_x509_default_host(from, to); +} + +static void * +migrate_hook_start_multifd_tls_x509_override_host(QTestState *from, + QTestState *to) +{ + migrate_hook_start_precopy_tcp_multifd_common(from, to, "none"); + return migrate_hook_start_tls_x509_override_host(from, to); +} + +static void * +migrate_hook_start_multifd_tls_x509_mismatch_host(QTestState *from, + QTestState *to) +{ + migrate_hook_start_precopy_tcp_multifd_common(from, to, "none"); + return migrate_hook_start_tls_x509_mismatch_host(from, to); +} + +static void * +migrate_hook_start_multifd_tls_x509_allow_anon_client(QTestState *from, + QTestState *to) +{ + migrate_hook_start_precopy_tcp_multifd_common(from, to, "none"); + return migrate_hook_start_tls_x509_allow_anon_client(from, to); +} + +static void * +migrate_hook_start_multifd_tls_x509_reject_anon_client(QTestState *from, + QTestState *to) +{ + migrate_hook_start_precopy_tcp_multifd_common(from, to, "none"); + return migrate_hook_start_tls_x509_reject_anon_client(from, to); +} +#endif /* CONFIG_TASN1 */ + +static void test_multifd_tcp_tls_psk_match(void) +{ + MigrateCommon args = { + .listen_uri = "defer", + .start_hook = migrate_hook_start_multifd_tcp_tls_psk_match, + .end_hook = migrate_hook_end_tls_psk, + .start = { + .caps[MIGRATION_CAPABILITY_MULTIFD] = true, + }, + }; + test_precopy_common(&args); +} + +static void test_multifd_tcp_tls_psk_mismatch(void) +{ + MigrateCommon args = { + .start = { + .hide_stderr = true, + .caps[MIGRATION_CAPABILITY_MULTIFD] = true, + }, + .listen_uri = "defer", + .start_hook = migrate_hook_start_multifd_tcp_tls_psk_mismatch, + .end_hook = migrate_hook_end_tls_psk, + .result = MIG_TEST_FAIL, + }; + test_precopy_common(&args); +} + +static void test_multifd_postcopy_tcp_tls_psk_match(void) +{ + MigrateCommon args = { + .start = { + .caps[MIGRATION_CAPABILITY_MULTIFD] = true, + .caps[MIGRATION_CAPABILITY_POSTCOPY_RAM] = true, + }, + .listen_uri = "defer", + .start_hook = migrate_hook_start_multifd_tcp_tls_psk_match, + .end_hook = migrate_hook_end_tls_psk, + }; + + test_precopy_common(&args); +} + +#ifdef CONFIG_TASN1 +static void test_multifd_tcp_tls_x509_default_host(void) +{ + MigrateCommon args = { + .listen_uri = "defer", + .start_hook = migrate_hook_start_multifd_tls_x509_default_host, + .end_hook = migrate_hook_end_tls_x509, + .start = { + .caps[MIGRATION_CAPABILITY_MULTIFD] = true, + }, + }; + test_precopy_common(&args); +} + +static void test_multifd_tcp_tls_x509_override_host(void) +{ + MigrateCommon args = { + .listen_uri = "defer", + .start_hook = migrate_hook_start_multifd_tls_x509_override_host, + .end_hook = migrate_hook_end_tls_x509, + .start = { + .caps[MIGRATION_CAPABILITY_MULTIFD] = true, + }, + }; + test_precopy_common(&args); +} + +static void test_multifd_tcp_tls_x509_mismatch_host(void) +{ + /* + * This has different behaviour to the non-multifd case. + * + * In non-multifd case when client aborts due to mismatched + * cert host, the server has already started trying to load + * migration state, and so it exits with I/O failure. + * + * In multifd case when client aborts due to mismatched + * cert host, the server is still waiting for the other + * multifd connections to arrive so hasn't started trying + * to load migration state, and thus just aborts the migration + * without exiting. + */ + MigrateCommon args = { + .start = { + .hide_stderr = true, + .caps[MIGRATION_CAPABILITY_MULTIFD] = true, + }, + .listen_uri = "defer", + .start_hook = migrate_hook_start_multifd_tls_x509_mismatch_host, + .end_hook = migrate_hook_end_tls_x509, + .result = MIG_TEST_FAIL, + }; + test_precopy_common(&args); +} + +static void test_multifd_tcp_tls_x509_allow_anon_client(void) +{ + MigrateCommon args = { + .listen_uri = "defer", + .start_hook = migrate_hook_start_multifd_tls_x509_allow_anon_client, + .end_hook = migrate_hook_end_tls_x509, + .start = { + .caps[MIGRATION_CAPABILITY_MULTIFD] = true, + }, + }; + test_precopy_common(&args); +} + +static void test_multifd_tcp_tls_x509_reject_anon_client(void) +{ + MigrateCommon args = { + .start = { + .hide_stderr = true, + .caps[MIGRATION_CAPABILITY_MULTIFD] = true, + }, + .listen_uri = "defer", + .start_hook = migrate_hook_start_multifd_tls_x509_reject_anon_client, + .end_hook = migrate_hook_end_tls_x509, + .result = MIG_TEST_FAIL, + }; + test_precopy_common(&args); +} +#endif /* CONFIG_TASN1 */ + +static void migration_test_add_tls_smoke(MigrationTestEnv *env) +{ + migration_test_add("/migration/precopy/tcp/tls/psk/match", + test_precopy_tcp_tls_psk_match); +} + +void migration_test_add_tls(MigrationTestEnv *env) +{ + tmpfs = env->tmpfs; + + migration_test_add_tls_smoke(env); + + if (!env->full_set) { + return; + } + + migration_test_add("/migration/precopy/unix/tls/psk", + test_precopy_unix_tls_psk); + + if (env->has_uffd) { + /* + * NOTE: psk test is enough for postcopy, as other types of TLS + * channels are tested under precopy. Here what we want to test is the + * general postcopy path that has TLS channel enabled. + */ + migration_test_add("/migration/postcopy/tls/psk", + test_postcopy_tls_psk); + migration_test_add("/migration/postcopy/recovery/tls/psk", + test_postcopy_recovery_tls_psk); + migration_test_add("/migration/postcopy/preempt/tls/psk", + test_postcopy_preempt_tls_psk); + migration_test_add("/migration/postcopy/preempt/recovery/tls/psk", + test_postcopy_preempt_all); + migration_test_add("/migration/multifd+postcopy/recovery/tls/psk", + test_multifd_postcopy_recovery_tls_psk); + migration_test_add( + "/migration/multifd+postcopy/preempt/recovery/tls/psk", + test_multifd_postcopy_preempt_recovery_tls_psk); + } +#ifdef CONFIG_TASN1 + migration_test_add("/migration/precopy/unix/tls/x509/default-host", + test_precopy_unix_tls_x509_default_host); + migration_test_add("/migration/precopy/unix/tls/x509/override-host", + test_precopy_unix_tls_x509_override_host); +#endif /* CONFIG_TASN1 */ + + migration_test_add("/migration/precopy/tcp/tls/psk/mismatch", + test_precopy_tcp_tls_psk_mismatch); +#ifdef CONFIG_TASN1 + migration_test_add("/migration/precopy/tcp/tls/x509/default-host", + test_precopy_tcp_tls_x509_default_host); + migration_test_add("/migration/precopy/tcp/tls/x509/override-host", + test_precopy_tcp_tls_x509_override_host); + migration_test_add("/migration/precopy/tcp/tls/x509/mismatch-host", + test_precopy_tcp_tls_x509_mismatch_host); + migration_test_add("/migration/precopy/tcp/tls/x509/friendly-client", + test_precopy_tcp_tls_x509_friendly_client); + migration_test_add("/migration/precopy/tcp/tls/x509/hostile-client", + test_precopy_tcp_tls_x509_hostile_client); + migration_test_add("/migration/precopy/tcp/tls/x509/allow-anon-client", + test_precopy_tcp_tls_x509_allow_anon_client); + migration_test_add("/migration/precopy/tcp/tls/x509/reject-anon-client", + test_precopy_tcp_tls_x509_reject_anon_client); +#endif /* CONFIG_TASN1 */ + + migration_test_add("/migration/multifd/tcp/tls/psk/match", + test_multifd_tcp_tls_psk_match); + migration_test_add("/migration/multifd/tcp/tls/psk/mismatch", + test_multifd_tcp_tls_psk_mismatch); + if (env->has_uffd) { + migration_test_add("/migration/multifd+postcopy/tcp/tls/psk/match", + test_multifd_postcopy_tcp_tls_psk_match); + } +#ifdef CONFIG_TASN1 + migration_test_add("/migration/multifd/tcp/tls/x509/default-host", + test_multifd_tcp_tls_x509_default_host); + migration_test_add("/migration/multifd/tcp/tls/x509/override-host", + test_multifd_tcp_tls_x509_override_host); + migration_test_add("/migration/multifd/tcp/tls/x509/mismatch-host", + test_multifd_tcp_tls_x509_mismatch_host); + migration_test_add("/migration/multifd/tcp/tls/x509/allow-anon-client", + test_multifd_tcp_tls_x509_allow_anon_client); + migration_test_add("/migration/multifd/tcp/tls/x509/reject-anon-client", + test_multifd_tcp_tls_x509_reject_anon_client); +#endif /* CONFIG_TASN1 */ +} diff --git a/tests/qtest/netdev-socket.c b/tests/qtest/netdev-socket.c index fc7d119..b731af0 100644 --- a/tests/qtest/netdev-socket.c +++ b/tests/qtest/netdev-socket.c @@ -11,7 +11,7 @@ #include <glib/gstdio.h> #include "../unit/socket-helpers.h" #include "libqtest.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qstring.h" #include "qemu/sockets.h" #include "qapi/qobject-input-visitor.h" #include "qapi/qapi-visit-sockets.h" @@ -204,7 +204,7 @@ static void test_stream_unix_reconnect(void) qts1 = qtest_initf("-nodefaults -M none " "-netdev stream,server=false,id=st0,addr.type=unix," - "addr.path=%s,reconnect=1", path); + "addr.path=%s,reconnect-ms=1000", path); wait_stream_connected(qts0, "st0", &addr); g_assert_cmpint(addr->type, ==, SOCKET_ADDRESS_TYPE_UNIX); diff --git a/tests/qtest/npcm7xx_adc-test.c b/tests/qtest/npcm7xx_adc-test.c index e751a72..8bc89b8 100644 --- a/tests/qtest/npcm7xx_adc-test.c +++ b/tests/qtest/npcm7xx_adc-test.c @@ -18,7 +18,7 @@ #include "qemu/bitops.h" #include "qemu/timer.h" #include "libqtest.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #define REF_HZ (25000000) diff --git a/tests/qtest/npcm7xx_emc-test.c b/tests/qtest/npcm7xx_emc-test.c index 2e1a1a6..eeedb27 100644 --- a/tests/qtest/npcm7xx_emc-test.c +++ b/tests/qtest/npcm7xx_emc-test.c @@ -16,8 +16,8 @@ #include "qemu/osdep.h" #include "libqos/libqos.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qnum.h" +#include "qobject/qdict.h" +#include "qobject/qnum.h" #include "qemu/bitops.h" #include "qemu/iov.h" diff --git a/tests/qtest/npcm7xx_pwm-test.c b/tests/qtest/npcm7xx_pwm-test.c index b53a43c..052ea87 100644 --- a/tests/qtest/npcm7xx_pwm-test.c +++ b/tests/qtest/npcm7xx_pwm-test.c @@ -17,8 +17,8 @@ #include "qemu/osdep.h" #include "qemu/bitops.h" #include "libqtest.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qnum.h" +#include "qobject/qdict.h" +#include "qobject/qnum.h" static int verbosity_level; diff --git a/tests/qtest/npcm7xx_timer-test.c b/tests/qtest/npcm7xx_timer-test.c index 58f58c2..4371104 100644 --- a/tests/qtest/npcm7xx_timer-test.c +++ b/tests/qtest/npcm7xx_timer-test.c @@ -465,7 +465,6 @@ static void test_periodic_interrupt(gconstpointer test_data) int i; tim_reset(td); - clock_step_next(); tim_write_ticr(td, count); tim_write_tcsr(td, CEN | IE | MODE_PERIODIC | PRESCALE(ps)); diff --git a/tests/qtest/npcm7xx_watchdog_timer-test.c b/tests/qtest/npcm7xx_watchdog_timer-test.c index 981b853..521ea78 100644 --- a/tests/qtest/npcm7xx_watchdog_timer-test.c +++ b/tests/qtest/npcm7xx_watchdog_timer-test.c @@ -18,7 +18,7 @@ #include "qemu/timer.h" #include "libqtest.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #define WTCR_OFFSET 0x1c #define REF_HZ (25000000) diff --git a/tests/qtest/npcm_gmac-test.c b/tests/qtest/npcm_gmac-test.c index c28b471..1317da2 100644 --- a/tests/qtest/npcm_gmac-test.c +++ b/tests/qtest/npcm_gmac-test.c @@ -36,7 +36,7 @@ typedef struct TestData { const GMACModule *module; } TestData; -/* Values extracted from hw/arm/npcm7xx.c */ +/* Values extracted from hw/arm/npcm8xx.c */ static const GMACModule gmac_module_list[] = { { .irq = 14, @@ -46,6 +46,14 @@ static const GMACModule gmac_module_list[] = { .irq = 15, .base_addr = 0xf0804000 }, + { + .irq = 16, + .base_addr = 0xf0806000 + }, + { + .irq = 17, + .base_addr = 0xf0808000 + } }; /* Returns the index of the GMAC module. */ @@ -174,18 +182,32 @@ static uint32_t gmac_read(QTestState *qts, const GMACModule *mod, return qtest_readl(qts, mod->base_addr + regno); } +static uint16_t pcs_read(QTestState *qts, const GMACModule *mod, + NPCMRegister regno) +{ + uint32_t write_value = (regno & 0x3ffe00) >> 9; + qtest_writel(qts, PCS_BASE_ADDRESS + NPCM_PCS_IND_AC_BA, write_value); + uint32_t read_offset = regno & 0x1ff; + return qtest_readl(qts, PCS_BASE_ADDRESS + read_offset); +} + /* Check that GMAC registers are reset to default value */ static void test_init(gconstpointer test_data) { const TestData *td = test_data; const GMACModule *mod = td->module; - QTestState *qts = qtest_init("-machine npcm750-evb"); + QTestState *qts = qtest_init("-machine npcm845-evb"); #define CHECK_REG32(regno, value) \ do { \ g_assert_cmphex(gmac_read(qts, mod, (regno)), ==, (value)); \ } while (0) +#define CHECK_REG_PCS(regno, value) \ + do { \ + g_assert_cmphex(pcs_read(qts, mod, (regno)), ==, (value)); \ + } while (0) + CHECK_REG32(NPCM_DMA_BUS_MODE, 0x00020100); CHECK_REG32(NPCM_DMA_XMT_POLL_DEMAND, 0); CHECK_REG32(NPCM_DMA_RCV_POLL_DEMAND, 0); @@ -235,6 +257,63 @@ static void test_init(gconstpointer test_data) CHECK_REG32(NPCM_GMAC_PTP_TAR, 0); CHECK_REG32(NPCM_GMAC_PTP_TTSR, 0); + if (mod->base_addr == 0xf0802000) { + CHECK_REG_PCS(NPCM_PCS_SR_CTL_ID1, 0x699e); + CHECK_REG_PCS(NPCM_PCS_SR_CTL_ID2, 0); + CHECK_REG_PCS(NPCM_PCS_SR_CTL_STS, 0x8000); + + CHECK_REG_PCS(NPCM_PCS_SR_MII_CTRL, 0x1140); + CHECK_REG_PCS(NPCM_PCS_SR_MII_STS, 0x0109); + CHECK_REG_PCS(NPCM_PCS_SR_MII_DEV_ID1, 0x699e); + CHECK_REG_PCS(NPCM_PCS_SR_MII_DEV_ID2, 0x0ced0); + CHECK_REG_PCS(NPCM_PCS_SR_MII_AN_ADV, 0x0020); + CHECK_REG_PCS(NPCM_PCS_SR_MII_LP_BABL, 0); + CHECK_REG_PCS(NPCM_PCS_SR_MII_AN_EXPN, 0); + CHECK_REG_PCS(NPCM_PCS_SR_MII_EXT_STS, 0xc000); + + CHECK_REG_PCS(NPCM_PCS_SR_TIM_SYNC_ABL, 0x0003); + CHECK_REG_PCS(NPCM_PCS_SR_TIM_SYNC_TX_MAX_DLY_LWR, 0x0038); + CHECK_REG_PCS(NPCM_PCS_SR_TIM_SYNC_TX_MAX_DLY_UPR, 0); + CHECK_REG_PCS(NPCM_PCS_SR_TIM_SYNC_TX_MIN_DLY_LWR, 0x0038); + CHECK_REG_PCS(NPCM_PCS_SR_TIM_SYNC_TX_MIN_DLY_UPR, 0); + CHECK_REG_PCS(NPCM_PCS_SR_TIM_SYNC_RX_MAX_DLY_LWR, 0x0058); + CHECK_REG_PCS(NPCM_PCS_SR_TIM_SYNC_RX_MAX_DLY_UPR, 0); + CHECK_REG_PCS(NPCM_PCS_SR_TIM_SYNC_RX_MIN_DLY_LWR, 0x0048); + CHECK_REG_PCS(NPCM_PCS_SR_TIM_SYNC_RX_MIN_DLY_UPR, 0); + + CHECK_REG_PCS(NPCM_PCS_VR_MII_MMD_DIG_CTRL1, 0x2400); + CHECK_REG_PCS(NPCM_PCS_VR_MII_AN_CTRL, 0); + CHECK_REG_PCS(NPCM_PCS_VR_MII_AN_INTR_STS, 0x000a); + CHECK_REG_PCS(NPCM_PCS_VR_MII_TC, 0); + CHECK_REG_PCS(NPCM_PCS_VR_MII_DBG_CTRL, 0); + CHECK_REG_PCS(NPCM_PCS_VR_MII_EEE_MCTRL0, 0x899c); + CHECK_REG_PCS(NPCM_PCS_VR_MII_EEE_TXTIMER, 0); + CHECK_REG_PCS(NPCM_PCS_VR_MII_EEE_RXTIMER, 0); + CHECK_REG_PCS(NPCM_PCS_VR_MII_LINK_TIMER_CTRL, 0); + CHECK_REG_PCS(NPCM_PCS_VR_MII_EEE_MCTRL1, 0); + CHECK_REG_PCS(NPCM_PCS_VR_MII_DIG_STS, 0x0010); + CHECK_REG_PCS(NPCM_PCS_VR_MII_ICG_ERRCNT1, 0); + CHECK_REG_PCS(NPCM_PCS_VR_MII_MISC_STS, 0); + CHECK_REG_PCS(NPCM_PCS_VR_MII_RX_LSTS, 0); + CHECK_REG_PCS(NPCM_PCS_VR_MII_MP_TX_BSTCTRL0, 0x00a); + CHECK_REG_PCS(NPCM_PCS_VR_MII_MP_TX_LVLCTRL0, 0x007f); + CHECK_REG_PCS(NPCM_PCS_VR_MII_MP_TX_GENCTRL0, 0x0001); + CHECK_REG_PCS(NPCM_PCS_VR_MII_MP_TX_GENCTRL1, 0); + CHECK_REG_PCS(NPCM_PCS_VR_MII_MP_TX_STS, 0); + CHECK_REG_PCS(NPCM_PCS_VR_MII_MP_RX_GENCTRL0, 0x0100); + CHECK_REG_PCS(NPCM_PCS_VR_MII_MP_RX_GENCTRL1, 0x1100); + CHECK_REG_PCS(NPCM_PCS_VR_MII_MP_RX_LOS_CTRL0, 0x000e); + CHECK_REG_PCS(NPCM_PCS_VR_MII_MP_MPLL_CTRL0, 0x0100); + CHECK_REG_PCS(NPCM_PCS_VR_MII_MP_MPLL_CTRL1, 0x0032); + CHECK_REG_PCS(NPCM_PCS_VR_MII_MP_MPLL_STS, 0x0001); + CHECK_REG_PCS(NPCM_PCS_VR_MII_MP_MISC_CTRL2, 0); + CHECK_REG_PCS(NPCM_PCS_VR_MII_MP_LVL_CTRL, 0x0019); + CHECK_REG_PCS(NPCM_PCS_VR_MII_MP_MISC_CTRL0, 0); + CHECK_REG_PCS(NPCM_PCS_VR_MII_MP_MISC_CTRL1, 0); + CHECK_REG_PCS(NPCM_PCS_VR_MII_DIG_CTRL2, 0); + CHECK_REG_PCS(NPCM_PCS_VR_MII_DIG_ERRCNT_SEL, 0); + } + qtest_quit(qts); } @@ -242,7 +321,7 @@ static void gmac_add_test(const char *name, const TestData* td, GTestDataFunc fn) { g_autofree char *full_name = g_strdup_printf( - "npcm7xx_gmac/gmac[%d]/%s", gmac_module_index(td->module), name); + "npcm8xx_gmac/gmac[%d]/%s", gmac_module_index(td->module), name); qtest_add_data_func(full_name, td, fn); } diff --git a/tests/qtest/numa-test.c b/tests/qtest/numa-test.c index ede4189..d657f38 100644 --- a/tests/qtest/numa-test.c +++ b/tests/qtest/numa-test.c @@ -11,8 +11,8 @@ #include "qemu/osdep.h" #include "libqtest.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qlist.h" +#include "qobject/qdict.h" +#include "qobject/qlist.h" static char *make_cli(const GString *generic_cli, const char *test_cli) { @@ -162,7 +162,7 @@ static void pc_numa_cpu(const void *data) } else if (socket == 1 && core == 1 && thread == 1) { g_assert_cmpint(node, ==, 1); } else { - g_assert(false); + g_assert_not_reached(); } qobject_unref(e); } @@ -207,7 +207,7 @@ static void spapr_numa_cpu(const void *data) } else if (core == 3) { g_assert_cmpint(node, ==, 1); } else { - g_assert(false); + g_assert_not_reached(); } qobject_unref(e); } @@ -257,7 +257,7 @@ static void aarch64_numa_cpu(const void *data) } else if (socket == 1 && cluster == 0 && core == 0 && thread == 0) { g_assert_cmpint(node, ==, 0); } else { - g_assert(false); + g_assert_not_reached(); } qobject_unref(e); } @@ -305,7 +305,7 @@ static void loongarch64_numa_cpu(const void *data) } else if (socket == 1 && core == 0 && thread == 0) { g_assert_cmpint(node, ==, 0); } else { - g_assert(false); + g_assert_not_reached(); } qobject_unref(e); } @@ -367,7 +367,7 @@ static void pc_dynamic_cpu_cfg(const void *data) } else if (socket == 1) { g_assert_cmpint(node, ==, 0); } else { - g_assert(false); + g_assert_not_reached(); } qobject_unref(e); } diff --git a/tests/qtest/pnv-host-i2c-test.c b/tests/qtest/pnv-host-i2c-test.c index 7f64d59..51e613e 100644 --- a/tests/qtest/pnv-host-i2c-test.c +++ b/tests/qtest/pnv-host-i2c-test.c @@ -191,12 +191,10 @@ static uint8_t pnv_i2c_pca9554_read_pins(PnvI2cDev *dev) { uint8_t send_buf[1]; uint8_t recv_buf[1]; - uint8_t inputs; send_buf[0] = PCA9554_INPUT; pnv_i2c_send(dev, send_buf, 1); pnv_i2c_recv(dev, recv_buf, 1); - inputs = recv_buf[0]; - return inputs; + return recv_buf[0]; } static void pnv_i2c_pca9554_flip_polarity(PnvI2cDev *dev) diff --git a/tests/qtest/pnv-spi-seeprom-test.c b/tests/qtest/pnv-spi-seeprom-test.c index 57f20af..600493c 100644 --- a/tests/qtest/pnv-spi-seeprom-test.c +++ b/tests/qtest/pnv-spi-seeprom-test.c @@ -92,7 +92,7 @@ static void test_spi_seeprom(const void *data) qts = qtest_initf("-machine powernv10 -smp 2,cores=2," "threads=1 -accel tcg,thread=single -nographic " "-blockdev node-name=pib_spic2,driver=file," - "filename=%s -device 25csm04,bus=pnv-spi-bus.2,cs=0," + "filename=%s -device 25csm04,bus=chip0.spi.2,cs=0," "drive=pib_spic2", tmp_path); spi_seeprom_transaction(qts, chip); qtest_quit(qts); diff --git a/tests/qtest/pnv-xive2-common.c b/tests/qtest/pnv-xive2-common.c new file mode 100644 index 0000000..bf2bce00 --- /dev/null +++ b/tests/qtest/pnv-xive2-common.c @@ -0,0 +1,190 @@ +/* + * QTest testcase for PowerNV 10 interrupt controller (xive2) + * - Common functions for XIVE2 tests + * + * Copyright (c) 2024, IBM Corporation. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#include "qemu/osdep.h" +#include "libqtest.h" + +#include "pnv-xive2-common.h" + + +static uint64_t pnv_xscom_addr(uint32_t pcba) +{ + return P10_XSCOM_BASE | ((uint64_t) pcba << 3); +} + +static uint64_t pnv_xive_xscom_addr(uint32_t reg) +{ + return pnv_xscom_addr(XIVE_XSCOM + reg); +} + +uint64_t pnv_xive_xscom_read(QTestState *qts, uint32_t reg) +{ + return qtest_readq(qts, pnv_xive_xscom_addr(reg)); +} + +void pnv_xive_xscom_write(QTestState *qts, uint32_t reg, uint64_t val) +{ + qtest_writeq(qts, pnv_xive_xscom_addr(reg), val); +} + +static void xive_get_struct(QTestState *qts, uint64_t src, void *dest, + size_t size) +{ + uint8_t *destination = (uint8_t *)dest; + size_t i; + + for (i = 0; i < size; i++) { + *(destination + i) = qtest_readb(qts, src + i); + } +} + +static void xive_copy_struct(QTestState *qts, void *src, uint64_t dest, + size_t size) +{ + uint8_t *source = (uint8_t *)src; + size_t i; + + for (i = 0; i < size; i++) { + qtest_writeb(qts, dest + i, *(source + i)); + } +} + +uint64_t xive_get_queue_addr(uint32_t end_index) +{ + return XIVE_QUEUE_MEM + (uint64_t)end_index * XIVE_QUEUE_SIZE; +} + +uint8_t get_esb(QTestState *qts, uint32_t index, uint8_t page, + uint32_t offset) +{ + uint64_t addr; + + addr = XIVE_ESB_ADDR + ((uint64_t)index << (XIVE_PAGE_SHIFT + 1)); + if (page == 1) { + addr += 1 << XIVE_PAGE_SHIFT; + } + return qtest_readb(qts, addr + offset); +} + +void set_esb(QTestState *qts, uint32_t index, uint8_t page, + uint32_t offset, uint32_t val) +{ + uint64_t addr; + + addr = XIVE_ESB_ADDR + ((uint64_t)index << (XIVE_PAGE_SHIFT + 1)); + if (page == 1) { + addr += 1 << XIVE_PAGE_SHIFT; + } + return qtest_writel(qts, addr + offset, cpu_to_be32(val)); +} + +void get_nvp(QTestState *qts, uint32_t index, Xive2Nvp* nvp) +{ + uint64_t addr = XIVE_NVP_MEM + (uint64_t)index * sizeof(Xive2Nvp); + xive_get_struct(qts, addr, nvp, sizeof(Xive2Nvp)); +} + +void set_nvp(QTestState *qts, uint32_t index, uint8_t first) +{ + uint64_t nvp_addr; + Xive2Nvp nvp; + uint64_t report_addr; + + nvp_addr = XIVE_NVP_MEM + (uint64_t)index * sizeof(Xive2Nvp); + report_addr = (XIVE_REPORT_MEM + (uint64_t)index * XIVE_REPORT_SIZE) >> 8; + + memset(&nvp, 0, sizeof(nvp)); + nvp.w0 = xive_set_field32(NVP2_W0_VALID, 0, 1); + nvp.w0 = xive_set_field32(NVP2_W0_PGOFIRST, nvp.w0, first); + nvp.w6 = xive_set_field32(NVP2_W6_REPORTING_LINE, nvp.w6, + (report_addr >> 24) & 0xfffffff); + nvp.w7 = xive_set_field32(NVP2_W7_REPORTING_LINE, nvp.w7, + report_addr & 0xffffff); + xive_copy_struct(qts, &nvp, nvp_addr, sizeof(nvp)); +} + +static uint64_t get_cl_pair_addr(Xive2Nvp *nvp) +{ + uint64_t upper = xive_get_field32(0x0fffffff, nvp->w6); + uint64_t lower = xive_get_field32(0xffffff00, nvp->w7); + return (upper << 32) | (lower << 8); +} + +void get_cl_pair(QTestState *qts, Xive2Nvp *nvp, uint8_t *cl_pair) +{ + uint64_t addr = get_cl_pair_addr(nvp); + xive_get_struct(qts, addr, cl_pair, XIVE_REPORT_SIZE); +} + +void set_cl_pair(QTestState *qts, Xive2Nvp *nvp, uint8_t *cl_pair) +{ + uint64_t addr = get_cl_pair_addr(nvp); + xive_copy_struct(qts, cl_pair, addr, XIVE_REPORT_SIZE); +} + +void set_nvg(QTestState *qts, uint32_t index, uint8_t next) +{ + uint64_t nvg_addr; + Xive2Nvgc nvg; + + nvg_addr = XIVE_NVG_MEM + (uint64_t)index * sizeof(Xive2Nvgc); + + memset(&nvg, 0, sizeof(nvg)); + nvg.w0 = xive_set_field32(NVGC2_W0_VALID, 0, 1); + nvg.w0 = xive_set_field32(NVGC2_W0_PGONEXT, nvg.w0, next); + xive_copy_struct(qts, &nvg, nvg_addr, sizeof(nvg)); +} + +void set_eas(QTestState *qts, uint32_t index, uint32_t end_index, + uint32_t data) +{ + uint64_t eas_addr; + Xive2Eas eas; + + eas_addr = XIVE_EAS_MEM + (uint64_t)index * sizeof(Xive2Eas); + + memset(&eas, 0, sizeof(eas)); + eas.w = xive_set_field64(EAS2_VALID, 0, 1); + eas.w = xive_set_field64(EAS2_END_INDEX, eas.w, end_index); + eas.w = xive_set_field64(EAS2_END_DATA, eas.w, data); + xive_copy_struct(qts, &eas, eas_addr, sizeof(eas)); +} + +void set_end(QTestState *qts, uint32_t index, uint32_t nvp_index, + uint8_t priority, bool i) +{ + uint64_t end_addr, queue_addr, queue_hi, queue_lo; + uint8_t queue_size; + Xive2End end; + + end_addr = XIVE_END_MEM + (uint64_t)index * sizeof(Xive2End); + queue_addr = xive_get_queue_addr(index); + queue_hi = (queue_addr >> 32) & END2_W2_EQ_ADDR_HI; + queue_lo = queue_addr & END2_W3_EQ_ADDR_LO; + queue_size = ctz16(XIVE_QUEUE_SIZE) - 12; + + memset(&end, 0, sizeof(end)); + end.w0 = xive_set_field32(END2_W0_VALID, 0, 1); + end.w0 = xive_set_field32(END2_W0_ENQUEUE, end.w0, 1); + end.w0 = xive_set_field32(END2_W0_UCOND_NOTIFY, end.w0, 1); + end.w0 = xive_set_field32(END2_W0_BACKLOG, end.w0, 1); + + end.w1 = xive_set_field32(END2_W1_GENERATION, 0, 1); + + end.w2 = cpu_to_be32(queue_hi); + + end.w3 = cpu_to_be32(queue_lo); + end.w3 = xive_set_field32(END2_W3_QSIZE, end.w3, queue_size); + + end.w6 = xive_set_field32(END2_W6_IGNORE, 0, i); + end.w6 = xive_set_field32(END2_W6_VP_OFFSET, end.w6, nvp_index); + + end.w7 = xive_set_field32(END2_W7_F0_PRIORITY, 0, priority); + xive_copy_struct(qts, &end, end_addr, sizeof(end)); +} + diff --git a/tests/qtest/pnv-xive2-common.h b/tests/qtest/pnv-xive2-common.h new file mode 100644 index 0000000..2077c05 --- /dev/null +++ b/tests/qtest/pnv-xive2-common.h @@ -0,0 +1,112 @@ +/* + * QTest testcase for PowerNV 10 interrupt controller (xive2) + * + * Copyright (c) 2024, IBM Corporation. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef TEST_PNV_XIVE2_COMMON_H +#define TEST_PNV_XIVE2_COMMON_H + +#define PPC_BIT(bit) (0x8000000000000000ULL >> (bit)) +#define PPC_BIT32(bit) (0x80000000 >> (bit)) +#define PPC_BIT8(bit) (0x80 >> (bit)) +#define PPC_BITMASK(bs, be) ((PPC_BIT(bs) - PPC_BIT(be)) | PPC_BIT(bs)) +#define PPC_BITMASK32(bs, be) ((PPC_BIT32(bs) - PPC_BIT32(be)) | \ + PPC_BIT32(bs)) +#include "qemu/bswap.h" +#include "hw/intc/pnv_xive2_regs.h" +#include "hw/ppc/xive_regs.h" +#include "hw/ppc/xive2_regs.h" + +/* + * sizing: + * 128 interrupts + * => ESB BAR range: 16M + * 256 ENDs + * => END BAR range: 16M + * 256 VPs + * => NVPG,NVC BAR range: 32M + */ +#define MAX_IRQS 128 +#define MAX_ENDS 256 +#define MAX_VPS 256 + +#define XIVE_PAGE_SHIFT 16 + +#define XIVE_TRIGGER_PAGE 0 +#define XIVE_EOI_PAGE 1 + +#define XIVE_IC_ADDR 0x0006030200000000ull +#define XIVE_IC_TM_INDIRECT (XIVE_IC_ADDR + (256 << XIVE_PAGE_SHIFT)) +#define XIVE_IC_BAR ((0x3ull << 62) | XIVE_IC_ADDR) +#define XIVE_TM_BAR 0xc006030203180000ull +#define XIVE_ESB_ADDR 0x0006050000000000ull +#define XIVE_ESB_BAR ((0x3ull << 62) | XIVE_ESB_ADDR) +#define XIVE_END_BAR 0xc006060000000000ull +#define XIVE_NVPG_ADDR 0x0006040000000000ull +#define XIVE_NVPG_BAR ((0x3ull << 62) | XIVE_NVPG_ADDR) +#define XIVE_NVC_ADDR 0x0006030208000000ull +#define XIVE_NVC_BAR ((0x3ull << 62) | XIVE_NVC_ADDR) + +/* + * Memory layout + * A check is done when a table is configured to ensure that the max + * size of the resource fits in the table. + */ +#define XIVE_VST_SIZE 0x10000ull /* must be at least 4k */ + +#define XIVE_MEM_START 0x10000000ull +#define XIVE_ESB_MEM XIVE_MEM_START +#define XIVE_EAS_MEM (XIVE_ESB_MEM + XIVE_VST_SIZE) +#define XIVE_END_MEM (XIVE_EAS_MEM + XIVE_VST_SIZE) +#define XIVE_NVP_MEM (XIVE_END_MEM + XIVE_VST_SIZE) +#define XIVE_NVG_MEM (XIVE_NVP_MEM + XIVE_VST_SIZE) +#define XIVE_NVC_MEM (XIVE_NVG_MEM + XIVE_VST_SIZE) +#define XIVE_SYNC_MEM (XIVE_NVC_MEM + XIVE_VST_SIZE) +#define XIVE_QUEUE_MEM (XIVE_SYNC_MEM + XIVE_VST_SIZE) +#define XIVE_QUEUE_SIZE 4096 /* per End */ +#define XIVE_REPORT_MEM (XIVE_QUEUE_MEM + XIVE_QUEUE_SIZE * MAX_VPS) +#define XIVE_REPORT_SIZE 256 /* two cache lines per NVP */ +#define XIVE_MEM_END (XIVE_REPORT_MEM + XIVE_REPORT_SIZE * MAX_VPS) + +#define P10_XSCOM_BASE 0x000603fc00000000ull +#define XIVE_XSCOM 0x2010800ull + +#define XIVE_ESB_RESET 0b00 +#define XIVE_ESB_OFF 0b01 +#define XIVE_ESB_PENDING 0b10 +#define XIVE_ESB_QUEUED 0b11 + +#define XIVE_ESB_GET 0x800 +#define XIVE_ESB_SET_PQ_00 0xc00 /* Load */ +#define XIVE_ESB_SET_PQ_01 0xd00 /* Load */ +#define XIVE_ESB_SET_PQ_10 0xe00 /* Load */ +#define XIVE_ESB_SET_PQ_11 0xf00 /* Load */ + +#define XIVE_ESB_STORE_EOI 0x400 /* Store */ + + +extern uint64_t pnv_xive_xscom_read(QTestState *qts, uint32_t reg); +extern void pnv_xive_xscom_write(QTestState *qts, uint32_t reg, uint64_t val); +extern uint64_t xive_get_queue_addr(uint32_t end_index); +extern uint8_t get_esb(QTestState *qts, uint32_t index, uint8_t page, + uint32_t offset); +extern void set_esb(QTestState *qts, uint32_t index, uint8_t page, + uint32_t offset, uint32_t val); +extern void get_nvp(QTestState *qts, uint32_t index, Xive2Nvp* nvp); +extern void set_nvp(QTestState *qts, uint32_t index, uint8_t first); +extern void get_cl_pair(QTestState *qts, Xive2Nvp *nvp, uint8_t *cl_pair); +extern void set_cl_pair(QTestState *qts, Xive2Nvp *nvp, uint8_t *cl_pair); +extern void set_nvg(QTestState *qts, uint32_t index, uint8_t next); +extern void set_eas(QTestState *qts, uint32_t index, uint32_t end_index, + uint32_t data); +extern void set_end(QTestState *qts, uint32_t index, uint32_t nvp_index, + uint8_t priority, bool i); + + +void test_flush_sync_inject(QTestState *qts); +void test_nvpg_bar(QTestState *qts); + +#endif /* TEST_PNV_XIVE2_COMMON_H */ diff --git a/tests/qtest/pnv-xive2-flush-sync.c b/tests/qtest/pnv-xive2-flush-sync.c new file mode 100644 index 0000000..142826b --- /dev/null +++ b/tests/qtest/pnv-xive2-flush-sync.c @@ -0,0 +1,205 @@ +/* + * QTest testcase for PowerNV 10 interrupt controller (xive2) + * - Test cache flush/queue sync injection + * + * Copyright (c) 2024, IBM Corporation. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#include "qemu/osdep.h" +#include "libqtest.h" + +#include "pnv-xive2-common.h" +#include "hw/intc/pnv_xive2_regs.h" +#include "hw/ppc/xive_regs.h" +#include "hw/ppc/xive2_regs.h" + +#define PNV_XIVE2_QUEUE_IPI 0x00 +#define PNV_XIVE2_QUEUE_HW 0x01 +#define PNV_XIVE2_QUEUE_NXC 0x02 +#define PNV_XIVE2_QUEUE_INT 0x03 +#define PNV_XIVE2_QUEUE_OS 0x04 +#define PNV_XIVE2_QUEUE_POOL 0x05 +#define PNV_XIVE2_QUEUE_HARD 0x06 +#define PNV_XIVE2_CACHE_ENDC 0x08 +#define PNV_XIVE2_CACHE_ESBC 0x09 +#define PNV_XIVE2_CACHE_EASC 0x0a +#define PNV_XIVE2_QUEUE_NXC_LD_LCL_NCO 0x10 +#define PNV_XIVE2_QUEUE_NXC_LD_LCL_CO 0x11 +#define PNV_XIVE2_QUEUE_NXC_ST_LCL_NCI 0x12 +#define PNV_XIVE2_QUEUE_NXC_ST_LCL_CI 0x13 +#define PNV_XIVE2_QUEUE_NXC_ST_RMT_NCI 0x14 +#define PNV_XIVE2_QUEUE_NXC_ST_RMT_CI 0x15 +#define PNV_XIVE2_CACHE_NXC 0x18 + +#define PNV_XIVE2_SYNC_IPI 0x000 +#define PNV_XIVE2_SYNC_HW 0x080 +#define PNV_XIVE2_SYNC_NxC 0x100 +#define PNV_XIVE2_SYNC_INT 0x180 +#define PNV_XIVE2_SYNC_OS_ESC 0x200 +#define PNV_XIVE2_SYNC_POOL_ESC 0x280 +#define PNV_XIVE2_SYNC_HARD_ESC 0x300 +#define PNV_XIVE2_SYNC_NXC_LD_LCL_NCO 0x800 +#define PNV_XIVE2_SYNC_NXC_LD_LCL_CO 0x880 +#define PNV_XIVE2_SYNC_NXC_ST_LCL_NCI 0x900 +#define PNV_XIVE2_SYNC_NXC_ST_LCL_CI 0x980 +#define PNV_XIVE2_SYNC_NXC_ST_RMT_NCI 0xA00 +#define PNV_XIVE2_SYNC_NXC_ST_RMT_CI 0xA80 + + +static uint64_t get_sync_addr(uint32_t src_pir, int ic_topo_id, int type) +{ + int thread_nr = src_pir & 0x7f; + uint64_t addr = XIVE_SYNC_MEM + thread_nr * 512 + ic_topo_id * 32 + type; + return addr; +} + +static uint8_t get_sync(QTestState *qts, uint32_t src_pir, int ic_topo_id, + int type) +{ + uint64_t addr = get_sync_addr(src_pir, ic_topo_id, type); + return qtest_readb(qts, addr); +} + +static void clr_sync(QTestState *qts, uint32_t src_pir, int ic_topo_id, + int type) +{ + uint64_t addr = get_sync_addr(src_pir, ic_topo_id, type); + qtest_writeb(qts, addr, 0x0); +} + +static void inject_cache_flush(QTestState *qts, int ic_topo_id, + uint64_t scom_addr) +{ + (void)ic_topo_id; + pnv_xive_xscom_write(qts, scom_addr, 0); +} + +static void inject_queue_sync(QTestState *qts, int ic_topo_id, uint64_t offset) +{ + (void)ic_topo_id; + uint64_t addr = XIVE_IC_ADDR + (VST_SYNC << XIVE_PAGE_SHIFT) + offset; + qtest_writeq(qts, addr, 0); +} + +static void inject_op(QTestState *qts, int ic_topo_id, int type) +{ + switch (type) { + case PNV_XIVE2_QUEUE_IPI: + inject_queue_sync(qts, ic_topo_id, PNV_XIVE2_SYNC_IPI); + break; + case PNV_XIVE2_QUEUE_HW: + inject_queue_sync(qts, ic_topo_id, PNV_XIVE2_SYNC_HW); + break; + case PNV_XIVE2_QUEUE_NXC: + inject_queue_sync(qts, ic_topo_id, PNV_XIVE2_SYNC_NxC); + break; + case PNV_XIVE2_QUEUE_INT: + inject_queue_sync(qts, ic_topo_id, PNV_XIVE2_SYNC_INT); + break; + case PNV_XIVE2_QUEUE_OS: + inject_queue_sync(qts, ic_topo_id, PNV_XIVE2_SYNC_OS_ESC); + break; + case PNV_XIVE2_QUEUE_POOL: + inject_queue_sync(qts, ic_topo_id, PNV_XIVE2_SYNC_POOL_ESC); + break; + case PNV_XIVE2_QUEUE_HARD: + inject_queue_sync(qts, ic_topo_id, PNV_XIVE2_SYNC_HARD_ESC); + break; + case PNV_XIVE2_CACHE_ENDC: + inject_cache_flush(qts, ic_topo_id, X_VC_ENDC_FLUSH_INJECT); + break; + case PNV_XIVE2_CACHE_ESBC: + inject_cache_flush(qts, ic_topo_id, X_VC_ESBC_FLUSH_INJECT); + break; + case PNV_XIVE2_CACHE_EASC: + inject_cache_flush(qts, ic_topo_id, X_VC_EASC_FLUSH_INJECT); + break; + case PNV_XIVE2_QUEUE_NXC_LD_LCL_NCO: + inject_queue_sync(qts, ic_topo_id, PNV_XIVE2_SYNC_NXC_LD_LCL_NCO); + break; + case PNV_XIVE2_QUEUE_NXC_LD_LCL_CO: + inject_queue_sync(qts, ic_topo_id, PNV_XIVE2_SYNC_NXC_LD_LCL_CO); + break; + case PNV_XIVE2_QUEUE_NXC_ST_LCL_NCI: + inject_queue_sync(qts, ic_topo_id, PNV_XIVE2_SYNC_NXC_ST_LCL_NCI); + break; + case PNV_XIVE2_QUEUE_NXC_ST_LCL_CI: + inject_queue_sync(qts, ic_topo_id, PNV_XIVE2_SYNC_NXC_ST_LCL_CI); + break; + case PNV_XIVE2_QUEUE_NXC_ST_RMT_NCI: + inject_queue_sync(qts, ic_topo_id, PNV_XIVE2_SYNC_NXC_ST_RMT_NCI); + break; + case PNV_XIVE2_QUEUE_NXC_ST_RMT_CI: + inject_queue_sync(qts, ic_topo_id, PNV_XIVE2_SYNC_NXC_ST_RMT_CI); + break; + case PNV_XIVE2_CACHE_NXC: + inject_cache_flush(qts, ic_topo_id, X_PC_NXC_FLUSH_INJECT); + break; + default: + g_assert_not_reached(); + break; + } +} + +const uint8_t xive_inject_tests[] = { + PNV_XIVE2_QUEUE_IPI, + PNV_XIVE2_QUEUE_HW, + PNV_XIVE2_QUEUE_NXC, + PNV_XIVE2_QUEUE_INT, + PNV_XIVE2_QUEUE_OS, + PNV_XIVE2_QUEUE_POOL, + PNV_XIVE2_QUEUE_HARD, + PNV_XIVE2_CACHE_ENDC, + PNV_XIVE2_CACHE_ESBC, + PNV_XIVE2_CACHE_EASC, + PNV_XIVE2_QUEUE_NXC_LD_LCL_NCO, + PNV_XIVE2_QUEUE_NXC_LD_LCL_CO, + PNV_XIVE2_QUEUE_NXC_ST_LCL_NCI, + PNV_XIVE2_QUEUE_NXC_ST_LCL_CI, + PNV_XIVE2_QUEUE_NXC_ST_RMT_NCI, + PNV_XIVE2_QUEUE_NXC_ST_RMT_CI, + PNV_XIVE2_CACHE_NXC, +}; + +void test_flush_sync_inject(QTestState *qts) +{ + int ic_topo_id = 0; + + /* + * Writes performed by qtest are not done in the context of a thread. + * This means that QEMU XIVE code doesn't have a way to determine what + * thread is originating the write. In order to allow for some testing, + * QEMU XIVE code will assume a PIR of 0 when unable to determine the + * source thread for cache flush and queue sync inject operations. + * See hw/intc/pnv_xive2.c: pnv_xive2_inject_notify() for details. + */ + int src_pir = 0; + int test_nr; + uint8_t byte; + + g_test_message("========================================================="); + g_test_message("Starting cache flush/queue sync injection tests..."); + + for (test_nr = 0; test_nr < sizeof(xive_inject_tests); + test_nr++) { + int op_type = xive_inject_tests[test_nr]; + + g_test_message("Running test %d", test_nr); + + /* start with status byte set to 0 */ + clr_sync(qts, src_pir, ic_topo_id, op_type); + byte = get_sync(qts, src_pir, ic_topo_id, op_type); + g_assert_cmphex(byte, ==, 0); + + /* request cache flush or queue sync operation */ + inject_op(qts, ic_topo_id, op_type); + + /* verify that status byte was written to 0xff */ + byte = get_sync(qts, src_pir, ic_topo_id, op_type); + g_assert_cmphex(byte, ==, 0xff); + + clr_sync(qts, src_pir, ic_topo_id, op_type); + } +} + diff --git a/tests/qtest/pnv-xive2-nvpg_bar.c b/tests/qtest/pnv-xive2-nvpg_bar.c new file mode 100644 index 0000000..6ac8d36 --- /dev/null +++ b/tests/qtest/pnv-xive2-nvpg_bar.c @@ -0,0 +1,152 @@ +/* + * QTest testcase for PowerNV 10 interrupt controller (xive2) + * - Test NVPG BAR MMIO operations + * + * Copyright (c) 2024, IBM Corporation. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#include "qemu/osdep.h" +#include "libqtest.h" + +#include "pnv-xive2-common.h" + +#define NVPG_BACKLOG_OP_SHIFT 10 +#define NVPG_BACKLOG_PRIO_SHIFT 4 + +#define XIVE_PRIORITY_MAX 7 + +enum NVx { + NVP, + NVG, + NVC +}; + +typedef enum { + INCR_STORE = 0b100, + INCR_LOAD = 0b000, + DECR_STORE = 0b101, + DECR_LOAD = 0b001, + READ_x = 0b010, + READ_y = 0b011, +} backlog_op; + +static uint32_t nvpg_backlog_op(QTestState *qts, backlog_op op, + enum NVx type, uint64_t index, + uint8_t priority, uint8_t delta) +{ + uint64_t addr, offset; + uint32_t count = 0; + + switch (type) { + case NVP: + addr = XIVE_NVPG_ADDR + (index << (XIVE_PAGE_SHIFT + 1)); + break; + case NVG: + addr = XIVE_NVPG_ADDR + (index << (XIVE_PAGE_SHIFT + 1)) + + (1 << XIVE_PAGE_SHIFT); + break; + case NVC: + addr = XIVE_NVC_ADDR + (index << XIVE_PAGE_SHIFT); + break; + default: + g_assert_not_reached(); + } + + offset = (op & 0b11) << NVPG_BACKLOG_OP_SHIFT; + offset |= priority << NVPG_BACKLOG_PRIO_SHIFT; + if (op >> 2) { + qtest_writeb(qts, addr + offset, delta); + } else { + count = qtest_readw(qts, addr + offset); + } + return count; +} + +void test_nvpg_bar(QTestState *qts) +{ + uint32_t nvp_target = 0x11; + uint32_t group_target = 0x17; /* size 16 */ + uint32_t vp_irq = 33, group_irq = 47; + uint32_t vp_end = 3, group_end = 97; + uint32_t vp_irq_data = 0x33333333; + uint32_t group_irq_data = 0x66666666; + uint8_t vp_priority = 0, group_priority = 5; + uint32_t vp_count[XIVE_PRIORITY_MAX + 1] = { 0 }; + uint32_t group_count[XIVE_PRIORITY_MAX + 1] = { 0 }; + uint32_t count, delta; + uint8_t i; + + g_test_message("========================================================="); + g_test_message("Testing NVPG BAR operations"); + + set_nvg(qts, group_target, 0); + set_nvp(qts, nvp_target, 0x04); + set_nvp(qts, group_target, 0x04); + + /* + * Setup: trigger a VP-specific interrupt and a group interrupt + * so that the backlog counters are initialized to something else + * than 0 for at least one priority level + */ + set_eas(qts, vp_irq, vp_end, vp_irq_data); + set_end(qts, vp_end, nvp_target, vp_priority, false /* group */); + + set_eas(qts, group_irq, group_end, group_irq_data); + set_end(qts, group_end, group_target, group_priority, true /* group */); + + get_esb(qts, vp_irq, XIVE_EOI_PAGE, XIVE_ESB_SET_PQ_00); + set_esb(qts, vp_irq, XIVE_TRIGGER_PAGE, 0, 0); + vp_count[vp_priority]++; + + get_esb(qts, group_irq, XIVE_EOI_PAGE, XIVE_ESB_SET_PQ_00); + set_esb(qts, group_irq, XIVE_TRIGGER_PAGE, 0, 0); + group_count[group_priority]++; + + /* check the initial counters */ + for (i = 0; i <= XIVE_PRIORITY_MAX; i++) { + count = nvpg_backlog_op(qts, READ_x, NVP, nvp_target, i, 0); + g_assert_cmpuint(count, ==, vp_count[i]); + + count = nvpg_backlog_op(qts, READ_y, NVG, group_target, i, 0); + g_assert_cmpuint(count, ==, group_count[i]); + } + + /* do a few ops on the VP. Counter can only be 0 and 1 */ + vp_priority = 2; + delta = 7; + nvpg_backlog_op(qts, INCR_STORE, NVP, nvp_target, vp_priority, delta); + vp_count[vp_priority] = 1; + count = nvpg_backlog_op(qts, INCR_LOAD, NVP, nvp_target, vp_priority, 0); + g_assert_cmpuint(count, ==, vp_count[vp_priority]); + count = nvpg_backlog_op(qts, READ_y, NVP, nvp_target, vp_priority, 0); + g_assert_cmpuint(count, ==, vp_count[vp_priority]); + + count = nvpg_backlog_op(qts, DECR_LOAD, NVP, nvp_target, vp_priority, 0); + g_assert_cmpuint(count, ==, vp_count[vp_priority]); + vp_count[vp_priority] = 0; + nvpg_backlog_op(qts, DECR_STORE, NVP, nvp_target, vp_priority, delta); + count = nvpg_backlog_op(qts, READ_x, NVP, nvp_target, vp_priority, 0); + g_assert_cmpuint(count, ==, vp_count[vp_priority]); + + /* do a few ops on the group */ + group_priority = 2; + delta = 9; + /* can't go negative */ + nvpg_backlog_op(qts, DECR_STORE, NVG, group_target, group_priority, delta); + count = nvpg_backlog_op(qts, READ_y, NVG, group_target, group_priority, 0); + g_assert_cmpuint(count, ==, 0); + nvpg_backlog_op(qts, INCR_STORE, NVG, group_target, group_priority, delta); + group_count[group_priority] += delta; + count = nvpg_backlog_op(qts, INCR_LOAD, NVG, group_target, + group_priority, delta); + g_assert_cmpuint(count, ==, group_count[group_priority]); + group_count[group_priority]++; + + count = nvpg_backlog_op(qts, DECR_LOAD, NVG, group_target, + group_priority, delta); + g_assert_cmpuint(count, ==, group_count[group_priority]); + group_count[group_priority]--; + count = nvpg_backlog_op(qts, READ_x, NVG, group_target, group_priority, 0); + g_assert_cmpuint(count, ==, group_count[group_priority]); +} diff --git a/tests/qtest/pnv-xive2-test.c b/tests/qtest/pnv-xive2-test.c new file mode 100644 index 0000000..5313d4e --- /dev/null +++ b/tests/qtest/pnv-xive2-test.c @@ -0,0 +1,585 @@ +/* + * QTest testcase for PowerNV 10 interrupt controller (xive2) + * - Test irq to hardware thread + * - Test 'Pull Thread Context to Odd Thread Reporting Line' + * - Test irq to hardware group + * - Test irq to hardware group going through backlog + * - Test irq to pool thread + * + * Copyright (c) 2024, IBM Corporation. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#include "qemu/osdep.h" +#include "libqtest.h" + +#include "pnv-xive2-common.h" +#include "hw/intc/pnv_xive2_regs.h" +#include "hw/ppc/xive_regs.h" +#include "hw/ppc/xive2_regs.h" + +#define SMT 4 /* some tests will break if less than 4 */ + + +static void set_table(QTestState *qts, uint64_t type, uint64_t addr) +{ + uint64_t vsd, size, log_size; + + /* + * First, let's make sure that all the resources used fit in the + * given table. + */ + switch (type) { + case VST_ESB: + size = MAX_IRQS / 4; + break; + case VST_EAS: + size = MAX_IRQS * 8; + break; + case VST_END: + size = MAX_ENDS * 32; + break; + case VST_NVP: + case VST_NVG: + case VST_NVC: + size = MAX_VPS * 32; + break; + case VST_SYNC: + size = 64 * 1024; + break; + default: + g_assert_not_reached(); + } + + g_assert_cmpuint(size, <=, XIVE_VST_SIZE); + log_size = ctzl(XIVE_VST_SIZE) - 12; + + vsd = ((uint64_t) VSD_MODE_EXCLUSIVE) << 62 | addr | log_size; + pnv_xive_xscom_write(qts, X_VC_VSD_TABLE_ADDR, type << 48); + pnv_xive_xscom_write(qts, X_VC_VSD_TABLE_DATA, vsd); + + if (type != VST_EAS && type != VST_IC && type != VST_ERQ) { + pnv_xive_xscom_write(qts, X_PC_VSD_TABLE_ADDR, type << 48); + pnv_xive_xscom_write(qts, X_PC_VSD_TABLE_DATA, vsd); + } +} + +static void set_tima8(QTestState *qts, uint32_t pir, uint32_t offset, + uint8_t b) +{ + uint64_t ic_addr; + + ic_addr = XIVE_IC_TM_INDIRECT + (pir << XIVE_PAGE_SHIFT); + qtest_writeb(qts, ic_addr + offset, b); +} + +static void set_tima32(QTestState *qts, uint32_t pir, uint32_t offset, + uint32_t l) +{ + uint64_t ic_addr; + + ic_addr = XIVE_IC_TM_INDIRECT + (pir << XIVE_PAGE_SHIFT); + qtest_writel(qts, ic_addr + offset, l); +} + +static uint8_t get_tima8(QTestState *qts, uint32_t pir, uint32_t offset) +{ + uint64_t ic_addr; + + ic_addr = XIVE_IC_TM_INDIRECT + (pir << XIVE_PAGE_SHIFT); + return qtest_readb(qts, ic_addr + offset); +} + +static uint16_t get_tima16(QTestState *qts, uint32_t pir, uint32_t offset) +{ + uint64_t ic_addr; + + ic_addr = XIVE_IC_TM_INDIRECT + (pir << XIVE_PAGE_SHIFT); + return qtest_readw(qts, ic_addr + offset); +} + +static uint32_t get_tima32(QTestState *qts, uint32_t pir, uint32_t offset) +{ + uint64_t ic_addr; + + ic_addr = XIVE_IC_TM_INDIRECT + (pir << XIVE_PAGE_SHIFT); + return qtest_readl(qts, ic_addr + offset); +} + +static void reset_pool_threads(QTestState *qts) +{ + uint8_t first_group = 0; + int i; + + for (i = 0; i < SMT; i++) { + uint32_t nvp_idx = 0x100 + i; + set_nvp(qts, nvp_idx, first_group); + set_tima32(qts, i, TM_QW2_HV_POOL + TM_WORD0, 0x000000ff); + set_tima32(qts, i, TM_QW2_HV_POOL + TM_WORD1, 0); + set_tima32(qts, i, TM_QW2_HV_POOL + TM_WORD2, TM_QW2W2_VP | nvp_idx); + } +} + +static void reset_hw_threads(QTestState *qts) +{ + uint8_t first_group = 0; + uint32_t w1 = 0x000000ff; + int i; + + if (SMT >= 4) { + /* define 2 groups of 2, part of a bigger group of size 4 */ + set_nvg(qts, 0x80, 0x02); + set_nvg(qts, 0x82, 0x02); + set_nvg(qts, 0x81, 0); + first_group = 0x01; + w1 = 0x000300ff; + } + + for (i = 0; i < SMT; i++) { + set_nvp(qts, 0x80 + i, first_group); + set_tima32(qts, i, TM_QW3_HV_PHYS + TM_WORD0, 0x00ff00ff); + set_tima32(qts, i, TM_QW3_HV_PHYS + TM_WORD1, w1); + set_tima32(qts, i, TM_QW3_HV_PHYS + TM_WORD2, 0x80000000); + } +} + +static void reset_state(QTestState *qts) +{ + size_t mem_used = XIVE_MEM_END - XIVE_MEM_START; + + qtest_memset(qts, XIVE_MEM_START, 0, mem_used); + reset_hw_threads(qts); + reset_pool_threads(qts); +} + +static void init_xive(QTestState *qts) +{ + uint64_t val1, val2, range; + + /* + * We can take a few shortcuts here, as we know the default values + * used for xive initialization + */ + + /* + * Set the BARs. + * We reuse the same values used by firmware to ease debug. + */ + pnv_xive_xscom_write(qts, X_CQ_IC_BAR, XIVE_IC_BAR); + pnv_xive_xscom_write(qts, X_CQ_TM_BAR, XIVE_TM_BAR); + + /* ESB and NVPG use 2 pages per resource. The others only one page */ + range = (MAX_IRQS << 17) >> 25; + val1 = XIVE_ESB_BAR | range; + pnv_xive_xscom_write(qts, X_CQ_ESB_BAR, val1); + + range = (MAX_ENDS << 16) >> 25; + val1 = XIVE_END_BAR | range; + pnv_xive_xscom_write(qts, X_CQ_END_BAR, val1); + + range = (MAX_VPS << 17) >> 25; + val1 = XIVE_NVPG_BAR | range; + pnv_xive_xscom_write(qts, X_CQ_NVPG_BAR, val1); + + range = (MAX_VPS << 16) >> 25; + val1 = XIVE_NVC_BAR | range; + pnv_xive_xscom_write(qts, X_CQ_NVC_BAR, val1); + + /* + * Enable hw threads. + * We check the value written. Useless with current + * implementation, but it validates the xscom read path and it's + * what the hardware procedure says + */ + val1 = 0xF000000000000000ull; /* core 0, 4 threads */ + pnv_xive_xscom_write(qts, X_TCTXT_EN0, val1); + val2 = pnv_xive_xscom_read(qts, X_TCTXT_EN0); + g_assert_cmphex(val1, ==, val2); + + /* Memory tables */ + set_table(qts, VST_ESB, XIVE_ESB_MEM); + set_table(qts, VST_EAS, XIVE_EAS_MEM); + set_table(qts, VST_END, XIVE_END_MEM); + set_table(qts, VST_NVP, XIVE_NVP_MEM); + set_table(qts, VST_NVG, XIVE_NVG_MEM); + set_table(qts, VST_NVC, XIVE_NVC_MEM); + set_table(qts, VST_SYNC, XIVE_SYNC_MEM); + + reset_hw_threads(qts); + reset_pool_threads(qts); +} + +static void test_hw_irq(QTestState *qts) +{ + uint32_t irq = 2; + uint32_t irq_data = 0x600df00d; + uint32_t end_index = 5; + uint32_t target_pir = 1; + uint32_t target_nvp = 0x80 + target_pir; + uint8_t priority = 5; + uint32_t reg32; + uint16_t reg16; + uint8_t pq, nsr, cppr; + + g_test_message("========================================================="); + g_test_message("Testing irq %d to hardware thread %d", irq, target_pir); + + /* irq config */ + set_eas(qts, irq, end_index, irq_data); + set_end(qts, end_index, target_nvp, priority, false /* group */); + + /* enable and trigger irq */ + get_esb(qts, irq, XIVE_EOI_PAGE, XIVE_ESB_SET_PQ_00); + set_esb(qts, irq, XIVE_TRIGGER_PAGE, 0, 0); + + /* check irq is raised on cpu */ + pq = get_esb(qts, irq, XIVE_EOI_PAGE, XIVE_ESB_GET); + g_assert_cmpuint(pq, ==, XIVE_ESB_PENDING); + + reg32 = get_tima32(qts, target_pir, TM_QW3_HV_PHYS + TM_WORD0); + nsr = reg32 >> 24; + cppr = (reg32 >> 16) & 0xFF; + g_assert_cmphex(nsr, ==, 0x80); + g_assert_cmphex(cppr, ==, 0xFF); + + /* ack the irq */ + reg16 = get_tima16(qts, target_pir, TM_SPC_ACK_HV_REG); + nsr = reg16 >> 8; + cppr = reg16 & 0xFF; + g_assert_cmphex(nsr, ==, 0x80); + g_assert_cmphex(cppr, ==, priority); + + /* check irq data is what was configured */ + reg32 = qtest_readl(qts, xive_get_queue_addr(end_index)); + g_assert_cmphex((reg32 & 0x7fffffff), ==, (irq_data & 0x7fffffff)); + + /* End Of Interrupt */ + set_esb(qts, irq, XIVE_EOI_PAGE, XIVE_ESB_STORE_EOI, 0); + pq = get_esb(qts, irq, XIVE_EOI_PAGE, XIVE_ESB_GET); + g_assert_cmpuint(pq, ==, XIVE_ESB_RESET); + + /* reset CPPR */ + set_tima8(qts, target_pir, TM_QW3_HV_PHYS + TM_CPPR, 0xFF); + reg32 = get_tima32(qts, target_pir, TM_QW3_HV_PHYS + TM_WORD0); + nsr = reg32 >> 24; + cppr = (reg32 >> 16) & 0xFF; + g_assert_cmphex(nsr, ==, 0x00); + g_assert_cmphex(cppr, ==, 0xFF); +} + +static void test_pool_irq(QTestState *qts) +{ + uint32_t irq = 2; + uint32_t irq_data = 0x600d0d06; + uint32_t end_index = 5; + uint32_t target_pir = 1; + uint32_t target_nvp = 0x100 + target_pir; + uint8_t priority = 5; + uint32_t reg32; + uint16_t reg16; + uint8_t pq, nsr, cppr, ipb; + + g_test_message("========================================================="); + g_test_message("Testing irq %d to pool thread %d", irq, target_pir); + + /* irq config */ + set_eas(qts, irq, end_index, irq_data); + set_end(qts, end_index, target_nvp, priority, false /* group */); + + /* enable and trigger irq */ + get_esb(qts, irq, XIVE_EOI_PAGE, XIVE_ESB_SET_PQ_00); + set_esb(qts, irq, XIVE_TRIGGER_PAGE, 0, 0); + + /* check irq is raised on cpu */ + pq = get_esb(qts, irq, XIVE_EOI_PAGE, XIVE_ESB_GET); + g_assert_cmpuint(pq, ==, XIVE_ESB_PENDING); + + /* check TIMA values in the PHYS ring (shared by POOL ring) */ + reg32 = get_tima32(qts, target_pir, TM_QW3_HV_PHYS + TM_WORD0); + nsr = reg32 >> 24; + cppr = (reg32 >> 16) & 0xFF; + g_assert_cmphex(nsr, ==, 0x40); + g_assert_cmphex(cppr, ==, 0xFF); + + /* check TIMA values in the POOL ring */ + reg32 = get_tima32(qts, target_pir, TM_QW2_HV_POOL + TM_WORD0); + nsr = reg32 >> 24; + cppr = (reg32 >> 16) & 0xFF; + ipb = (reg32 >> 8) & 0xFF; + g_assert_cmphex(nsr, ==, 0); + g_assert_cmphex(cppr, ==, 0); + g_assert_cmphex(ipb, ==, 0x80 >> priority); + + /* ack the irq */ + reg16 = get_tima16(qts, target_pir, TM_SPC_ACK_HV_REG); + nsr = reg16 >> 8; + cppr = reg16 & 0xFF; + g_assert_cmphex(nsr, ==, 0x40); + g_assert_cmphex(cppr, ==, priority); + + /* check irq data is what was configured */ + reg32 = qtest_readl(qts, xive_get_queue_addr(end_index)); + g_assert_cmphex((reg32 & 0x7fffffff), ==, (irq_data & 0x7fffffff)); + + /* check IPB is cleared in the POOL ring */ + reg32 = get_tima32(qts, target_pir, TM_QW2_HV_POOL + TM_WORD0); + ipb = (reg32 >> 8) & 0xFF; + g_assert_cmphex(ipb, ==, 0); + + /* End Of Interrupt */ + set_esb(qts, irq, XIVE_EOI_PAGE, XIVE_ESB_STORE_EOI, 0); + pq = get_esb(qts, irq, XIVE_EOI_PAGE, XIVE_ESB_GET); + g_assert_cmpuint(pq, ==, XIVE_ESB_RESET); + + /* reset CPPR */ + set_tima8(qts, target_pir, TM_QW3_HV_PHYS + TM_CPPR, 0xFF); + reg32 = get_tima32(qts, target_pir, TM_QW3_HV_PHYS + TM_WORD0); + nsr = reg32 >> 24; + cppr = (reg32 >> 16) & 0xFF; + g_assert_cmphex(nsr, ==, 0x00); + g_assert_cmphex(cppr, ==, 0xFF); +} + +#define XIVE_ODD_CL 0x80 +static void test_pull_thread_ctx_to_odd_thread_cl(QTestState *qts) +{ + uint32_t target_pir = 1; + uint32_t target_nvp = 0x80 + target_pir; + Xive2Nvp nvp; + uint8_t cl_pair[XIVE_REPORT_SIZE]; + uint32_t qw1w0, qw3w0, qw1w2, qw2w2; + uint8_t qw3b8; + uint32_t cl_word; + uint32_t word2; + + g_test_message("========================================================="); + g_test_message("Testing 'Pull Thread Context to Odd Thread Reporting " \ + "Line'"); + + /* clear odd cache line prior to pull operation */ + memset(cl_pair, 0, sizeof(cl_pair)); + get_nvp(qts, target_nvp, &nvp); + set_cl_pair(qts, &nvp, cl_pair); + + /* Read some values from TIMA that we expect to see in cacheline */ + qw1w0 = get_tima32(qts, target_pir, TM_QW1_OS + TM_WORD0); + qw3w0 = get_tima32(qts, target_pir, TM_QW3_HV_PHYS + TM_WORD0); + qw1w2 = get_tima32(qts, target_pir, TM_QW1_OS + TM_WORD2); + qw2w2 = get_tima32(qts, target_pir, TM_QW2_HV_POOL + TM_WORD2); + qw3b8 = get_tima8(qts, target_pir, TM_QW3_HV_PHYS + TM_WORD2); + + /* Execute the pull operation */ + set_tima8(qts, target_pir, TM_SPC_PULL_PHYS_CTX_OL, 0); + + /* Verify odd cache line values match TIMA after pull operation */ + get_cl_pair(qts, &nvp, cl_pair); + memcpy(&cl_word, &cl_pair[XIVE_ODD_CL + TM_QW1_OS + TM_WORD0], 4); + g_assert_cmphex(qw1w0, ==, be32_to_cpu(cl_word)); + memcpy(&cl_word, &cl_pair[XIVE_ODD_CL + TM_QW3_HV_PHYS + TM_WORD0], 4); + g_assert_cmphex(qw3w0, ==, be32_to_cpu(cl_word)); + memcpy(&cl_word, &cl_pair[XIVE_ODD_CL + TM_QW1_OS + TM_WORD2], 4); + g_assert_cmphex(qw1w2, ==, be32_to_cpu(cl_word)); + memcpy(&cl_word, &cl_pair[XIVE_ODD_CL + TM_QW2_HV_POOL + TM_WORD2], 4); + g_assert_cmphex(qw2w2, ==, be32_to_cpu(cl_word)); + g_assert_cmphex(qw3b8, ==, + cl_pair[XIVE_ODD_CL + TM_QW3_HV_PHYS + TM_WORD2]); + + /* Verify that all TIMA valid bits for target thread are cleared */ + word2 = get_tima32(qts, target_pir, TM_QW1_OS + TM_WORD2); + g_assert_cmphex(xive_get_field32(TM_QW1W2_VO, word2), ==, 0); + word2 = get_tima32(qts, target_pir, TM_QW2_HV_POOL + TM_WORD2); + g_assert_cmphex(xive_get_field32(TM_QW2W2_VP, word2), ==, 0); + word2 = get_tima32(qts, target_pir, TM_QW3_HV_PHYS + TM_WORD2); + g_assert_cmphex(xive_get_field32(TM_QW3W2_VT, word2), ==, 0); +} + +static void test_hw_group_irq(QTestState *qts) +{ + uint32_t irq = 100; + uint32_t irq_data = 0xdeadbeef; + uint32_t end_index = 23; + uint32_t chosen_one; + uint32_t target_nvp = 0x81; /* group size = 4 */ + uint8_t priority = 6; + uint32_t reg32; + uint16_t reg16; + uint8_t pq, nsr, cppr; + + g_test_message("========================================================="); + g_test_message("Testing irq %d to hardware group of size 4", irq); + + /* irq config */ + set_eas(qts, irq, end_index, irq_data); + set_end(qts, end_index, target_nvp, priority, true /* group */); + + /* enable and trigger irq */ + get_esb(qts, irq, XIVE_EOI_PAGE, XIVE_ESB_SET_PQ_00); + set_esb(qts, irq, XIVE_TRIGGER_PAGE, 0, 0); + + /* check irq is raised on cpu */ + pq = get_esb(qts, irq, XIVE_EOI_PAGE, XIVE_ESB_GET); + g_assert_cmpuint(pq, ==, XIVE_ESB_PENDING); + + /* find the targeted vCPU */ + for (chosen_one = 0; chosen_one < SMT; chosen_one++) { + reg32 = get_tima32(qts, chosen_one, TM_QW3_HV_PHYS + TM_WORD0); + nsr = reg32 >> 24; + if (nsr == 0x82) { + break; + } + } + g_assert_cmphex(chosen_one, <, SMT); + cppr = (reg32 >> 16) & 0xFF; + g_assert_cmphex(nsr, ==, 0x82); + g_assert_cmphex(cppr, ==, 0xFF); + + /* ack the irq */ + reg16 = get_tima16(qts, chosen_one, TM_SPC_ACK_HV_REG); + nsr = reg16 >> 8; + cppr = reg16 & 0xFF; + g_assert_cmphex(nsr, ==, 0x82); + g_assert_cmphex(cppr, ==, priority); + + /* check irq data is what was configured */ + reg32 = qtest_readl(qts, xive_get_queue_addr(end_index)); + g_assert_cmphex((reg32 & 0x7fffffff), ==, (irq_data & 0x7fffffff)); + + /* End Of Interrupt */ + set_esb(qts, irq, XIVE_EOI_PAGE, XIVE_ESB_STORE_EOI, 0); + pq = get_esb(qts, irq, XIVE_EOI_PAGE, XIVE_ESB_GET); + g_assert_cmpuint(pq, ==, XIVE_ESB_RESET); + + /* reset CPPR */ + set_tima8(qts, chosen_one, TM_QW3_HV_PHYS + TM_CPPR, 0xFF); + reg32 = get_tima32(qts, chosen_one, TM_QW3_HV_PHYS + TM_WORD0); + nsr = reg32 >> 24; + cppr = (reg32 >> 16) & 0xFF; + g_assert_cmphex(nsr, ==, 0x00); + g_assert_cmphex(cppr, ==, 0xFF); +} + +static void test_hw_group_irq_backlog(QTestState *qts) +{ + uint32_t irq = 31; + uint32_t irq_data = 0x01234567; + uint32_t end_index = 129; + uint32_t target_nvp = 0x81; /* group size = 4 */ + uint32_t chosen_one = 3; + uint8_t blocking_priority, priority = 3; + uint32_t reg32; + uint16_t reg16; + uint8_t pq, nsr, cppr, lsmfb, i; + + g_test_message("========================================================="); + g_test_message("Testing irq %d to hardware group of size 4 going " \ + "through backlog", + irq); + + /* + * set current priority of all threads in the group to something + * higher than what we're about to trigger + */ + blocking_priority = priority - 1; + for (i = 0; i < SMT; i++) { + set_tima8(qts, i, TM_QW3_HV_PHYS + TM_CPPR, blocking_priority); + } + + /* irq config */ + set_eas(qts, irq, end_index, irq_data); + set_end(qts, end_index, target_nvp, priority, true /* group */); + + /* enable and trigger irq */ + get_esb(qts, irq, XIVE_EOI_PAGE, XIVE_ESB_SET_PQ_00); + set_esb(qts, irq, XIVE_TRIGGER_PAGE, 0, 0); + + /* check irq is raised on cpu */ + pq = get_esb(qts, irq, XIVE_EOI_PAGE, XIVE_ESB_GET); + g_assert_cmpuint(pq, ==, XIVE_ESB_PENDING); + + /* check no interrupt is pending on the 2 possible targets */ + for (i = 0; i < SMT; i++) { + reg32 = get_tima32(qts, i, TM_QW3_HV_PHYS + TM_WORD0); + nsr = reg32 >> 24; + cppr = (reg32 >> 16) & 0xFF; + lsmfb = reg32 & 0xFF; + g_assert_cmphex(nsr, ==, 0x0); + g_assert_cmphex(cppr, ==, blocking_priority); + g_assert_cmphex(lsmfb, ==, priority); + } + + /* lower priority of one thread */ + set_tima8(qts, chosen_one, TM_QW3_HV_PHYS + TM_CPPR, priority + 1); + + /* check backlogged interrupt is presented */ + reg32 = get_tima32(qts, chosen_one, TM_QW3_HV_PHYS + TM_WORD0); + nsr = reg32 >> 24; + cppr = (reg32 >> 16) & 0xFF; + g_assert_cmphex(nsr, ==, 0x82); + g_assert_cmphex(cppr, ==, priority + 1); + + /* ack the irq */ + reg16 = get_tima16(qts, chosen_one, TM_SPC_ACK_HV_REG); + nsr = reg16 >> 8; + cppr = reg16 & 0xFF; + g_assert_cmphex(nsr, ==, 0x82); + g_assert_cmphex(cppr, ==, priority); + + /* check irq data is what was configured */ + reg32 = qtest_readl(qts, xive_get_queue_addr(end_index)); + g_assert_cmphex((reg32 & 0x7fffffff), ==, (irq_data & 0x7fffffff)); + + /* End Of Interrupt */ + set_esb(qts, irq, XIVE_EOI_PAGE, XIVE_ESB_STORE_EOI, 0); + pq = get_esb(qts, irq, XIVE_EOI_PAGE, XIVE_ESB_GET); + g_assert_cmpuint(pq, ==, XIVE_ESB_RESET); + + /* reset CPPR */ + set_tima8(qts, chosen_one, TM_QW3_HV_PHYS + TM_CPPR, 0xFF); + reg32 = get_tima32(qts, chosen_one, TM_QW3_HV_PHYS + TM_WORD0); + nsr = reg32 >> 24; + cppr = (reg32 >> 16) & 0xFF; + lsmfb = reg32 & 0xFF; + g_assert_cmphex(nsr, ==, 0x00); + g_assert_cmphex(cppr, ==, 0xFF); + g_assert_cmphex(lsmfb, ==, 0xFF); +} + +static void test_xive(void) +{ + QTestState *qts; + + qts = qtest_initf("-M powernv10 -smp %d,cores=1,threads=%d -nographic " + "-nodefaults -serial mon:stdio -S " + "-d guest_errors -trace '*xive*'", + SMT, SMT); + init_xive(qts); + + test_hw_irq(qts); + + /* omit reset_state here and use settings from test_hw_irq */ + test_pull_thread_ctx_to_odd_thread_cl(qts); + + reset_state(qts); + test_pool_irq(qts); + + reset_state(qts); + test_hw_group_irq(qts); + + reset_state(qts); + test_hw_group_irq_backlog(qts); + + reset_state(qts); + test_flush_sync_inject(qts); + + reset_state(qts); + test_nvpg_bar(qts); + + qtest_quit(qts); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + qtest_add_func("xive2", test_xive); + return g_test_run(); +} diff --git a/tests/qtest/pvpanic-pci-test.c b/tests/qtest/pvpanic-pci-test.c index dc021c2..f788a44 100644 --- a/tests/qtest/pvpanic-pci-test.c +++ b/tests/qtest/pvpanic-pci-test.c @@ -13,7 +13,7 @@ #include "qemu/osdep.h" #include "libqtest.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "libqos/pci.h" #include "libqos/pci-pc.h" #include "hw/misc/pvpanic.h" diff --git a/tests/qtest/pvpanic-test.c b/tests/qtest/pvpanic-test.c index d49d2ba..5606baf 100644 --- a/tests/qtest/pvpanic-test.c +++ b/tests/qtest/pvpanic-test.c @@ -9,7 +9,7 @@ #include "qemu/osdep.h" #include "libqtest.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "hw/misc/pvpanic.h" static void test_panic_nopause(void) diff --git a/tests/qtest/q35-test.c b/tests/qtest/q35-test.c index c922d81..62fff49 100644 --- a/tests/qtest/q35-test.c +++ b/tests/qtest/q35-test.c @@ -14,7 +14,7 @@ #include "libqos/pci.h" #include "libqos/pci-pc.h" #include "hw/pci-host/q35.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #define TSEG_SIZE_TEST_GUEST_RAM_MBYTES 128 @@ -83,7 +83,6 @@ static void test_smram_lock(void) { QPCIBus *pcibus; QPCIDevice *pcidev; - QDict *response; QTestState *qts; qts = qtest_init("-M q35"); @@ -107,10 +106,7 @@ static void test_smram_lock(void) g_assert(smram_test_bit(pcidev, MCH_HOST_BRIDGE_SMRAM_D_OPEN) == false); /* reset */ - response = qtest_qmp(qts, "{'execute': 'system_reset', 'arguments': {} }"); - g_assert(response); - g_assert(!qdict_haskey(response, "error")); - qobject_unref(response); + qtest_system_reset(qts); /* check open is settable again */ smram_set_bit(pcidev, MCH_HOST_BRIDGE_SMRAM_D_OPEN, false); @@ -194,7 +190,6 @@ static void test_smram_smbase_lock(void) { QPCIBus *pcibus; QPCIDevice *pcidev; - QDict *response; QTestState *qts; int i; @@ -237,10 +232,7 @@ static void test_smram_smbase_lock(void) } /* reset */ - response = qtest_qmp(qts, "{'execute': 'system_reset', 'arguments': {} }"); - g_assert(response); - g_assert(!qdict_haskey(response, "error")); - qobject_unref(response); + qtest_system_reset(qts); /* check RAM at SMBASE is available after reset */ g_assert_cmpint(qtest_readb(qts, SMBASE), ==, SMRAM_TEST_PATTERN); @@ -254,41 +246,6 @@ static void test_smram_smbase_lock(void) qtest_quit(qts); } -static void test_without_smram_base(void) -{ - QPCIBus *pcibus; - QPCIDevice *pcidev; - QTestState *qts; - int i; - - qts = qtest_init("-M pc-q35-4.1"); - - pcibus = qpci_new_pc(qts, NULL); - g_assert(pcibus != NULL); - - pcidev = qpci_device_find(pcibus, 0); - g_assert(pcidev != NULL); - - /* check that RAM is accessible */ - qtest_writeb(qts, SMBASE, SMRAM_TEST_PATTERN); - g_assert_cmpint(qtest_readb(qts, SMBASE), ==, SMRAM_TEST_PATTERN); - - /* check that writing to 0x9c succeeds */ - for (i = 0; i <= 0xff; i++) { - qpci_config_writeb(pcidev, MCH_HOST_BRIDGE_F_SMBASE, i); - g_assert(qpci_config_readb(pcidev, MCH_HOST_BRIDGE_F_SMBASE) == i); - } - - /* check that RAM is still accessible */ - qtest_writeb(qts, SMBASE, SMRAM_TEST_PATTERN + 1); - g_assert_cmpint(qtest_readb(qts, SMBASE), ==, (SMRAM_TEST_PATTERN + 1)); - - g_free(pcidev); - qpci_free_pc(pcibus); - - qtest_quit(qts); -} - int main(int argc, char **argv) { g_test_init(&argc, &argv, NULL); @@ -301,6 +258,6 @@ int main(int argc, char **argv) qtest_add_data_func("/q35/tseg-size/ext/16mb", &tseg_ext_16mb, test_tseg_size); qtest_add_func("/q35/smram/smbase_lock", test_smram_smbase_lock); - qtest_add_func("/q35/smram/legacy_smbase", test_without_smram_base); + return g_test_run(); } diff --git a/tests/qtest/qmp-cmd-test.c b/tests/qtest/qmp-cmd-test.c index 2c15f60..040d042 100644 --- a/tests/qtest/qmp-cmd-test.c +++ b/tests/qtest/qmp-cmd-test.c @@ -14,7 +14,7 @@ #include "libqtest.h" #include "qapi/error.h" #include "qapi/qapi-visit-introspect.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "qapi/qobject-input-visitor.h" const char common_args[] = "-nodefaults -machine none"; @@ -100,6 +100,7 @@ static bool query_is_ignored(const char *cmd) /* Success depends on target arch: */ "query-cpu-definitions", /* arm, i386, ppc, s390x */ "query-gic-capabilities", /* arm */ + "query-s390x-cpu-polarization", /* s390x */ /* Success depends on target-specific build configuration: */ "query-pci", /* CONFIG_PCI */ "x-query-virtio", /* CONFIG_VIRTIO */ diff --git a/tests/qtest/qmp-test.c b/tests/qtest/qmp-test.c index 22957fa..edf0886 100644 --- a/tests/qtest/qmp-test.c +++ b/tests/qtest/qmp-test.c @@ -14,10 +14,10 @@ #include "libqtest.h" #include "qapi/error.h" #include "qapi/qapi-visit-control.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qlist.h" +#include "qobject/qdict.h" +#include "qobject/qlist.h" #include "qapi/qobject-input-visitor.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qstring.h" const char common_args[] = "-nodefaults -machine none"; diff --git a/tests/qtest/qom-test.c b/tests/qtest/qom-test.c index d677f87..27d70bc 100644 --- a/tests/qtest/qom-test.c +++ b/tests/qtest/qom-test.c @@ -9,8 +9,8 @@ #include "qemu/osdep.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qlist.h" +#include "qobject/qdict.h" +#include "qobject/qlist.h" #include "qemu/cutils.h" #include "libqtest.h" @@ -88,6 +88,17 @@ static void test_machine(gconstpointer data) qts = qtest_initf("-machine %s", machine); + if (g_test_slow()) { + /* Make sure we can get the machine class properties: */ + g_autofree char *qom_machine = g_strdup_printf("%s-machine", machine); + + response = qtest_qmp(qts, "{ 'execute': 'qom-list-properties'," + " 'arguments': { 'typename': %s } }", + qom_machine); + g_assert(response); + qobject_unref(response); + } + test_properties(qts, "/machine", true); response = qtest_qmp(qts, "{ 'execute': 'quit' }"); diff --git a/tests/qtest/qos-test.c b/tests/qtest/qos-test.c index 114f6be..abfd4b9 100644 --- a/tests/qtest/qos-test.c +++ b/tests/qtest/qos-test.c @@ -20,7 +20,7 @@ #include <getopt.h> #include "libqtest-single.h" #include "qapi/error.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "qemu/module.h" #include "qapi/qobject-input-visitor.h" #include "qapi/qapi-visit-machine.h" @@ -103,8 +103,7 @@ static void restart_qemu_or_continue(char *path) old_path = g_strdup(path); qtest_start(path); } else { /* if cmd line is the same, reset the guest */ - qobject_unref(qmp("{ 'execute': 'system_reset' }")); - qmp_eventwait("RESET"); + qtest_system_reset(global_qtest); } } diff --git a/tests/qtest/readconfig-test.c b/tests/qtest/readconfig-test.c index 760f974..c6f32a4 100644 --- a/tests/qtest/readconfig-test.c +++ b/tests/qtest/readconfig-test.c @@ -13,10 +13,10 @@ #include "qapi/qapi-visit-machine.h" #include "qapi/qapi-visit-qom.h" #include "qapi/qapi-visit-ui.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qlist.h" +#include "qobject/qdict.h" +#include "qobject/qlist.h" #include "qapi/qobject-input-visitor.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qstring.h" #include "qemu/units.h" static QTestState *qtest_init_with_config(const char *cfgdata) diff --git a/tests/qtest/riscv-csr-test.c b/tests/qtest/riscv-csr-test.c new file mode 100644 index 0000000..ff5c29e --- /dev/null +++ b/tests/qtest/riscv-csr-test.c @@ -0,0 +1,56 @@ +/* + * QTest testcase for RISC-V CSRs + * + * Copyright (c) 2024 Syntacore. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "qemu/osdep.h" +#include "libqtest.h" + +#define CSR_MVENDORID 0xf11 +#define CSR_MISELECT 0x350 + +static void run_test_csr(void) +{ + uint64_t res; + uint64_t val = 0; + + QTestState *qts = qtest_init("-machine virt -cpu veyron-v1"); + + res = qtest_csr_call(qts, "get_csr", 0, CSR_MVENDORID, &val); + + g_assert_cmpint(res, ==, 0); + g_assert_cmpint(val, ==, 0x61f); + + val = 0xff; + res = qtest_csr_call(qts, "set_csr", 0, CSR_MISELECT, &val); + + g_assert_cmpint(res, ==, 0); + + val = 0; + res = qtest_csr_call(qts, "get_csr", 0, CSR_MISELECT, &val); + + g_assert_cmpint(res, ==, 0); + g_assert_cmpint(val, ==, 0xff); + + qtest_quit(qts); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + qtest_add_func("/cpu/csr", run_test_csr); + + return g_test_run(); +} diff --git a/tests/qtest/riscv-iommu-test.c b/tests/qtest/riscv-iommu-test.c new file mode 100644 index 0000000..df0c781 --- /dev/null +++ b/tests/qtest/riscv-iommu-test.c @@ -0,0 +1,210 @@ +/* + * QTest testcase for RISC-V IOMMU + * + * Copyright (c) 2024 Ventana Micro Systems Inc. + * + * This work is licensed under the terms of the GNU GPL, version 2 or (at your + * option) any later version. See the COPYING file in the top-level directory. + * + */ + +#include "qemu/osdep.h" +#include "libqtest-single.h" +#include "qemu/module.h" +#include "libqos/qgraph.h" +#include "libqos/riscv-iommu.h" +#include "hw/pci/pci_regs.h" + +static uint32_t riscv_iommu_read_reg32(QRISCVIOMMU *r_iommu, int reg_offset) +{ + return qpci_io_readl(&r_iommu->dev, r_iommu->reg_bar, reg_offset); +} + +static uint64_t riscv_iommu_read_reg64(QRISCVIOMMU *r_iommu, int reg_offset) +{ + return qpci_io_readq(&r_iommu->dev, r_iommu->reg_bar, reg_offset); +} + +static void riscv_iommu_write_reg32(QRISCVIOMMU *r_iommu, int reg_offset, + uint32_t val) +{ + qpci_io_writel(&r_iommu->dev, r_iommu->reg_bar, reg_offset, val); +} + +static void riscv_iommu_write_reg64(QRISCVIOMMU *r_iommu, int reg_offset, + uint64_t val) +{ + qpci_io_writeq(&r_iommu->dev, r_iommu->reg_bar, reg_offset, val); +} + +static void test_pci_config(void *obj, void *data, QGuestAllocator *t_alloc) +{ + QRISCVIOMMU *r_iommu = obj; + QPCIDevice *dev = &r_iommu->dev; + uint16_t vendorid, deviceid, classid; + + vendorid = qpci_config_readw(dev, PCI_VENDOR_ID); + deviceid = qpci_config_readw(dev, PCI_DEVICE_ID); + classid = qpci_config_readw(dev, PCI_CLASS_DEVICE); + + g_assert_cmpuint(vendorid, ==, RISCV_IOMMU_PCI_VENDOR_ID); + g_assert_cmpuint(deviceid, ==, RISCV_IOMMU_PCI_DEVICE_ID); + g_assert_cmpuint(classid, ==, RISCV_IOMMU_PCI_DEVICE_CLASS); +} + +static void test_reg_reset(void *obj, void *data, QGuestAllocator *t_alloc) +{ + QRISCVIOMMU *r_iommu = obj; + uint64_t cap; + uint32_t reg; + + cap = riscv_iommu_read_reg64(r_iommu, RISCV_IOMMU_REG_CAP); + g_assert_cmpuint(cap & RISCV_IOMMU_CAP_VERSION, ==, 0x10); + + reg = riscv_iommu_read_reg32(r_iommu, RISCV_IOMMU_REG_CQCSR); + g_assert_cmpuint(reg & RISCV_IOMMU_CQCSR_CQEN, ==, 0); + g_assert_cmpuint(reg & RISCV_IOMMU_CQCSR_CIE, ==, 0); + g_assert_cmpuint(reg & RISCV_IOMMU_CQCSR_CQON, ==, 0); + g_assert_cmpuint(reg & RISCV_IOMMU_CQCSR_BUSY, ==, 0); + + reg = riscv_iommu_read_reg32(r_iommu, RISCV_IOMMU_REG_FQCSR); + g_assert_cmpuint(reg & RISCV_IOMMU_FQCSR_FQEN, ==, 0); + g_assert_cmpuint(reg & RISCV_IOMMU_FQCSR_FIE, ==, 0); + g_assert_cmpuint(reg & RISCV_IOMMU_FQCSR_FQON, ==, 0); + g_assert_cmpuint(reg & RISCV_IOMMU_FQCSR_BUSY, ==, 0); + + reg = riscv_iommu_read_reg32(r_iommu, RISCV_IOMMU_REG_PQCSR); + g_assert_cmpuint(reg & RISCV_IOMMU_PQCSR_PQEN, ==, 0); + g_assert_cmpuint(reg & RISCV_IOMMU_PQCSR_PIE, ==, 0); + g_assert_cmpuint(reg & RISCV_IOMMU_PQCSR_PQON, ==, 0); + g_assert_cmpuint(reg & RISCV_IOMMU_PQCSR_BUSY, ==, 0); + + reg = riscv_iommu_read_reg32(r_iommu, RISCV_IOMMU_REG_DDTP); + g_assert_cmpuint(reg & RISCV_IOMMU_DDTP_BUSY, ==, 0); + g_assert_cmpuint(reg & RISCV_IOMMU_DDTP_MODE, ==, + RISCV_IOMMU_DDTP_MODE_OFF); + + reg = riscv_iommu_read_reg32(r_iommu, RISCV_IOMMU_REG_IPSR); + g_assert_cmpuint(reg, ==, 0); +} + +/* + * Common timeout-based poll for CQCSR, FQCSR and PQCSR. All + * their ON bits are mapped as RISCV_IOMMU_QUEUE_ACTIVE (16), + */ +static void qtest_wait_for_queue_active(QRISCVIOMMU *r_iommu, + uint32_t queue_csr) +{ + QTestState *qts = global_qtest; + guint64 timeout_us = 2 * 1000 * 1000; + gint64 start_time = g_get_monotonic_time(); + uint32_t reg; + + for (;;) { + qtest_clock_step(qts, 100); + + reg = riscv_iommu_read_reg32(r_iommu, queue_csr); + if (reg & RISCV_IOMMU_QUEUE_ACTIVE) { + break; + } + g_assert(g_get_monotonic_time() - start_time <= timeout_us); + } +} + +/* + * Goes through the queue activation procedures of chapter 6.2, + * "Guidelines for initialization", of the RISCV-IOMMU spec. + */ +static void test_iommu_init_queues(void *obj, void *data, + QGuestAllocator *t_alloc) +{ + QRISCVIOMMU *r_iommu = obj; + uint64_t reg64, q_addr; + uint32_t reg; + int k = 2; + + reg64 = riscv_iommu_read_reg64(r_iommu, RISCV_IOMMU_REG_CAP); + g_assert_cmpuint(reg64 & RISCV_IOMMU_CAP_VERSION, ==, 0x10); + + /* + * Program the command queue. Write 0xF to civ, fiv, pmiv and + * piv. With the current PCI device impl we expect 2 writable + * bits for each (k = 2) since we have N = 4 total vectors (2^k). + */ + riscv_iommu_write_reg32(r_iommu, RISCV_IOMMU_REG_IVEC, 0xFFFF); + reg = riscv_iommu_read_reg32(r_iommu, RISCV_IOMMU_REG_IVEC); + g_assert_cmpuint(reg & RISCV_IOMMU_REG_IVEC_CIV, ==, 0x3); + g_assert_cmpuint(reg & RISCV_IOMMU_REG_IVEC_FIV, ==, 0x30); + g_assert_cmpuint(reg & RISCV_IOMMU_REG_IVEC_PMIV, ==, 0x300); + g_assert_cmpuint(reg & RISCV_IOMMU_REG_IVEC_PIV, ==, 0x3000); + + /* Alloc a 4*16 bytes buffer and use it to set cqb */ + q_addr = guest_alloc(t_alloc, 4 * 16); + reg64 = 0; + deposit64(reg64, RISCV_IOMMU_CQB_PPN_START, + RISCV_IOMMU_CQB_PPN_LEN, q_addr); + deposit64(reg64, RISCV_IOMMU_CQB_LOG2SZ_START, + RISCV_IOMMU_CQB_LOG2SZ_LEN, k - 1); + riscv_iommu_write_reg64(r_iommu, RISCV_IOMMU_REG_CQB, reg64); + + /* cqt = 0, cqcsr.cqen = 1, poll cqcsr.cqon until it reads 1 */ + riscv_iommu_write_reg32(r_iommu, RISCV_IOMMU_REG_CQT, 0); + + reg = riscv_iommu_read_reg32(r_iommu, RISCV_IOMMU_REG_CQCSR); + reg |= RISCV_IOMMU_CQCSR_CQEN; + riscv_iommu_write_reg32(r_iommu, RISCV_IOMMU_REG_CQCSR, reg); + + qtest_wait_for_queue_active(r_iommu, RISCV_IOMMU_REG_CQCSR); + + /* + * Program the fault queue. Alloc a 4*32 bytes (instead of 4*16) + * buffer and use it to set fqb. + */ + q_addr = guest_alloc(t_alloc, 4 * 32); + reg64 = 0; + deposit64(reg64, RISCV_IOMMU_FQB_PPN_START, + RISCV_IOMMU_FQB_PPN_LEN, q_addr); + deposit64(reg64, RISCV_IOMMU_FQB_LOG2SZ_START, + RISCV_IOMMU_FQB_LOG2SZ_LEN, k - 1); + riscv_iommu_write_reg64(r_iommu, RISCV_IOMMU_REG_FQB, reg64); + + /* fqt = 0, fqcsr.fqen = 1, poll fqcsr.fqon until it reads 1 */ + riscv_iommu_write_reg32(r_iommu, RISCV_IOMMU_REG_FQT, 0); + + reg = riscv_iommu_read_reg32(r_iommu, RISCV_IOMMU_REG_FQCSR); + reg |= RISCV_IOMMU_FQCSR_FQEN; + riscv_iommu_write_reg32(r_iommu, RISCV_IOMMU_REG_FQCSR, reg); + + qtest_wait_for_queue_active(r_iommu, RISCV_IOMMU_REG_FQCSR); + + /* + * Program the page-request queue. Alloc a 4*16 bytes buffer + * and use it to set pqb. + */ + q_addr = guest_alloc(t_alloc, 4 * 16); + reg64 = 0; + deposit64(reg64, RISCV_IOMMU_PQB_PPN_START, + RISCV_IOMMU_PQB_PPN_LEN, q_addr); + deposit64(reg64, RISCV_IOMMU_PQB_LOG2SZ_START, + RISCV_IOMMU_PQB_LOG2SZ_LEN, k - 1); + riscv_iommu_write_reg64(r_iommu, RISCV_IOMMU_REG_PQB, reg64); + + /* pqt = 0, pqcsr.pqen = 1, poll pqcsr.pqon until it reads 1 */ + riscv_iommu_write_reg32(r_iommu, RISCV_IOMMU_REG_PQT, 0); + + reg = riscv_iommu_read_reg32(r_iommu, RISCV_IOMMU_REG_PQCSR); + reg |= RISCV_IOMMU_PQCSR_PQEN; + riscv_iommu_write_reg32(r_iommu, RISCV_IOMMU_REG_PQCSR, reg); + + qtest_wait_for_queue_active(r_iommu, RISCV_IOMMU_REG_PQCSR); +} + +static void register_riscv_iommu_test(void) +{ + qos_add_test("pci_config", "riscv-iommu-pci", test_pci_config, NULL); + qos_add_test("reg_reset", "riscv-iommu-pci", test_reg_reset, NULL); + qos_add_test("iommu_init_queues", "riscv-iommu-pci", + test_iommu_init_queues, NULL); +} + +libqos_init(register_riscv_iommu_test); diff --git a/tests/qtest/rs5c372-test.c b/tests/qtest/rs5c372-test.c new file mode 100644 index 0000000..0f6a9b6 --- /dev/null +++ b/tests/qtest/rs5c372-test.c @@ -0,0 +1,43 @@ +/* + * QTest testcase for the RS5C372 RTC + * + * Copyright (c) 2025 Bernhard Beschow <shentey@gmail.com> + * + * Based on ds1338-test.c + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/bcd.h" +#include "libqos/i2c.h" + +#define RS5C372_ADDR 0x32 + +static void rs5c372_read_date(void *obj, void *data, QGuestAllocator *alloc) +{ + QI2CDevice *i2cdev = obj; + + uint8_t resp[0x10]; + time_t now = time(NULL); + struct tm *utc = gmtime(&now); + + i2c_read_block(i2cdev, 0, resp, sizeof(resp)); + + /* check retrieved time against local time */ + g_assert_cmpuint(from_bcd(resp[5]), == , utc->tm_mday); + g_assert_cmpuint(from_bcd(resp[6]), == , 1 + utc->tm_mon); + g_assert_cmpuint(2000 + from_bcd(resp[7]), == , 1900 + utc->tm_year); +} + +static void rs5c372_register_nodes(void) +{ + QOSGraphEdgeOptions opts = { }; + add_qi2c_address(&opts, &(QI2CAddress) { RS5C372_ADDR }); + + qos_node_create_driver("rs5c372", i2c_device_create); + qos_node_consumes("rs5c372", "i2c-bus", &opts); + qos_add_test("read_date", "rs5c372", rs5c372_read_date, NULL); +} + +libqos_init(rs5c372_register_nodes); diff --git a/tests/qtest/rtl8139-test.c b/tests/qtest/rtl8139-test.c index eedf90f..55f671f 100644 --- a/tests/qtest/rtl8139-test.c +++ b/tests/qtest/rtl8139-test.c @@ -65,7 +65,7 @@ PORT(IntrMask, w, 0x3c) PORT(IntrStatus, w, 0x3E) PORT(TimerInt, l, 0x54) -#define fatal(...) do { g_test_message(__VA_ARGS__); g_assert(0); } while (0) +#define fatal(...) do { g_test_message(__VA_ARGS__); g_assert_not_reached(); } while (0) static void test_timer(void) { diff --git a/tests/qtest/stm32l4x5.h b/tests/qtest/stm32l4x5.h new file mode 100644 index 0000000..2d21cc6 --- /dev/null +++ b/tests/qtest/stm32l4x5.h @@ -0,0 +1,42 @@ +/* + * QTest testcase header for STM32L4X5 : + * used for consolidating common objects in stm32l4x5_*-test.c + * + * Copyright (c) 2024 Arnaud Minier <arnaud.minier@telecom-paris.fr> + * Copyright (c) 2024 Inès Varhol <ines.varhol@telecom-paris.fr> + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "libqtest.h" + +/* copied from clock.h */ +#define CLOCK_PERIOD_1SEC (1000000000llu << 32) +#define CLOCK_PERIOD_FROM_HZ(hz) (((hz) != 0) ? CLOCK_PERIOD_1SEC / (hz) : 0u) +/* + * MSI (4 MHz) is used as system clock source after startup + * from Reset. + * AHB, APB1 and APB2 prescalers are set to 1 at reset. + */ +#define SYSCLK_PERIOD CLOCK_PERIOD_FROM_HZ(4000000) +#define RCC_AHB2ENR 0x4002104C +#define RCC_APB1ENR1 0x40021058 +#define RCC_APB1ENR2 0x4002105C +#define RCC_APB2ENR 0x40021060 + + +static inline uint64_t get_clock_period(QTestState *qts, const char *path) +{ + uint64_t clock_period = 0; + QDict *r; + + r = qtest_qmp(qts, "{ 'execute': 'qom-get', 'arguments':" + " { 'path': %s, 'property': 'qtest-clock-period'} }", path); + g_assert_false(qdict_haskey(r, "error")); + clock_period = qdict_get_int(r, "return"); + qobject_unref(r); + return clock_period; +} + + diff --git a/tests/qtest/stm32l4x5_gpio-test.c b/tests/qtest/stm32l4x5_gpio-test.c index 72a7823..3c6ea71 100644 --- a/tests/qtest/stm32l4x5_gpio-test.c +++ b/tests/qtest/stm32l4x5_gpio-test.c @@ -10,6 +10,7 @@ #include "qemu/osdep.h" #include "libqtest-single.h" +#include "stm32l4x5.h" #define GPIO_BASE_ADDR 0x48000000 #define GPIO_SIZE 0x400 @@ -168,14 +169,6 @@ static uint32_t reset(uint32_t gpio, unsigned int offset) return 0x0; } -static void system_reset(void) -{ - QDict *r; - r = qtest_qmp(global_qtest, "{'execute': 'system_reset'}"); - g_assert_false(qdict_haskey(r, "error")); - qobject_unref(r); -} - static void test_idr_reset_value(void) { /* @@ -213,7 +206,7 @@ static void test_idr_reset_value(void) gpio_writel(GPIO_H, OTYPER, 0xDEADBEEF); gpio_writel(GPIO_H, PUPDR, 0xDEADBEEF); - system_reset(); + qtest_system_reset(global_qtest); uint32_t moder = gpio_readl(GPIO_A, MODER); uint32_t odr = gpio_readl(GPIO_A, ODR); @@ -505,6 +498,26 @@ static void test_bsrr_brr(const void *data) gpio_writel(gpio, ODR, reset(gpio, ODR)); } +static void test_clock_enable(void) +{ + /* + * For each GPIO, enable its clock in RCC + * and check that its clock period changes to SYSCLK_PERIOD + */ + unsigned int gpio_id; + + for (uint32_t gpio = GPIO_A; gpio <= GPIO_H; gpio += GPIO_B - GPIO_A) { + gpio_id = get_gpio_id(gpio); + g_autofree char *path = g_strdup_printf("/machine/soc/gpio%c/clk", + gpio_id + 'a'); + g_assert_cmpuint(get_clock_period(global_qtest, path), ==, 0); + /* Enable the gpio clock */ + writel(RCC_AHB2ENR, readl(RCC_AHB2ENR) | (0x1 << gpio_id)); + g_assert_cmpuint(get_clock_period(global_qtest, path), ==, + SYSCLK_PERIOD); + } +} + int main(int argc, char **argv) { int ret; @@ -556,6 +569,8 @@ int main(int argc, char **argv) qtest_add_data_func("stm32l4x5/gpio/test_bsrr_brr2", test_data(GPIO_D, 0), test_bsrr_brr); + qtest_add_func("stm32l4x5/gpio/test_clock_enable", + test_clock_enable); qtest_start("-machine b-l475e-iot01a"); ret = g_test_run(); diff --git a/tests/qtest/stm32l4x5_syscfg-test.c b/tests/qtest/stm32l4x5_syscfg-test.c index 258417c..376c80e 100644 --- a/tests/qtest/stm32l4x5_syscfg-test.c +++ b/tests/qtest/stm32l4x5_syscfg-test.c @@ -10,6 +10,7 @@ #include "qemu/osdep.h" #include "libqtest-single.h" +#include "stm32l4x5.h" #define SYSCFG_BASE_ADDR 0x40010000 #define SYSCFG_MEMRMP 0x00 @@ -26,7 +27,9 @@ #define INVALID_ADDR 0x2C /* SoC forwards GPIOs to SysCfg */ -#define SYSCFG "/machine/soc" +#define SOC "/machine/soc" +#define SYSCFG "/machine/soc/syscfg" +#define SYSCFG_CLK "/machine/soc/syscfg/clk" #define EXTI "/machine/soc/exti" static void syscfg_writel(unsigned int offset, uint32_t value) @@ -41,15 +44,7 @@ static uint32_t syscfg_readl(unsigned int offset) static void syscfg_set_irq(int num, int level) { - qtest_set_irq_in(global_qtest, SYSCFG, NULL, num, level); -} - -static void system_reset(void) -{ - QDict *response; - response = qtest_qmp(global_qtest, "{'execute': 'system_reset'}"); - g_assert(qdict_haskey(response, "return")); - qobject_unref(response); + qtest_set_irq_in(global_qtest, SOC, NULL, num, level); } static void test_reset(void) @@ -179,7 +174,7 @@ static void test_set_only_bits(void) syscfg_writel(SYSCFG_SWPR2, 0x00000000); g_assert_cmphex(syscfg_readl(SYSCFG_SWPR2), ==, 0xFFFFFFFF); - system_reset(); + qtest_system_reset(global_qtest); } static void test_clear_only_bits(void) @@ -191,7 +186,7 @@ static void test_clear_only_bits(void) syscfg_writel(SYSCFG_CFGR1, 0x00000001); g_assert_cmphex(syscfg_readl(SYSCFG_CFGR1), ==, 0x00000000); - system_reset(); + qtest_system_reset(global_qtest); } static void test_interrupt(void) @@ -301,6 +296,17 @@ static void test_irq_gpio_multiplexer(void) syscfg_writel(SYSCFG_EXTICR1, 0x00000000); } +static void test_clock_enable(void) +{ + g_assert_cmpuint(get_clock_period(global_qtest, SYSCFG_CLK), ==, 0); + + /* Enable SYSCFG clock */ + writel(RCC_APB2ENR, readl(RCC_APB2ENR) | (0x1 << 0)); + + g_assert_cmpuint(get_clock_period(global_qtest, SYSCFG_CLK), ==, + SYSCLK_PERIOD); +} + int main(int argc, char **argv) { int ret; @@ -325,6 +331,8 @@ int main(int argc, char **argv) test_irq_pin_multiplexer); qtest_add_func("stm32l4x5/syscfg/test_irq_gpio_multiplexer", test_irq_gpio_multiplexer); + qtest_add_func("stm32l4x5/syscfg/test_clock_enable", + test_clock_enable); qtest_start("-machine b-l475e-iot01a"); ret = g_test_run(); diff --git a/tests/qtest/stm32l4x5_usart-test.c b/tests/qtest/stm32l4x5_usart-test.c index c175ff3..98a7472 100644 --- a/tests/qtest/stm32l4x5_usart-test.c +++ b/tests/qtest/stm32l4x5_usart-test.c @@ -12,6 +12,7 @@ #include "libqtest.h" #include "hw/misc/stm32l4x5_rcc_internals.h" #include "hw/registerfields.h" +#include "stm32l4x5.h" #define RCC_BASE_ADDR 0x40021000 /* Use USART 1 ADDR, assume the others work the same */ @@ -36,6 +37,8 @@ REG32(GTPR, 0x10) REG32(RTOR, 0x14) REG32(RQR, 0x18) REG32(ISR, 0x1C) + FIELD(ISR, REACK, 22, 1) + FIELD(ISR, TEACK, 21, 1) FIELD(ISR, TXE, 7, 1) FIELD(ISR, RXNE, 5, 1) FIELD(ISR, ORE, 3, 1) @@ -191,7 +194,7 @@ static void init_uart(QTestState *qts) /* Enable the transmitter, the receiver and the USART. */ qtest_writel(qts, (USART1_BASE_ADDR + A_CR1), - R_CR1_UE_MASK | R_CR1_RE_MASK | R_CR1_TE_MASK); + cr1 | R_CR1_UE_MASK | R_CR1_RE_MASK | R_CR1_TE_MASK); } static void test_write_read(void) @@ -298,10 +301,65 @@ static void test_send_str(void) qtest_quit(qts); } -int main(int argc, char **argv) +static void test_ack(void) { - int ret; + uint32_t cr1; + uint32_t isr; + QTestState *qts = qtest_init("-M b-l475e-iot01a"); + + init_uart(qts); + + cr1 = qtest_readl(qts, (USART1_BASE_ADDR + A_CR1)); + + /* Disable the transmitter and receiver. */ + qtest_writel(qts, (USART1_BASE_ADDR + A_CR1), + cr1 & ~(R_CR1_RE_MASK | R_CR1_TE_MASK)); + + /* Test ISR ACK for transmitter and receiver disabled */ + isr = qtest_readl(qts, (USART1_BASE_ADDR + A_ISR)); + g_assert_false(isr & R_ISR_TEACK_MASK); + g_assert_false(isr & R_ISR_REACK_MASK); + + /* Enable the transmitter and receiver. */ + qtest_writel(qts, (USART1_BASE_ADDR + A_CR1), + cr1 | (R_CR1_RE_MASK | R_CR1_TE_MASK)); + + /* Test ISR ACK for transmitter and receiver disabled */ + isr = qtest_readl(qts, (USART1_BASE_ADDR + A_ISR)); + g_assert_true(isr & R_ISR_TEACK_MASK); + g_assert_true(isr & R_ISR_REACK_MASK); + + qtest_quit(qts); +} +static void check_clock(QTestState *qts, const char *path, uint32_t rcc_reg, + uint32_t reg_offset) +{ + g_assert_cmpuint(get_clock_period(qts, path), ==, 0); + qtest_writel(qts, rcc_reg, qtest_readl(qts, rcc_reg) | (0x1 << reg_offset)); + g_assert_cmpuint(get_clock_period(qts, path), ==, SYSCLK_PERIOD); +} + +static void test_clock_enable(void) +{ + /* + * For each USART device, enable its clock in RCC + * and check that its clock frequency is SYSCLK_PERIOD + */ + QTestState *qts = qtest_init("-M b-l475e-iot01a"); + + check_clock(qts, "machine/soc/usart[0]/clk", RCC_APB2ENR, 14); + check_clock(qts, "machine/soc/usart[1]/clk", RCC_APB1ENR1, 17); + check_clock(qts, "machine/soc/usart[2]/clk", RCC_APB1ENR1, 18); + check_clock(qts, "machine/soc/uart[0]/clk", RCC_APB1ENR1, 19); + check_clock(qts, "machine/soc/uart[1]/clk", RCC_APB1ENR1, 20); + check_clock(qts, "machine/soc/lpuart1/clk", RCC_APB1ENR2, 0); + + qtest_quit(qts); +} + +int main(int argc, char **argv) +{ g_test_init(&argc, &argv, NULL); g_test_set_nonfatal_assertions(); @@ -310,8 +368,8 @@ int main(int argc, char **argv) qtest_add_func("stm32l4x5/usart/send_char", test_send_char); qtest_add_func("stm32l4x5/usart/receive_str", test_receive_str); qtest_add_func("stm32l4x5/usart/send_str", test_send_str); - ret = g_test_run(); - - return ret; + qtest_add_func("stm32l4x5/usart/ack", test_ack); + qtest_add_func("stm32l4x5/usart/clock_enable", test_clock_enable); + return g_test_run(); } diff --git a/tests/qtest/tco-test.c b/tests/qtest/tco-test.c index 0547d41..20ccefa 100644 --- a/tests/qtest/tco-test.c +++ b/tests/qtest/tco-test.c @@ -12,7 +12,7 @@ #include "libqtest.h" #include "libqos/pci.h" #include "libqos/pci-pc.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "hw/pci/pci_regs.h" #include "hw/southbridge/ich9.h" #include "hw/acpi/ich9.h" diff --git a/tests/qtest/test-filter-mirror.c b/tests/qtest/test-filter-mirror.c index f3865f7..723d2c2 100644 --- a/tests/qtest/test-filter-mirror.c +++ b/tests/qtest/test-filter-mirror.c @@ -10,7 +10,7 @@ #include "qemu/osdep.h" #include "libqtest.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "qemu/iov.h" #include "qemu/sockets.h" #include "qemu/error-report.h" diff --git a/tests/qtest/test-filter-redirector.c b/tests/qtest/test-filter-redirector.c index a77d5fd..a996a80 100644 --- a/tests/qtest/test-filter-redirector.c +++ b/tests/qtest/test-filter-redirector.c @@ -52,7 +52,7 @@ #include "qemu/osdep.h" #include "libqtest.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "qemu/iov.h" #include "qemu/sockets.h" #include "qemu/error-report.h" diff --git a/tests/qtest/test-netfilter.c b/tests/qtest/test-netfilter.c index b09ef7f..326d4bd 100644 --- a/tests/qtest/test-netfilter.c +++ b/tests/qtest/test-netfilter.c @@ -10,7 +10,7 @@ #include "qemu/osdep.h" #include "libqtest-single.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" /* add a netfilter to a netdev and then remove it */ static void add_one_netfilter(void) diff --git a/tests/qtest/test-x86-cpuid-compat.c b/tests/qtest/test-x86-cpuid-compat.c index b9e7e5e..456e2af 100644 --- a/tests/qtest/test-x86-cpuid-compat.c +++ b/tests/qtest/test-x86-cpuid-compat.c @@ -1,8 +1,8 @@ #include "qemu/osdep.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qlist.h" -#include "qapi/qmp/qnum.h" -#include "qapi/qmp/qbool.h" +#include "qobject/qdict.h" +#include "qobject/qlist.h" +#include "qobject/qnum.h" +#include "qobject/qbool.h" #include "libqtest-single.h" static char *get_cpu0_qom_path(void) @@ -193,7 +193,6 @@ static void add_feature_test(const char *name, const char *cpu, args->bitnr = bitnr; args->expected_value = expected_value; qtest_add_data_func(name, args, test_feature_flag); - return; } static void test_plus_minus_subprocess(void) @@ -357,19 +356,6 @@ int main(int argc, char **argv) "486", "xstore=on", "pc-i440fx-2.7", "xlevel2", 0); } - /* - * QEMU 2.3.0 had auto-level enabled for CPUID[7], already, - * and the compat code that sets default level shouldn't - * disable the auto-level=7 code: - */ - if (qtest_has_machine("pc-i440fx-2.3")) { - add_cpuid_test("x86/cpuid/auto-level7/pc-i440fx-2.3/off", - "Penryn", NULL, "pc-i440fx-2.3", - "level", 4); - add_cpuid_test("x86/cpuid/auto-level7/pc-i440fx-2.3/on", - "Penryn", "erms=on", "pc-i440fx-2.3", - "level", 7); - } if (qtest_has_machine("pc-i440fx-2.9")) { add_cpuid_test("x86/cpuid/auto-level7/pc-i440fx-2.9/off", "Conroe", NULL, "pc-i440fx-2.9", @@ -379,25 +365,6 @@ int main(int argc, char **argv) "level", 10); } - /* - * xlevel doesn't have any feature that triggers auto-level - * code on old machine-types. Just check that the compat code - * is working correctly: - */ - if (qtest_has_machine("pc-i440fx-2.3")) { - add_cpuid_test("x86/cpuid/xlevel-compat/pc-i440fx-2.3", - "SandyBridge", NULL, "pc-i440fx-2.3", - "xlevel", 0x8000000a); - } - if (qtest_has_machine("pc-i440fx-2.4")) { - add_cpuid_test("x86/cpuid/xlevel-compat/pc-i440fx-2.4/npt-off", - "SandyBridge", NULL, "pc-i440fx-2.4", - "xlevel", 0x80000008); - add_cpuid_test("x86/cpuid/xlevel-compat/pc-i440fx-2.4/npt-on", - "SandyBridge", "svm=on,npt=on", "pc-i440fx-2.4", - "xlevel", 0x80000008); - } - /* Test feature parsing */ add_feature_test("x86/cpuid/features/plus", "486", "+arat", diff --git a/tests/qtest/tmp105-test.c b/tests/qtest/tmp105-test.c index 3678646..3b114a5 100644 --- a/tests/qtest/tmp105-test.c +++ b/tests/qtest/tmp105-test.c @@ -12,7 +12,7 @@ #include "libqtest-single.h" #include "libqos/qgraph.h" #include "libqos/i2c.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "hw/sensor/tmp105_regs.h" #define TMP105_TEST_ID "tmp105-test" @@ -100,9 +100,9 @@ static void send_and_receive(void *obj, void *data, QGuestAllocator *alloc) g_assert_cmphex(value, ==, 0x14f0); i2c_set16(i2cdev, TMP105_REG_T_LOW, 0x1234); - g_assert_cmphex(i2c_get16(i2cdev, TMP105_REG_T_LOW), ==, 0x1234); + g_assert_cmphex(i2c_get16(i2cdev, TMP105_REG_T_LOW), ==, 0x1230); i2c_set16(i2cdev, TMP105_REG_T_HIGH, 0x4231); - g_assert_cmphex(i2c_get16(i2cdev, TMP105_REG_T_HIGH), ==, 0x4231); + g_assert_cmphex(i2c_get16(i2cdev, TMP105_REG_T_HIGH), ==, 0x4230); } static void tmp105_register_nodes(void) diff --git a/tests/qtest/tpm-emu.c b/tests/qtest/tpm-emu.c index 2bf8ff4..9e4c200 100644 --- a/tests/qtest/tpm-emu.c +++ b/tests/qtest/tpm-emu.c @@ -16,8 +16,8 @@ #include "backends/tpm/tpm_ioctl.h" #include "io/channel-socket.h" #include "qapi/error.h" -#include "qapi/qmp/qlist.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qlist.h" +#include "qobject/qstring.h" #include "tpm-emu.h" void tpm_emu_test_wait_cond(TPMTestState *s) diff --git a/tests/qtest/tpm-emu.h b/tests/qtest/tpm-emu.h index 712cee9..59c8009 100644 --- a/tests/qtest/tpm-emu.h +++ b/tests/qtest/tpm-emu.h @@ -21,7 +21,7 @@ #include "qemu/sockets.h" #include "io/channel.h" -#include "sysemu/tpm.h" +#include "system/tpm.h" #include "libqtest.h" struct tpm_hdr { diff --git a/tests/qtest/tpm-tests.c b/tests/qtest/tpm-tests.c index fb94496..197714f 100644 --- a/tests/qtest/tpm-tests.c +++ b/tests/qtest/tpm-tests.c @@ -114,7 +114,7 @@ void tpm_test_swtpm_migration_test(const char *src_tpm_path, sizeof(tpm_pcrread_resp)); tpm_util_migrate(src_qemu, uri); - tpm_util_wait_for_migration_complete(src_qemu); + tpm_util_wait_for_migration_complete(dst_qemu); tpm_util_pcrread(dst_qemu, tx, tpm_pcrread_resp, sizeof(tpm_pcrread_resp)); diff --git a/tests/qtest/tpm-util.c b/tests/qtest/tpm-util.c index 1c0319e..2cb2dd4 100644 --- a/tests/qtest/tpm-util.c +++ b/tests/qtest/tpm-util.c @@ -18,7 +18,7 @@ #include "hw/acpi/tpm.h" #include "libqtest.h" #include "tpm-util.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" void tpm_util_crb_transfer(QTestState *s, const unsigned char *req, size_t req_size, diff --git a/tests/qtest/ufs-test.c b/tests/qtest/ufs-test.c index 60199ab..4867ccf 100644 --- a/tests/qtest/ufs-test.c +++ b/tests/qtest/ufs-test.c @@ -8,13 +8,14 @@ #include "qemu/osdep.h" #include "qemu/module.h" -#include "qemu/units.h" #include "libqtest.h" #include "libqos/qgraph.h" #include "libqos/pci.h" #include "scsi/constants.h" #include "block/ufs.h" +#include "qemu/bitmap.h" +#define DWORD_BYTE 4 /* Test images sizes in Bytes */ #define TEST_IMAGE_SIZE (64 * 1024 * 1024) /* Timeout for various operations, in seconds. */ @@ -26,6 +27,12 @@ #define UTP_COMMAND_DESCRIPTOR_SIZE 4096 #define UTP_RESPONSE_UPIU_OFFSET 1024 #define UTP_PRDT_UPIU_OFFSET 2048 +#define UTRD_TEST_SLOT 0 +#define UFS_MAX_CMD_DESC 32 +/* Constants for MCQ */ +#define TEST_QID 0 +#define QUEUE_SIZE 32 +#define UFS_MCQ_MAX_QNUM 32 typedef struct QUfs QUfs; @@ -34,12 +41,22 @@ struct QUfs { QPCIDevice dev; QPCIBar bar; - uint64_t utrlba; - uint64_t utmrlba; + DECLARE_BITMAP(cmd_desc_bitmap, UFS_MAX_CMD_DESC); uint64_t cmd_desc_addr; uint64_t data_buffer_addr; bool enabled; + bool support_mcq; + + /* for legacy doorbell mode */ + uint64_t utrlba; + + /* for mcq mode */ + uint32_t maxq; + uint64_t sqlba[UFS_MCQ_MAX_QNUM]; + uint64_t cqlba[UFS_MCQ_MAX_QNUM]; + uint64_t sqdao[UFS_MCQ_MAX_QNUM]; + uint64_t cqdao[UFS_MCQ_MAX_QNUM]; }; static inline uint32_t ufs_rreg(QUfs *ufs, size_t offset) @@ -52,6 +69,24 @@ static inline void ufs_wreg(QUfs *ufs, size_t offset, uint32_t value) qpci_io_writel(&ufs->dev, ufs->bar, offset, value); } +static int alloc_cmd_desc_slot(QUfs *ufs) +{ + int slot = find_first_zero_bit(ufs->cmd_desc_bitmap, UFS_MAX_CMD_DESC); + if (slot == UFS_MAX_CMD_DESC) { + g_assert_not_reached(); + } + set_bit(slot, ufs->cmd_desc_bitmap); + return slot; +} + +static void release_cmd_desc_slot(QUfs *ufs, int slot) +{ + if (!test_bit(slot, ufs->cmd_desc_bitmap)) { + g_assert_not_reached(); + } + clear_bit(slot, ufs->cmd_desc_bitmap); +} + static void ufs_wait_for_irq(QUfs *ufs) { uint64_t end_time; @@ -64,14 +99,11 @@ static void ufs_wait_for_irq(QUfs *ufs) } while (is == 0 && g_get_monotonic_time() < end_time); } -static UtpTransferReqDesc ufs_build_req_utrd(uint64_t cmd_desc_addr, - uint8_t slot, +static UtpTransferReqDesc ufs_build_req_utrd(uint64_t command_desc_base_addr, uint32_t data_direction, uint16_t prd_table_length) { UtpTransferReqDesc req = { 0 }; - uint64_t command_desc_base_addr = - cmd_desc_addr + slot * UTP_COMMAND_DESCRIPTOR_SIZE; req.header.dword_0 = cpu_to_le32(1 << 28 | data_direction | UFS_UTP_REQ_DESC_INT_CMD); @@ -88,54 +120,109 @@ static UtpTransferReqDesc ufs_build_req_utrd(uint64_t cmd_desc_addr, return req; } -static void ufs_send_nop_out(QUfs *ufs, uint8_t slot, - UtpTransferReqDesc *utrd_out, UtpUpiuRsp *rsp_out) +static enum UtpOcsCodes +__ufs_send_transfer_request_doorbell(QUfs *ufs, uint8_t lun, + const UtpTransferReqDesc *utrd) { - /* Build up utp transfer request descriptor */ - UtpTransferReqDesc utrd = ufs_build_req_utrd(ufs->cmd_desc_addr, slot, - UFS_UTP_NO_DATA_TRANSFER, 0); - uint64_t utrd_addr = ufs->utrlba + slot * sizeof(UtpTransferReqDesc); + uint64_t utrd_addr = + ufs->utrlba + UTRD_TEST_SLOT * sizeof(UtpTransferReqDesc); + UtpTransferReqDesc utrd_result; + + qtest_memwrite(ufs->dev.bus->qts, utrd_addr, utrd, sizeof(*utrd)); + + /* Ring the doorbell */ + ufs_wreg(ufs, A_UTRLDBR, 1); + ufs_wait_for_irq(ufs); + g_assert_true(FIELD_EX32(ufs_rreg(ufs, A_IS), IS, UTRCS)); + ufs_wreg(ufs, A_IS, FIELD_DP32(0, IS, UTRCS, 1)); + + /* Handle completed command */ + qtest_memread(ufs->dev.bus->qts, utrd_addr, &utrd_result, + sizeof(utrd_result)); + return le32_to_cpu(utrd_result.header.dword_2) & 0xf; +} + +static enum UtpOcsCodes +__ufs_send_transfer_request_mcq(QUfs *ufs, uint8_t lun, + const UtpTransferReqDesc *utrd) +{ + uint32_t sqtp = ufs_rreg(ufs, ufs->sqdao[TEST_QID] + 0x4); + uint64_t utrd_addr = ufs->sqlba[TEST_QID] + sqtp; + uint32_t cqhp; + uint64_t cqentry_addr; + UfsCqEntry cqentry; + + qtest_memwrite(ufs->dev.bus->qts, utrd_addr, utrd, sizeof(*utrd)); + + /* Insert a new entry into the submission queue */ + sqtp = ufs_rreg(ufs, ufs->sqdao[TEST_QID] + 0x4); + sqtp = (sqtp + sizeof(UfsSqEntry)) % (QUEUE_SIZE * sizeof(UfsSqEntry)); + ufs_wreg(ufs, ufs->sqdao[TEST_QID] + 0x4, sqtp); + ufs_wait_for_irq(ufs); + g_assert_true(FIELD_EX32(ufs_rreg(ufs, A_IS), IS, CQES)); + ufs_wreg(ufs, A_IS, FIELD_DP32(0, IS, CQES, 1)); + + /* Handle the completed command from the completion queue */ + cqhp = ufs_rreg(ufs, ufs->cqdao[TEST_QID]); + cqentry_addr = ufs->cqlba[TEST_QID] + cqhp; + qtest_memread(ufs->dev.bus->qts, cqentry_addr, &cqentry, sizeof(cqentry)); + ufs_wreg(ufs, ufs->cqdao[TEST_QID], cqhp); + + return cqentry.status; +} + +static enum UtpOcsCodes +ufs_send_transfer_request_sync(QUfs *ufs, uint8_t lun, + const UtpTransferReqDesc *utrd) +{ + if (ufs->support_mcq) { + return __ufs_send_transfer_request_mcq(ufs, lun, utrd); + } + + return __ufs_send_transfer_request_doorbell(ufs, lun, utrd); +} + +static enum UtpOcsCodes ufs_send_nop_out(QUfs *ufs, UtpUpiuRsp *rsp_out) +{ + int cmd_desc_slot = alloc_cmd_desc_slot(ufs); uint64_t req_upiu_addr = - ufs->cmd_desc_addr + slot * UTP_COMMAND_DESCRIPTOR_SIZE; + ufs->cmd_desc_addr + cmd_desc_slot * UTP_COMMAND_DESCRIPTOR_SIZE; uint64_t rsp_upiu_addr = req_upiu_addr + UTP_RESPONSE_UPIU_OFFSET; - qtest_memwrite(ufs->dev.bus->qts, utrd_addr, &utrd, sizeof(utrd)); /* Build up request upiu */ UtpUpiuReq req_upiu = { 0 }; req_upiu.header.trans_type = UFS_UPIU_TRANSACTION_NOP_OUT; - req_upiu.header.task_tag = slot; + req_upiu.header.task_tag = cmd_desc_slot; qtest_memwrite(ufs->dev.bus->qts, req_upiu_addr, &req_upiu, sizeof(req_upiu)); - /* Ring Doorbell */ - ufs_wreg(ufs, A_UTRLDBR, 1); - ufs_wait_for_irq(ufs); - g_assert_true(FIELD_EX32(ufs_rreg(ufs, A_IS), IS, UTRCS)); - ufs_wreg(ufs, A_IS, FIELD_DP32(0, IS, UTRCS, 1)); + /* Build up utp transfer request descriptor */ + UtpTransferReqDesc utrd = + ufs_build_req_utrd(req_upiu_addr, UFS_UTP_NO_DATA_TRANSFER, 0); + + /* Send Transfer Request */ + enum UtpOcsCodes ret = ufs_send_transfer_request_sync(ufs, 0, &utrd); - qtest_memread(ufs->dev.bus->qts, utrd_addr, utrd_out, sizeof(*utrd_out)); qtest_memread(ufs->dev.bus->qts, rsp_upiu_addr, rsp_out, sizeof(*rsp_out)); + release_cmd_desc_slot(ufs, cmd_desc_slot); + return ret; } -static void ufs_send_query(QUfs *ufs, uint8_t slot, uint8_t query_function, - uint8_t query_opcode, uint8_t idn, uint8_t index, - uint8_t selector, uint32_t attr_value, - UtpTransferReqDesc *utrd_out, UtpUpiuRsp *rsp_out) +static enum UtpOcsCodes ufs_send_query(QUfs *ufs, uint8_t query_function, + uint8_t query_opcode, uint8_t idn, + uint8_t index, uint8_t selector, + uint32_t attr_value, UtpUpiuRsp *rsp_out) { - /* Build up utp transfer request descriptor */ - UtpTransferReqDesc utrd = ufs_build_req_utrd(ufs->cmd_desc_addr, slot, - UFS_UTP_NO_DATA_TRANSFER, 0); - uint64_t utrd_addr = ufs->utrlba + slot * sizeof(UtpTransferReqDesc); + int cmd_desc_slot = alloc_cmd_desc_slot(ufs); uint64_t req_upiu_addr = - ufs->cmd_desc_addr + slot * UTP_COMMAND_DESCRIPTOR_SIZE; + ufs->cmd_desc_addr + cmd_desc_slot * UTP_COMMAND_DESCRIPTOR_SIZE; uint64_t rsp_upiu_addr = req_upiu_addr + UTP_RESPONSE_UPIU_OFFSET; - qtest_memwrite(ufs->dev.bus->qts, utrd_addr, &utrd, sizeof(utrd)); /* Build up request upiu */ UtpUpiuReq req_upiu = { 0 }; req_upiu.header.trans_type = UFS_UPIU_TRANSACTION_QUERY_REQ; req_upiu.header.query_func = query_function; - req_upiu.header.task_tag = slot; + req_upiu.header.task_tag = cmd_desc_slot; /* * QEMU UFS does not currently support Write descriptor, * so the value of data_segment_length is always 0. @@ -145,27 +232,28 @@ static void ufs_send_query(QUfs *ufs, uint8_t slot, uint8_t query_function, req_upiu.qr.idn = idn; req_upiu.qr.index = index; req_upiu.qr.selector = selector; - req_upiu.qr.value = attr_value; + req_upiu.qr.value = cpu_to_be32(attr_value); req_upiu.qr.length = UFS_QUERY_DESC_MAX_SIZE; qtest_memwrite(ufs->dev.bus->qts, req_upiu_addr, &req_upiu, sizeof(req_upiu)); - /* Ring Doorbell */ - ufs_wreg(ufs, A_UTRLDBR, 1); - ufs_wait_for_irq(ufs); - g_assert_true(FIELD_EX32(ufs_rreg(ufs, A_IS), IS, UTRCS)); - ufs_wreg(ufs, A_IS, FIELD_DP32(0, IS, UTRCS, 1)); + /* Build up utp transfer request descriptor */ + UtpTransferReqDesc utrd = + ufs_build_req_utrd(req_upiu_addr, UFS_UTP_NO_DATA_TRANSFER, 0); + + /* Send Transfer Request */ + enum UtpOcsCodes ret = ufs_send_transfer_request_sync(ufs, 0, &utrd); - qtest_memread(ufs->dev.bus->qts, utrd_addr, utrd_out, sizeof(*utrd_out)); qtest_memread(ufs->dev.bus->qts, rsp_upiu_addr, rsp_out, sizeof(*rsp_out)); + release_cmd_desc_slot(ufs, cmd_desc_slot); + return ret; } -static void ufs_send_scsi_command(QUfs *ufs, uint8_t slot, uint8_t lun, - const uint8_t *cdb, const uint8_t *data_in, - size_t data_in_len, uint8_t *data_out, - size_t data_out_len, - UtpTransferReqDesc *utrd_out, - UtpUpiuRsp *rsp_out) +static enum UtpOcsCodes +ufs_send_scsi_command(QUfs *ufs, uint8_t lun, const uint8_t *cdb, + const uint8_t *data_in, size_t data_in_len, + uint8_t *data_out, size_t data_out_len, + UtpUpiuRsp *rsp_out) { /* Build up PRDT */ @@ -175,8 +263,9 @@ static void ufs_send_scsi_command(QUfs *ufs, uint8_t slot, uint8_t lun, uint8_t flags; uint16_t prd_table_length, i; uint32_t data_direction, data_len; + int cmd_desc_slot = alloc_cmd_desc_slot(ufs); uint64_t req_upiu_addr = - ufs->cmd_desc_addr + slot * UTP_COMMAND_DESCRIPTOR_SIZE; + ufs->cmd_desc_addr + cmd_desc_slot * UTP_COMMAND_DESCRIPTOR_SIZE; uint64_t prdt_addr = req_upiu_addr + UTP_PRDT_UPIU_OFFSET; g_assert_true(data_in_len < MAX_PRD_ENTRY_COUNT * PRD_ENTRY_DATA_SIZE); @@ -218,36 +307,33 @@ static void ufs_send_scsi_command(QUfs *ufs, uint8_t slot, uint8_t lun, qtest_memwrite(ufs->dev.bus->qts, prdt_addr, entries, prd_table_length * sizeof(UfshcdSgEntry)); - /* Build up utp transfer request descriptor */ - UtpTransferReqDesc utrd = ufs_build_req_utrd( - ufs->cmd_desc_addr, slot, data_direction, prd_table_length); - uint64_t utrd_addr = ufs->utrlba + slot * sizeof(UtpTransferReqDesc); uint64_t rsp_upiu_addr = req_upiu_addr + UTP_RESPONSE_UPIU_OFFSET; - qtest_memwrite(ufs->dev.bus->qts, utrd_addr, &utrd, sizeof(utrd)); /* Build up request upiu */ UtpUpiuReq req_upiu = { 0 }; req_upiu.header.trans_type = UFS_UPIU_TRANSACTION_COMMAND; req_upiu.header.flags = flags; req_upiu.header.lun = lun; - req_upiu.header.task_tag = slot; + req_upiu.header.task_tag = cmd_desc_slot; req_upiu.sc.exp_data_transfer_len = cpu_to_be32(data_len); memcpy(req_upiu.sc.cdb, cdb, UFS_CDB_SIZE); qtest_memwrite(ufs->dev.bus->qts, req_upiu_addr, &req_upiu, sizeof(req_upiu)); - /* Ring Doorbell */ - ufs_wreg(ufs, A_UTRLDBR, 1); - ufs_wait_for_irq(ufs); - g_assert_true(FIELD_EX32(ufs_rreg(ufs, A_IS), IS, UTRCS)); - ufs_wreg(ufs, A_IS, FIELD_DP32(0, IS, UTRCS, 1)); + /* Build up utp transfer request descriptor */ + UtpTransferReqDesc utrd = + ufs_build_req_utrd(req_upiu_addr, data_direction, prd_table_length); + + /* Send Transfer Request */ + enum UtpOcsCodes ret = ufs_send_transfer_request_sync(ufs, lun, &utrd); - qtest_memread(ufs->dev.bus->qts, utrd_addr, utrd_out, sizeof(*utrd_out)); qtest_memread(ufs->dev.bus->qts, rsp_upiu_addr, rsp_out, sizeof(*rsp_out)); if (data_out_len) { qtest_memread(ufs->dev.bus->qts, ufs->data_buffer_addr, data_out, data_out_len); } + release_cmd_desc_slot(ufs, cmd_desc_slot); + return ret; } /** @@ -257,10 +343,10 @@ static void ufs_send_scsi_command(QUfs *ufs, uint8_t slot, uint8_t lun, static void ufs_init(QUfs *ufs, QGuestAllocator *alloc) { uint64_t end_time; - uint32_t nutrs, nutmrs; + uint32_t nutrs; uint32_t hcs, is, ucmdarg2, cap; uint32_t hce = 0, ie = 0; - UtpTransferReqDesc utrd; + enum UtpOcsCodes ocs; UtpUpiuRsp rsp_upiu; ufs->bar = qpci_iomap(&ufs->dev, 0, NULL); @@ -305,9 +391,12 @@ static void ufs_init(QUfs *ufs, QGuestAllocator *alloc) hcs = ufs_rreg(ufs, A_HCS); g_assert_true(FIELD_EX32(hcs, HCS, DP)); g_assert_true(FIELD_EX32(hcs, HCS, UTRLRDY)); - g_assert_true(FIELD_EX32(hcs, HCS, UTMRLRDY)); g_assert_true(FIELD_EX32(hcs, HCS, UCRDY)); + /* Check MCQ support */ + cap = ufs_rreg(ufs, A_CAP); + ufs->support_mcq = FIELD_EX32(cap, CAP, MCQS); + /* Enable all interrupt functions */ ie = FIELD_DP32(ie, IE, UTRCE, 1); ie = FIELD_DP32(ie, IE, UEE, 1); @@ -320,45 +409,89 @@ static void ufs_init(QUfs *ufs, QGuestAllocator *alloc) ie = FIELD_DP32(ie, IE, HCFEE, 1); ie = FIELD_DP32(ie, IE, SBFEE, 1); ie = FIELD_DP32(ie, IE, CEFEE, 1); + if (ufs->support_mcq) { + ie = FIELD_DP32(ie, IE, CQEE, 1); + } ufs_wreg(ufs, A_IE, ie); ufs_wreg(ufs, A_UTRIACR, 0); - /* Enable transfer request and task management request */ - cap = ufs_rreg(ufs, A_CAP); - nutrs = FIELD_EX32(cap, CAP, NUTRS) + 1; - nutmrs = FIELD_EX32(cap, CAP, NUTMRS) + 1; + /* Enable transfer request */ ufs->cmd_desc_addr = - guest_alloc(alloc, nutrs * UTP_COMMAND_DESCRIPTOR_SIZE); + guest_alloc(alloc, UFS_MAX_CMD_DESC * UTP_COMMAND_DESCRIPTOR_SIZE); ufs->data_buffer_addr = guest_alloc(alloc, MAX_PRD_ENTRY_COUNT * PRD_ENTRY_DATA_SIZE); - ufs->utrlba = guest_alloc(alloc, nutrs * sizeof(UtpTransferReqDesc)); - ufs->utmrlba = guest_alloc(alloc, nutmrs * sizeof(UtpTaskReqDesc)); - ufs_wreg(ufs, A_UTRLBA, ufs->utrlba & 0xffffffff); - ufs_wreg(ufs, A_UTRLBAU, ufs->utrlba >> 32); - ufs_wreg(ufs, A_UTMRLBA, ufs->utmrlba & 0xffffffff); - ufs_wreg(ufs, A_UTMRLBAU, ufs->utmrlba >> 32); - ufs_wreg(ufs, A_UTRLRSR, 1); - ufs_wreg(ufs, A_UTMRLRSR, 1); + if (ufs->support_mcq) { + uint32_t mcqcap, qid, qcfgptr, mcq_reg_offset; + uint32_t cqattr = 0, sqattr = 0; + + mcqcap = ufs_rreg(ufs, A_MCQCAP); + qcfgptr = FIELD_EX32(mcqcap, MCQCAP, QCFGPTR); + ufs->maxq = FIELD_EX32(mcqcap, MCQCAP, MAXQ) + 1; + for (qid = 0; qid < ufs->maxq; ++qid) { + ufs->sqlba[qid] = + guest_alloc(alloc, QUEUE_SIZE * sizeof(UtpTransferReqDesc)); + ufs->cqlba[qid] = + guest_alloc(alloc, QUEUE_SIZE * sizeof(UtpTransferReqDesc)); + mcq_reg_offset = qcfgptr * 0x200 + qid * 0x40; + + ufs_wreg(ufs, mcq_reg_offset + A_SQLBA, + ufs->sqlba[qid] & 0xffffffff); + ufs_wreg(ufs, mcq_reg_offset + A_SQUBA, ufs->sqlba[qid] >> 32); + ufs_wreg(ufs, mcq_reg_offset + A_CQLBA, + ufs->cqlba[qid] & 0xffffffff); + ufs_wreg(ufs, mcq_reg_offset + A_CQUBA, ufs->cqlba[qid] >> 32); + + /* Enable Completion Queue */ + cqattr = FIELD_DP32(cqattr, CQATTR, CQEN, 1); + cqattr = FIELD_DP32(cqattr, CQATTR, SIZE, + QUEUE_SIZE * sizeof(UtpTransferReqDesc) / + DWORD_BYTE); + ufs_wreg(ufs, mcq_reg_offset + A_CQATTR, cqattr); + + /* Enable Submission Queue */ + sqattr = FIELD_DP32(sqattr, SQATTR, SQEN, 1); + sqattr = FIELD_DP32(sqattr, SQATTR, SIZE, + QUEUE_SIZE * sizeof(UtpTransferReqDesc) / + DWORD_BYTE); + sqattr = FIELD_DP32(sqattr, SQATTR, CQID, qid); + ufs_wreg(ufs, mcq_reg_offset + A_SQATTR, sqattr); + + /* Cache head & tail pointer */ + ufs->sqdao[qid] = ufs_rreg(ufs, mcq_reg_offset + A_SQDAO); + ufs->cqdao[qid] = ufs_rreg(ufs, mcq_reg_offset + A_CQDAO); + } + } else { + nutrs = FIELD_EX32(cap, CAP, NUTRS) + 1; + ufs->utrlba = guest_alloc(alloc, nutrs * sizeof(UtpTransferReqDesc)); + + ufs_wreg(ufs, A_UTRLBA, ufs->utrlba & 0xffffffff); + ufs_wreg(ufs, A_UTRLBAU, ufs->utrlba >> 32); + ufs_wreg(ufs, A_UTRLRSR, 1); + } /* Send nop out to test transfer request */ - ufs_send_nop_out(ufs, 0, &utrd, &rsp_upiu); - g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + ocs = ufs_send_nop_out(ufs, &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS); /* Set fDeviceInit flag via query request */ - ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST, - UFS_UPIU_QUERY_OPCODE_SET_FLAG, - UFS_QUERY_FLAG_IDN_FDEVICEINIT, 0, 0, 0, &utrd, &rsp_upiu); - g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST, + UFS_UPIU_QUERY_OPCODE_SET_FLAG, + UFS_QUERY_FLAG_IDN_FDEVICEINIT, 0, 0, 0, &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS); + g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS); /* Wait for device to reset */ end_time = g_get_monotonic_time() + TIMEOUT_SECONDS * G_TIME_SPAN_SECOND; do { qtest_clock_step(ufs->dev.bus->qts, 100); - ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, - UFS_UPIU_QUERY_OPCODE_READ_FLAG, - UFS_QUERY_FLAG_IDN_FDEVICEINIT, 0, 0, 0, &utrd, - &rsp_upiu); + ocs = + ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_FLAG, + UFS_QUERY_FLAG_IDN_FDEVICEINIT, 0, 0, 0, &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS); + g_assert_cmpuint(rsp_upiu.header.response, ==, + UFS_COMMAND_RESULT_SUCCESS); } while (be32_to_cpu(rsp_upiu.qr.value) != 0 && g_get_monotonic_time() < end_time); g_assert_cmpuint(be32_to_cpu(rsp_upiu.qr.value), ==, 0); @@ -369,8 +502,15 @@ static void ufs_init(QUfs *ufs, QGuestAllocator *alloc) static void ufs_exit(QUfs *ufs, QGuestAllocator *alloc) { if (ufs->enabled) { - guest_free(alloc, ufs->utrlba); - guest_free(alloc, ufs->utmrlba); + if (ufs->support_mcq) { + for (uint32_t qid = 0; qid < ufs->maxq; ++qid) { + guest_free(alloc, ufs->sqlba[qid]); + guest_free(alloc, ufs->cqlba[qid]); + } + } else { + guest_free(alloc, ufs->utrlba); + } + guest_free(alloc, ufs->cmd_desc_addr); guest_free(alloc, ufs->data_buffer_addr); } @@ -433,15 +573,15 @@ static void ufstest_init(void *obj, void *data, QGuestAllocator *alloc) const uint8_t request_sense_cdb[UFS_CDB_SIZE] = { REQUEST_SENSE, }; - UtpTransferReqDesc utrd; + enum UtpOcsCodes ocs; UtpUpiuRsp rsp_upiu; ufs_init(ufs, alloc); /* Check REPORT_LUNS */ - ufs_send_scsi_command(ufs, 0, 0, report_luns_cdb, NULL, 0, buf, sizeof(buf), - &utrd, &rsp_upiu); - g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + ocs = ufs_send_scsi_command(ufs, 0, report_luns_cdb, NULL, 0, buf, + sizeof(buf), &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS); g_assert_cmpuint(rsp_upiu.header.scsi_status, ==, GOOD); /* LUN LIST LENGTH should be 8, in big endian */ g_assert_cmpuint(buf[3], ==, 8); @@ -449,15 +589,15 @@ static void ufstest_init(void *obj, void *data, QGuestAllocator *alloc) g_assert_cmpuint(buf[9], ==, 0); /* Clear Unit Attention */ - ufs_send_scsi_command(ufs, 0, 0, request_sense_cdb, NULL, 0, buf, - sizeof(buf), &utrd, &rsp_upiu); - g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + ocs = ufs_send_scsi_command(ufs, 0, request_sense_cdb, NULL, 0, buf, + sizeof(buf), &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS); g_assert_cmpuint(rsp_upiu.header.scsi_status, ==, CHECK_CONDITION); /* Check TEST_UNIT_READY */ - ufs_send_scsi_command(ufs, 0, 0, test_unit_ready_cdb, NULL, 0, NULL, 0, - &utrd, &rsp_upiu); - g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + ocs = ufs_send_scsi_command(ufs, 0, test_unit_ready_cdb, NULL, 0, NULL, 0, + &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS); g_assert_cmpuint(rsp_upiu.header.scsi_status, ==, GOOD); ufs_exit(ufs, alloc); @@ -499,22 +639,22 @@ static void ufstest_read_write(void *obj, void *data, QGuestAllocator *alloc) WRITE_10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00 }; uint32_t block_size; - UtpTransferReqDesc utrd; + enum UtpOcsCodes ocs; UtpUpiuRsp rsp_upiu; const int test_lun = 1; ufs_init(ufs, alloc); /* Clear Unit Attention */ - ufs_send_scsi_command(ufs, 0, test_lun, request_sense_cdb, NULL, 0, - read_buf, sizeof(read_buf), &utrd, &rsp_upiu); - g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + ocs = ufs_send_scsi_command(ufs, test_lun, request_sense_cdb, NULL, 0, + read_buf, sizeof(read_buf), &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS); g_assert_cmpuint(rsp_upiu.header.scsi_status, ==, CHECK_CONDITION); /* Read capacity */ - ufs_send_scsi_command(ufs, 0, test_lun, read_capacity_cdb, NULL, 0, - read_buf, sizeof(read_buf), &utrd, &rsp_upiu); - g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + ocs = ufs_send_scsi_command(ufs, test_lun, read_capacity_cdb, NULL, 0, + read_buf, sizeof(read_buf), &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS); g_assert_cmpuint(rsp_upiu.header.scsi_status, ==, UFS_COMMAND_RESULT_SUCCESS); block_size = ldl_be_p(&read_buf[8]); @@ -522,16 +662,16 @@ static void ufstest_read_write(void *obj, void *data, QGuestAllocator *alloc) /* Write data */ memset(write_buf, 0xab, block_size); - ufs_send_scsi_command(ufs, 0, test_lun, write_cdb, write_buf, block_size, - NULL, 0, &utrd, &rsp_upiu); - g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + ocs = ufs_send_scsi_command(ufs, test_lun, write_cdb, write_buf, block_size, + NULL, 0, &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS); g_assert_cmpuint(rsp_upiu.header.scsi_status, ==, UFS_COMMAND_RESULT_SUCCESS); /* Read data and verify */ - ufs_send_scsi_command(ufs, 0, test_lun, read_cdb, NULL, 0, read_buf, - block_size, &utrd, &rsp_upiu); - g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + ocs = ufs_send_scsi_command(ufs, test_lun, read_cdb, NULL, 0, read_buf, + block_size, &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS); g_assert_cmpuint(rsp_upiu.header.scsi_status, ==, UFS_COMMAND_RESULT_SUCCESS); g_assert_cmpint(memcmp(read_buf, write_buf, block_size), ==, 0); @@ -544,76 +684,74 @@ static void ufstest_query_flag_request(void *obj, void *data, { QUfs *ufs = obj; - UtpTransferReqDesc utrd; + enum UtpOcsCodes ocs; UtpUpiuRsp rsp_upiu; ufs_init(ufs, alloc); /* Read read-only flag */ - ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, - UFS_UPIU_QUERY_OPCODE_READ_FLAG, - UFS_QUERY_FLAG_IDN_FDEVICEINIT, 0, 0, 0, &utrd, &rsp_upiu); - g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_FLAG, + UFS_QUERY_FLAG_IDN_FDEVICEINIT, 0, 0, 0, &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS); g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS); g_assert_cmpuint(rsp_upiu.qr.opcode, ==, UFS_UPIU_QUERY_OPCODE_READ_FLAG); g_assert_cmpuint(rsp_upiu.qr.idn, ==, UFS_QUERY_FLAG_IDN_FDEVICEINIT); g_assert_cmpuint(rsp_upiu.qr.value, ==, be32_to_cpu(0)); /* Flag Set, Clear, Toggle Test with fDeviceLifeSpanModeEn */ - ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, - UFS_UPIU_QUERY_OPCODE_READ_FLAG, - UFS_QUERY_FLAG_IDN_LIFE_SPAN_MODE_ENABLE, 0, 0, 0, &utrd, - &rsp_upiu); - g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_FLAG, + UFS_QUERY_FLAG_IDN_LIFE_SPAN_MODE_ENABLE, 0, 0, 0, + &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS); g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS); g_assert_cmpuint(rsp_upiu.qr.value, ==, be32_to_cpu(0)); - ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST, - UFS_UPIU_QUERY_OPCODE_SET_FLAG, - UFS_QUERY_FLAG_IDN_LIFE_SPAN_MODE_ENABLE, 0, 0, 0, &utrd, - &rsp_upiu); - g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST, + UFS_UPIU_QUERY_OPCODE_SET_FLAG, + UFS_QUERY_FLAG_IDN_LIFE_SPAN_MODE_ENABLE, 0, 0, 0, + &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS); g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS); g_assert_cmpuint(rsp_upiu.qr.value, ==, be32_to_cpu(1)); - ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST, - UFS_UPIU_QUERY_OPCODE_CLEAR_FLAG, - UFS_QUERY_FLAG_IDN_LIFE_SPAN_MODE_ENABLE, 0, 0, 0, &utrd, - &rsp_upiu); - g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST, + UFS_UPIU_QUERY_OPCODE_CLEAR_FLAG, + UFS_QUERY_FLAG_IDN_LIFE_SPAN_MODE_ENABLE, 0, 0, 0, + &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS); g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS); g_assert_cmpuint(rsp_upiu.qr.value, ==, be32_to_cpu(0)); - ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST, - UFS_UPIU_QUERY_OPCODE_TOGGLE_FLAG, - UFS_QUERY_FLAG_IDN_LIFE_SPAN_MODE_ENABLE, 0, 0, 0, &utrd, - &rsp_upiu); - g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST, + UFS_UPIU_QUERY_OPCODE_TOGGLE_FLAG, + UFS_QUERY_FLAG_IDN_LIFE_SPAN_MODE_ENABLE, 0, 0, 0, + &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS); g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS); g_assert_cmpuint(rsp_upiu.qr.value, ==, be32_to_cpu(1)); - ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST, - UFS_UPIU_QUERY_OPCODE_TOGGLE_FLAG, - UFS_QUERY_FLAG_IDN_LIFE_SPAN_MODE_ENABLE, 0, 0, 0, &utrd, - &rsp_upiu); - g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST, + UFS_UPIU_QUERY_OPCODE_TOGGLE_FLAG, + UFS_QUERY_FLAG_IDN_LIFE_SPAN_MODE_ENABLE, 0, 0, 0, + &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS); g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS); g_assert_cmpuint(rsp_upiu.qr.value, ==, be32_to_cpu(0)); /* Read Write-only Flag (Intended Failure) */ - ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, - UFS_UPIU_QUERY_OPCODE_READ_FLAG, - UFS_QUERY_FLAG_IDN_PURGE_ENABLE, 0, 0, 0, &utrd, &rsp_upiu); - g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, - UFS_OCS_INVALID_CMD_TABLE_ATTR); + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_FLAG, + UFS_QUERY_FLAG_IDN_PURGE_ENABLE, 0, 0, 0, &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_INVALID_CMD_TABLE_ATTR); g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_QUERY_RESULT_NOT_READABLE); /* Write Read-Only Flag (Intended Failure) */ - ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST, - UFS_UPIU_QUERY_OPCODE_SET_FLAG, UFS_QUERY_FLAG_IDN_BUSY_RTC, - 0, 0, 0, &utrd, &rsp_upiu); - g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, - UFS_OCS_INVALID_CMD_TABLE_ATTR); + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST, + UFS_UPIU_QUERY_OPCODE_SET_FLAG, + UFS_QUERY_FLAG_IDN_BUSY_RTC, 0, 0, 0, &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_INVALID_CMD_TABLE_ATTR); g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_QUERY_RESULT_NOT_WRITEABLE); @@ -625,130 +763,146 @@ static void ufstest_query_attr_request(void *obj, void *data, { QUfs *ufs = obj; - UtpTransferReqDesc utrd; + enum UtpOcsCodes ocs; UtpUpiuRsp rsp_upiu; ufs_init(ufs, alloc); /* Read Readable Attributes*/ - ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, - UFS_UPIU_QUERY_OPCODE_READ_ATTR, - UFS_QUERY_ATTR_IDN_BOOT_LU_EN, 0, 0, 0, &utrd, &rsp_upiu); - g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_ATTR, + UFS_QUERY_ATTR_IDN_BOOT_LU_EN, 0, 0, 0, &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS); g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS); g_assert_cmpuint(rsp_upiu.qr.opcode, ==, UFS_UPIU_QUERY_OPCODE_READ_ATTR); g_assert_cmpuint(rsp_upiu.qr.idn, ==, UFS_QUERY_ATTR_IDN_BOOT_LU_EN); g_assert_cmpuint(rsp_upiu.qr.value, ==, cpu_to_be32(0x00)); - ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, - UFS_UPIU_QUERY_OPCODE_READ_ATTR, - UFS_QUERY_ATTR_IDN_BKOPS_STATUS, 0, 0, 0, &utrd, &rsp_upiu); - g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_ATTR, + UFS_QUERY_ATTR_IDN_BKOPS_STATUS, 0, 0, 0, &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS); g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS); g_assert_cmpuint(rsp_upiu.qr.value, ==, cpu_to_be32(0x00)); + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_ATTR, + UFS_QUERY_ATTR_IDN_CASE_ROUGH_TEMP, 0, 0, 0, + &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS); + g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS); + g_assert_cmpuint(rsp_upiu.qr.value, ==, cpu_to_be32(0x00)); + + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_ATTR, + UFS_QUERY_ATTR_IDN_HIGH_TEMP_BOUND, 0, 0, 0, + &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS); + g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS); + g_assert_cmpuint(rsp_upiu.qr.value, ==, cpu_to_be32(160)); + + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_ATTR, + UFS_QUERY_ATTR_IDN_LOW_TEMP_BOUND, 0, 0, 0, + &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS); + g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS); + g_assert_cmpuint(rsp_upiu.qr.value, ==, cpu_to_be32(60)); + /* Write Writable Attributes & Read Again */ - ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST, - UFS_UPIU_QUERY_OPCODE_WRITE_ATTR, - UFS_QUERY_ATTR_IDN_ACTIVE_ICC_LVL, 0, 0, 0x03, &utrd, - &rsp_upiu); - g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST, + UFS_UPIU_QUERY_OPCODE_WRITE_ATTR, + UFS_QUERY_ATTR_IDN_ACTIVE_ICC_LVL, 0, 0, 0x03, + &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS); g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS); g_assert_cmpuint(rsp_upiu.qr.value, ==, cpu_to_be32(0x03)); - ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST, - UFS_UPIU_QUERY_OPCODE_WRITE_ATTR, - UFS_QUERY_ATTR_IDN_EE_CONTROL, 0, 0, 0x07, &utrd, &rsp_upiu); - g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST, + UFS_UPIU_QUERY_OPCODE_WRITE_ATTR, + UFS_QUERY_ATTR_IDN_EE_CONTROL, 0, 0, 0x07, &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS); g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS); g_assert_cmpuint(rsp_upiu.qr.value, ==, cpu_to_be32(0x07)); - ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, - UFS_UPIU_QUERY_OPCODE_READ_ATTR, - UFS_QUERY_ATTR_IDN_ACTIVE_ICC_LVL, 0, 0, 0, &utrd, - &rsp_upiu); - g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_ATTR, + UFS_QUERY_ATTR_IDN_ACTIVE_ICC_LVL, 0, 0, 0, &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS); g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS); g_assert_cmpuint(rsp_upiu.qr.value, ==, cpu_to_be32(0x03)); - ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, - UFS_UPIU_QUERY_OPCODE_READ_ATTR, - UFS_QUERY_ATTR_IDN_EE_CONTROL, 0, 0, 0, &utrd, &rsp_upiu); - g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_ATTR, + UFS_QUERY_ATTR_IDN_EE_CONTROL, 0, 0, 0, &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS); g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS); g_assert_cmpuint(rsp_upiu.qr.value, ==, cpu_to_be32(0x07)); /* Write Invalid Value (Intended Error) */ - ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST, - UFS_UPIU_QUERY_OPCODE_WRITE_ATTR, - UFS_QUERY_ATTR_IDN_ACTIVE_ICC_LVL, 0, 0, 0x10, &utrd, - &rsp_upiu); - g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, - UFS_OCS_INVALID_CMD_TABLE_ATTR); + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST, + UFS_UPIU_QUERY_OPCODE_WRITE_ATTR, + UFS_QUERY_ATTR_IDN_ACTIVE_ICC_LVL, 0, 0, 0x10, + &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_INVALID_CMD_TABLE_ATTR); g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_QUERY_RESULT_INVALID_VALUE); - ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, - UFS_UPIU_QUERY_OPCODE_READ_ATTR, - UFS_QUERY_ATTR_IDN_ACTIVE_ICC_LVL, 0, 0, 0, &utrd, - &rsp_upiu); - g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_ATTR, + UFS_QUERY_ATTR_IDN_ACTIVE_ICC_LVL, 0, 0, 0, &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS); g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS); g_assert_cmpuint(rsp_upiu.qr.value, ==, cpu_to_be32(0x03)); /* Read Write-Only Attribute (Intended Error) */ - ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, - UFS_UPIU_QUERY_OPCODE_READ_ATTR, - UFS_QUERY_ATTR_IDN_SECONDS_PASSED, 0, 0, 0, &utrd, - &rsp_upiu); - g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, - UFS_OCS_INVALID_CMD_TABLE_ATTR); + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_ATTR, + UFS_QUERY_ATTR_IDN_SECONDS_PASSED, 0, 0, 0, &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_INVALID_CMD_TABLE_ATTR); g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_QUERY_RESULT_NOT_READABLE); /* Write Read-Only Attribute (Intended Error) */ - ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST, - UFS_UPIU_QUERY_OPCODE_WRITE_ATTR, - UFS_QUERY_ATTR_IDN_POWER_MODE, 0, 0, 0x01, &utrd, &rsp_upiu); - g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, - UFS_OCS_INVALID_CMD_TABLE_ATTR); + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST, + UFS_UPIU_QUERY_OPCODE_WRITE_ATTR, + UFS_QUERY_ATTR_IDN_POWER_MODE, 0, 0, 0x01, &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_INVALID_CMD_TABLE_ATTR); g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_QUERY_RESULT_NOT_WRITEABLE); - ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, - UFS_UPIU_QUERY_OPCODE_READ_ATTR, - UFS_QUERY_ATTR_IDN_POWER_MODE, 0, 0, 0, &utrd, &rsp_upiu); - g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_ATTR, + UFS_QUERY_ATTR_IDN_POWER_MODE, 0, 0, 0, &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS); g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS); g_assert_cmpuint(rsp_upiu.qr.value, ==, cpu_to_be32(0x00)); /* Reset Written Attributes */ - ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST, - UFS_UPIU_QUERY_OPCODE_WRITE_ATTR, - UFS_QUERY_ATTR_IDN_ACTIVE_ICC_LVL, 0, 0, 0, &utrd, - &rsp_upiu); - g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST, + UFS_UPIU_QUERY_OPCODE_WRITE_ATTR, + UFS_QUERY_ATTR_IDN_ACTIVE_ICC_LVL, 0, 0, 0, &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS); g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS); g_assert_cmpuint(rsp_upiu.qr.value, ==, cpu_to_be32(0x00)); - ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST, - UFS_UPIU_QUERY_OPCODE_WRITE_ATTR, - UFS_QUERY_ATTR_IDN_EE_CONTROL, 0, 0, 0, &utrd, &rsp_upiu); - g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST, + UFS_UPIU_QUERY_OPCODE_WRITE_ATTR, + UFS_QUERY_ATTR_IDN_EE_CONTROL, 0, 0, 0, &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS); g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS); g_assert_cmpuint(rsp_upiu.qr.value, ==, cpu_to_be32(0x00)); - ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, - UFS_UPIU_QUERY_OPCODE_READ_ATTR, - UFS_QUERY_ATTR_IDN_ACTIVE_ICC_LVL, 0, 0, 0, &utrd, - &rsp_upiu); - g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_ATTR, + UFS_QUERY_ATTR_IDN_ACTIVE_ICC_LVL, 0, 0, 0, &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS); g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS); g_assert_cmpuint(rsp_upiu.qr.value, ==, cpu_to_be32(0x00)); - ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, - UFS_UPIU_QUERY_OPCODE_READ_ATTR, - UFS_QUERY_ATTR_IDN_EE_CONTROL, 0, 0, 0, &utrd, &rsp_upiu); - g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_ATTR, + UFS_QUERY_ATTR_IDN_EE_CONTROL, 0, 0, 0, &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS); g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS); g_assert_cmpuint(rsp_upiu.qr.value, ==, cpu_to_be32(0x00)); @@ -760,17 +914,17 @@ static void ufstest_query_desc_request(void *obj, void *data, { QUfs *ufs = obj; - UtpTransferReqDesc utrd; + enum UtpOcsCodes ocs; UtpUpiuRsp rsp_upiu; ufs_init(ufs, alloc); /* Write Descriptor is not supported yet */ /* Read Device Descriptor */ - ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, - UFS_UPIU_QUERY_OPCODE_READ_DESC, UFS_QUERY_DESC_IDN_DEVICE, - 0, 0, 0, &utrd, &rsp_upiu); - g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_DESC, + UFS_QUERY_DESC_IDN_DEVICE, 0, 0, 0, &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS); g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS); g_assert_cmpuint(rsp_upiu.qr.opcode, ==, UFS_UPIU_QUERY_OPCODE_READ_DESC); g_assert_cmpuint(rsp_upiu.qr.idn, ==, UFS_QUERY_DESC_IDN_DEVICE); @@ -780,126 +934,123 @@ static void ufstest_query_desc_request(void *obj, void *data, /* Read Configuration Descriptor is not supported yet*/ /* Read Unit Descriptor */ - ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, - UFS_UPIU_QUERY_OPCODE_READ_DESC, UFS_QUERY_DESC_IDN_UNIT, 0, - 0, 0, &utrd, &rsp_upiu); - g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_DESC, + UFS_QUERY_DESC_IDN_UNIT, 0, 0, 0, &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS); g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS); g_assert_cmpuint(rsp_upiu.qr.data[0], ==, sizeof(UnitDescriptor)); g_assert_cmpuint(rsp_upiu.qr.data[1], ==, UFS_QUERY_DESC_IDN_UNIT); g_assert_cmpuint(rsp_upiu.qr.data[2], ==, 0); - ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, - UFS_UPIU_QUERY_OPCODE_READ_DESC, UFS_QUERY_DESC_IDN_UNIT, 1, - 0, 0, &utrd, &rsp_upiu); - g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_DESC, + UFS_QUERY_DESC_IDN_UNIT, 1, 0, 0, &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS); g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS); g_assert_cmpuint(rsp_upiu.qr.data[0], ==, sizeof(UnitDescriptor)); g_assert_cmpuint(rsp_upiu.qr.data[1], ==, UFS_QUERY_DESC_IDN_UNIT); g_assert_cmpuint(rsp_upiu.qr.data[2], ==, 1); - ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, - UFS_UPIU_QUERY_OPCODE_READ_DESC, UFS_QUERY_DESC_IDN_UNIT, - UFS_UPIU_RPMB_WLUN, 0, 0, &utrd, &rsp_upiu); - g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + ocs = + ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_DESC, UFS_QUERY_DESC_IDN_UNIT, + UFS_UPIU_RPMB_WLUN, 0, 0, &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS); g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS); g_assert_cmpuint(rsp_upiu.qr.data[0], ==, sizeof(RpmbUnitDescriptor)); g_assert_cmpuint(rsp_upiu.qr.data[1], ==, UFS_QUERY_DESC_IDN_UNIT); g_assert_cmpuint(rsp_upiu.qr.data[2], ==, UFS_UPIU_RPMB_WLUN); /* Read Interconnect Descriptor */ - ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, - UFS_UPIU_QUERY_OPCODE_READ_DESC, - UFS_QUERY_DESC_IDN_INTERCONNECT, 0, 0, 0, &utrd, &rsp_upiu); - g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_DESC, + UFS_QUERY_DESC_IDN_INTERCONNECT, 0, 0, 0, &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS); g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS); g_assert_cmpuint(rsp_upiu.qr.data[0], ==, sizeof(InterconnectDescriptor)); g_assert_cmpuint(rsp_upiu.qr.data[1], ==, UFS_QUERY_DESC_IDN_INTERCONNECT); /* Read String Descriptor */ - ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, - UFS_UPIU_QUERY_OPCODE_READ_DESC, UFS_QUERY_DESC_IDN_STRING, - 0, 0, 0, &utrd, &rsp_upiu); - g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_DESC, + UFS_QUERY_DESC_IDN_STRING, 0, 0, 0, &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS); g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS); g_assert_cmpuint(rsp_upiu.qr.data[0], ==, 0x12); g_assert_cmpuint(rsp_upiu.qr.data[1], ==, UFS_QUERY_DESC_IDN_STRING); - ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, - UFS_UPIU_QUERY_OPCODE_READ_DESC, UFS_QUERY_DESC_IDN_STRING, - 1, 0, 0, &utrd, &rsp_upiu); - g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_DESC, + UFS_QUERY_DESC_IDN_STRING, 1, 0, 0, &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS); g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS); g_assert_cmpuint(rsp_upiu.qr.data[0], ==, 0x22); g_assert_cmpuint(rsp_upiu.qr.data[1], ==, UFS_QUERY_DESC_IDN_STRING); - ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, - UFS_UPIU_QUERY_OPCODE_READ_DESC, UFS_QUERY_DESC_IDN_STRING, - 4, 0, 0, &utrd, &rsp_upiu); - g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_DESC, + UFS_QUERY_DESC_IDN_STRING, 4, 0, 0, &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS); g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS); g_assert_cmpuint(rsp_upiu.qr.data[0], ==, 0x0a); g_assert_cmpuint(rsp_upiu.qr.data[1], ==, UFS_QUERY_DESC_IDN_STRING); /* Read Geometry Descriptor */ - ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, - UFS_UPIU_QUERY_OPCODE_READ_DESC, UFS_QUERY_DESC_IDN_GEOMETRY, - 0, 0, 0, &utrd, &rsp_upiu); - g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_DESC, + UFS_QUERY_DESC_IDN_GEOMETRY, 0, 0, 0, &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS); g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS); g_assert_cmpuint(rsp_upiu.qr.data[0], ==, sizeof(GeometryDescriptor)); g_assert_cmpuint(rsp_upiu.qr.data[1], ==, UFS_QUERY_DESC_IDN_GEOMETRY); /* Read Power Descriptor */ - ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, - UFS_UPIU_QUERY_OPCODE_READ_DESC, UFS_QUERY_DESC_IDN_POWER, 0, - 0, 0, &utrd, &rsp_upiu); - g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_DESC, + UFS_QUERY_DESC_IDN_POWER, 0, 0, 0, &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS); g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS); g_assert_cmpuint(rsp_upiu.qr.data[0], ==, sizeof(PowerParametersDescriptor)); g_assert_cmpuint(rsp_upiu.qr.data[1], ==, UFS_QUERY_DESC_IDN_POWER); /* Read Health Descriptor */ - ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, - UFS_UPIU_QUERY_OPCODE_READ_DESC, UFS_QUERY_DESC_IDN_HEALTH, - 0, 0, 0, &utrd, &rsp_upiu); - g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, UFS_OCS_SUCCESS); + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_DESC, + UFS_QUERY_DESC_IDN_HEALTH, 0, 0, 0, &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_SUCCESS); g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_COMMAND_RESULT_SUCCESS); g_assert_cmpuint(rsp_upiu.qr.data[0], ==, sizeof(DeviceHealthDescriptor)); g_assert_cmpuint(rsp_upiu.qr.data[1], ==, UFS_QUERY_DESC_IDN_HEALTH); /* Invalid Index (Intended Failure) */ - ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, - UFS_UPIU_QUERY_OPCODE_READ_DESC, UFS_QUERY_DESC_IDN_UNIT, 4, - 0, 0, &utrd, &rsp_upiu); - g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, - UFS_OCS_INVALID_CMD_TABLE_ATTR); + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_DESC, + UFS_QUERY_DESC_IDN_UNIT, 4, 0, 0, &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_INVALID_CMD_TABLE_ATTR); g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_QUERY_RESULT_INVALID_INDEX); - ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, - UFS_UPIU_QUERY_OPCODE_READ_DESC, UFS_QUERY_DESC_IDN_STRING, - 5, 0, 0, &utrd, &rsp_upiu); - g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, - UFS_OCS_INVALID_CMD_TABLE_ATTR); + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_DESC, + UFS_QUERY_DESC_IDN_STRING, 5, 0, 0, &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_INVALID_CMD_TABLE_ATTR); g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_QUERY_RESULT_INVALID_INDEX); /* Invalid Selector (Intended Failure) */ - ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, - UFS_UPIU_QUERY_OPCODE_READ_DESC, UFS_QUERY_DESC_IDN_DEVICE, - 0, 1, 0, &utrd, &rsp_upiu); - g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, - UFS_OCS_INVALID_CMD_TABLE_ATTR); + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_DESC, + UFS_QUERY_DESC_IDN_DEVICE, 0, 1, 0, &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_INVALID_CMD_TABLE_ATTR); g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_QUERY_RESULT_INVALID_SELECTOR); - ufs_send_query(ufs, 0, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, - UFS_UPIU_QUERY_OPCODE_READ_DESC, UFS_QUERY_DESC_IDN_STRING, - 0, 1, 0, &utrd, &rsp_upiu); - g_assert_cmpuint(le32_to_cpu(utrd.header.dword_2), ==, - UFS_OCS_INVALID_CMD_TABLE_ATTR); + ocs = ufs_send_query(ufs, UFS_UPIU_QUERY_FUNC_STANDARD_READ_REQUEST, + UFS_UPIU_QUERY_OPCODE_READ_DESC, + UFS_QUERY_DESC_IDN_STRING, 0, 1, 0, &rsp_upiu); + g_assert_cmpuint(ocs, ==, UFS_OCS_INVALID_CMD_TABLE_ATTR); g_assert_cmpuint(rsp_upiu.header.response, ==, UFS_QUERY_RESULT_INVALID_SELECTOR); @@ -947,12 +1098,16 @@ static void ufs_register_nodes(void) QOSGraphEdgeOptions edge_opts = { .before_cmd_line = "-blockdev null-co,node-name=drv0,read-zeroes=on", .after_cmd_line = "-device ufs-lu,bus=ufs0,drive=drv0,lun=0", - .extra_device_opts = "addr=04.0,id=ufs0,nutrs=32,nutmrs=8" + .extra_device_opts = "addr=04.0,id=ufs0" }; - QOSGraphTestOptions io_test_opts = { - .before = ufs_blk_test_setup, - }; + QOSGraphTestOptions io_test_opts = { .before = ufs_blk_test_setup, + .edge.extra_device_opts = + "mcq=false,nutrs=32,nutmrs=8" }; + + QOSGraphTestOptions mcq_test_opts = { .before = ufs_blk_test_setup, + .edge.extra_device_opts = + "mcq=true,mcq-maxq=1" }; add_qpci_address(&edge_opts, &(QPCIAddress){ .devfn = QPCI_DEVFN(4, 0) }); @@ -972,13 +1127,14 @@ static void ufs_register_nodes(void) return; } qos_add_test("init", "ufs", ufstest_init, NULL); - qos_add_test("read-write", "ufs", ufstest_read_write, &io_test_opts); - qos_add_test("flag read-write", "ufs", - ufstest_query_flag_request, &io_test_opts); - qos_add_test("attr read-write", "ufs", - ufstest_query_attr_request, &io_test_opts); - qos_add_test("desc read-write", "ufs", - ufstest_query_desc_request, &io_test_opts); + qos_add_test("legacy-read-write", "ufs", ufstest_read_write, &io_test_opts); + qos_add_test("mcq-read-write", "ufs", ufstest_read_write, &mcq_test_opts); + qos_add_test("query-flag", "ufs", ufstest_query_flag_request, + &io_test_opts); + qos_add_test("query-attribute", "ufs", ufstest_query_attr_request, + &io_test_opts); + qos_add_test("query-desciptor", "ufs", ufstest_query_desc_request, + &io_test_opts); } libqos_init(ufs_register_nodes); diff --git a/tests/qtest/vhost-user-test.c b/tests/qtest/vhost-user-test.c index d607500..75cb3e4 100644 --- a/tests/qtest/vhost-user-test.c +++ b/tests/qtest/vhost-user-test.c @@ -12,7 +12,7 @@ #include "libqtest-single.h" #include "qapi/error.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "qemu/config-file.h" #include "qemu/option.h" #include "qemu/range.h" @@ -20,7 +20,7 @@ #include "chardev/char-fe.h" #include "qemu/memfd.h" #include "qemu/module.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "libqos/libqos.h" #include "libqos/pci-pc.h" #include "libqos/virtio-pci.h" @@ -920,7 +920,7 @@ static void wait_for_rings_started(TestServer *s, size_t count) static inline void test_server_connect(TestServer *server) { - test_server_create_chr(server, ",reconnect=1"); + test_server_create_chr(server, ",reconnect-ms=1000"); } static gboolean @@ -1043,7 +1043,8 @@ static void test_multiqueue(void *obj, void *arg, QGuestAllocator *alloc) static uint64_t vu_net_get_features(TestServer *s) { - uint64_t features = 0x1ULL << VHOST_F_LOG_ALL | + uint64_t features = 0x1ULL << VIRTIO_F_VERSION_1 | + 0x1ULL << VHOST_F_LOG_ALL | 0x1ULL << VHOST_USER_F_PROTOCOL_FEATURES; if (s->queues > 1) { diff --git a/tests/qtest/virtio-9p-test.c b/tests/qtest/virtio-9p-test.c index 3c8cd23..ac38ccf 100644 --- a/tests/qtest/virtio-9p-test.c +++ b/tests/qtest/virtio-9p-test.c @@ -20,6 +20,7 @@ #define tversion(...) v9fs_tversion((TVersionOpt) __VA_ARGS__) #define tattach(...) v9fs_tattach((TAttachOpt) __VA_ARGS__) #define tgetattr(...) v9fs_tgetattr((TGetAttrOpt) __VA_ARGS__) +#define tsetattr(...) v9fs_tsetattr((TSetAttrOpt) __VA_ARGS__) #define treaddir(...) v9fs_treaddir((TReadDirOpt) __VA_ARGS__) #define tlopen(...) v9fs_tlopen((TLOpenOpt) __VA_ARGS__) #define twrite(...) v9fs_twrite((TWriteOpt) __VA_ARGS__) @@ -693,6 +694,64 @@ static void fs_unlinkat_hardlink(void *obj, void *data, g_assert(stat(real_file, &st_real) == 0); } +static void fs_use_after_unlink(void *obj, void *data, + QGuestAllocator *t_alloc) +{ + QVirtio9P *v9p = obj; + v9fs_set_allocator(t_alloc); + static const uint32_t write_count = P9_MAX_SIZE / 2; + g_autofree char *real_file = virtio_9p_test_path("09/doa_file"); + g_autofree char *buf = g_malloc0(write_count); + struct stat st_file; + struct v9fs_attr attr; + uint32_t fid_file; + uint32_t count; + + tattach({ .client = v9p }); + + /* create a file "09/doa_file" and make sure it exists and is regular */ + tmkdir({ .client = v9p, .atPath = "/", .name = "09" }); + tlcreate({ .client = v9p, .atPath = "09", .name = "doa_file" }); + g_assert(stat(real_file, &st_file) == 0); + g_assert((st_file.st_mode & S_IFMT) == S_IFREG); + + /* request a FID for that regular file that we can work with next */ + fid_file = twalk({ + .client = v9p, .fid = 0, .path = "09/doa_file" + }).newfid; + g_assert(fid_file != 0); + + /* now first open the file in write mode before ... */ + tlopen({ .client = v9p, .fid = fid_file, .flags = O_WRONLY }); + /* ... removing the file from file system */ + tunlinkat({ .client = v9p, .atPath = "09", .name = "doa_file" }); + + /* file is removed, but we still have it open, so this should succeed */ + tgetattr({ + .client = v9p, .fid = fid_file, .request_mask = P9_GETATTR_BASIC, + .rgetattr.attr = &attr + }); + count = twrite({ + .client = v9p, .fid = fid_file, .offset = 0, .count = write_count, + .data = buf + }).count; + g_assert_cmpint(count, ==, write_count); + + /* truncate file to (arbitrarily chosen) size 2001 */ + tsetattr({ + .client = v9p, .fid = fid_file, .attr = (v9fs_attr) { + .valid = P9_SETATTR_SIZE, + .size = 2001 + } + }); + /* truncate apparently succeeded, let's double-check the size */ + tgetattr({ + .client = v9p, .fid = fid_file, .request_mask = P9_GETATTR_BASIC, + .rgetattr.attr = &attr + }); + g_assert_cmpint(attr.size, ==, 2001); +} + static void cleanup_9p_local_driver(void *data) { /* remove previously created test dir when test is completed */ @@ -758,6 +817,8 @@ static void register_virtio_9p_test(void) qos_add_test("local/hardlink_file", "virtio-9p", fs_hardlink_file, &opts); qos_add_test("local/unlinkat_hardlink", "virtio-9p", fs_unlinkat_hardlink, &opts); + qos_add_test("local/use_after_unlink", "virtio-9p", fs_use_after_unlink, + &opts); } libqos_init(register_virtio_9p_test); diff --git a/tests/qtest/virtio-balloon-test.c b/tests/qtest/virtio-balloon-test.c new file mode 100644 index 0000000..ecdd363 --- /dev/null +++ b/tests/qtest/virtio-balloon-test.c @@ -0,0 +1,57 @@ +/* + * QTest test cases for virtio balloon device + * + * Copyright (c) 2024 Gao Shiyuan <gaoshiyuan@baidu.com> + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "standard-headers/linux/virtio_balloon.h" + +/* + * https://gitlab.com/qemu-project/qemu/-/issues/2576 + * Used to trigger: + * virtio_address_space_lookup: Assertion `mrs.mr' failed. + */ +static void oss_fuzz_71649(void) +{ + QTestState *s = qtest_init("-device virtio-balloon -machine q35" + " -nodefaults"); + + qtest_outl(s, 0xcf8, 0x80000890); + qtest_outl(s, 0xcfc, 0x2); + qtest_outl(s, 0xcf8, 0x80000891); + qtest_inl(s, 0xcfc); + qtest_quit(s); +} + +static void query_stats(void) +{ + QTestState *s = qtest_init("-device virtio-balloon,id=balloon" + " -nodefaults"); + QDict *ret = qtest_qmp_assert_success_ref( + s, + "{ 'execute': 'qom-get', 'arguments': " \ + "{ 'path': '/machine/peripheral/balloon', " \ + " 'property': 'guest-stats' } }"); + QDict *stats = qdict_get_qdict(ret, "stats"); + + /* We expect 1 entry in the dict for each known kernel stat */ + assert(qdict_size(stats) == VIRTIO_BALLOON_S_NR); + + qobject_unref(ret); + qtest_quit(s); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + qtest_add_func("virtio-balloon/oss_fuzz_71649", oss_fuzz_71649); + qtest_add_func("virtio-balloon/query-stats", query_stats); + + return g_test_run(); +} + diff --git a/tests/qtest/virtio-iommu-test.c b/tests/qtest/virtio-iommu-test.c index afb2259..98ffa27 100644 --- a/tests/qtest/virtio-iommu-test.c +++ b/tests/qtest/virtio-iommu-test.c @@ -105,7 +105,7 @@ static int send_map(QTestState *qts, QVirtioIOMMU *v_iommu, QVirtQueue *vq = v_iommu->vq; uint64_t ro_addr, wr_addr; uint32_t free_head; - struct virtio_iommu_req_map req; + struct virtio_iommu_req_map req = {}; size_t ro_size = sizeof(req) - sizeof(struct virtio_iommu_req_tail); size_t wr_size = sizeof(struct virtio_iommu_req_tail); struct virtio_iommu_req_tail buffer; @@ -147,7 +147,7 @@ static int send_unmap(QTestState *qts, QVirtioIOMMU *v_iommu, QVirtQueue *vq = v_iommu->vq; uint64_t ro_addr, wr_addr; uint32_t free_head; - struct virtio_iommu_req_unmap req; + struct virtio_iommu_req_unmap req = {}; size_t ro_size = sizeof(req) - sizeof(struct virtio_iommu_req_tail); size_t wr_size = sizeof(struct virtio_iommu_req_tail); struct virtio_iommu_req_tail buffer; diff --git a/tests/qtest/virtio-net-failover.c b/tests/qtest/virtio-net-failover.c index 73dfabc..5baf81c 100644 --- a/tests/qtest/virtio-net-failover.c +++ b/tests/qtest/virtio-net-failover.c @@ -11,10 +11,11 @@ #include "libqtest.h" #include "libqos/pci.h" #include "libqos/pci-pc.h" -#include "migration-helpers.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qlist.h" -#include "qapi/qmp/qjson.h" +#include "migration/migration-qmp.h" +#include "migration/migration-util.h" +#include "qobject/qdict.h" +#include "qobject/qlist.h" +#include "qobject/qjson.h" #include "libqos/malloc-pc.h" #include "libqos/virtio-pci.h" #include "hw/pci/pci.h" @@ -772,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"); @@ -894,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); @@ -1021,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); @@ -1746,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/qtest/virtio-net-test.c b/tests/qtest/virtio-net-test.c index 2df75c9..60e5229 100644 --- a/tests/qtest/virtio-net-test.c +++ b/tests/qtest/virtio-net-test.c @@ -11,7 +11,7 @@ #include "libqtest-single.h" #include "qemu/iov.h" #include "qemu/module.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "hw/virtio/virtio-net.h" #include "libqos/qgraph.h" #include "libqos/virtio-net.h" diff --git a/tests/qtest/vmcoreinfo-test.c b/tests/qtest/vmcoreinfo-test.c new file mode 100644 index 0000000..dcf3b5a --- /dev/null +++ b/tests/qtest/vmcoreinfo-test.c @@ -0,0 +1,90 @@ +/* + * qtest vmcoreinfo test case + * + * Copyright Red Hat. 2025. + * + * Authors: + * Ani Sinha <anisinha@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. + */ + +#include "qemu/osdep.h" +#include "qemu/units.h" +#include "libqos/libqos-pc.h" +#include "libqtest.h" +#include "standard-headers/linux/qemu_fw_cfg.h" +#include "libqos/fw_cfg.h" +#include "qemu/bswap.h" +#include "hw/misc/vmcoreinfo.h" + +static void test_vmcoreinfo_write_basic(void) +{ + QFWCFG *fw_cfg; + QOSState *qs; + FWCfgVMCoreInfo info; + size_t filesize; + uint16_t guest_format; + uint16_t host_format; + uint32_t size; + uint64_t paddr; + + qs = qtest_pc_boot("-device vmcoreinfo"); + fw_cfg = pc_fw_cfg_init(qs->qts); + + memset(&info, 0 , sizeof(info)); + /* read vmcoreinfo and read back the host format */ + filesize = qfw_cfg_read_file(fw_cfg, qs, FW_CFG_VMCOREINFO_FILENAME, + &info, sizeof(info)); + g_assert_cmpint(filesize, ==, sizeof(info)); + + host_format = le16_to_cpu(info.host_format); + g_assert_cmpint(host_format, ==, FW_CFG_VMCOREINFO_FORMAT_ELF); + + memset(&info, 0 , sizeof(info)); + info.guest_format = cpu_to_le16(FW_CFG_VMCOREINFO_FORMAT_ELF); + info.size = cpu_to_le32(1 * MiB); + info.paddr = cpu_to_le64(0xffffff00); + info.host_format = cpu_to_le16(host_format); + + /* write the values to the host */ + filesize = qfw_cfg_write_file(fw_cfg, qs, FW_CFG_VMCOREINFO_FILENAME, + &info, sizeof(info)); + g_assert_cmpint(filesize, ==, sizeof(info)); + + memset(&info, 0 , sizeof(info)); + + /* now read back the values we wrote and compare that they are the same */ + filesize = qfw_cfg_read_file(fw_cfg, qs, FW_CFG_VMCOREINFO_FILENAME, + &info, sizeof(info)); + g_assert_cmpint(filesize, ==, sizeof(info)); + + size = le32_to_cpu(info.size); + paddr = le64_to_cpu(info.paddr); + guest_format = le16_to_cpu(info.guest_format); + + g_assert_cmpint(size, ==, 1 * MiB); + g_assert_cmpint(paddr, ==, 0xffffff00); + g_assert_cmpint(guest_format, ==, FW_CFG_VMCOREINFO_FORMAT_ELF); + + pc_fw_cfg_uninit(fw_cfg); + qtest_shutdown(qs); +} + +int main(int argc, char **argv) +{ + const char *arch = qtest_get_arch(); + + g_test_init(&argc, &argv, NULL); + + if (strcmp(arch, "i386") && strcmp(arch, "x86_64")) { + /* skip for non-x86 */ + exit(EXIT_SUCCESS); + } + + qtest_add_func("vmcoreinfo/basic-write", + test_vmcoreinfo_write_basic); + + return g_test_run(); +} diff --git a/tests/qtest/vmgenid-test.c b/tests/qtest/vmgenid-test.c index 29fee9e..33e96b7 100644 --- a/tests/qtest/vmgenid-test.c +++ b/tests/qtest/vmgenid-test.c @@ -15,7 +15,7 @@ #include "boot-sector.h" #include "acpi-utils.h" #include "libqtest.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #define VGID_GUID "324e6eaf-d1d1-4bf6-bf41-b9bb6c91fb87" #define VMGENID_GUID_OFFSET 40 /* allow space for @@ -61,7 +61,7 @@ static uint32_t acpi_find_vgia(QTestState *qts) /* The GUID is written at a fixed offset into the fw_cfg file * in order to implement the "OVMF SDT Header probe suppressor" - * see docs/specs/vmgenid.txt for more details + * see docs/specs/vmgenid.rst for more details */ guid_offset = le32_to_cpu(vgia_val) + VMGENID_GUID_OFFSET; g_free(table_aml); diff --git a/tests/qtest/wdt_ib700-test.c b/tests/qtest/wdt_ib700-test.c index 797288d..1754757 100644 --- a/tests/qtest/wdt_ib700-test.c +++ b/tests/qtest/wdt_ib700-test.c @@ -9,7 +9,7 @@ #include "qemu/osdep.h" #include "libqtest.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "qemu/timer.h" static void qmp_check_no_event(QTestState *s) diff --git a/tests/tcg/Makefile.target b/tests/tcg/Makefile.target index 452a2cd..af68f11 100644 --- a/tests/tcg/Makefile.target +++ b/tests/tcg/Makefile.target @@ -90,6 +90,7 @@ CFLAGS= LDFLAGS= QEMU_OPTS= +CHECK_PLUGIN_OUTPUT_COMMAND= # If TCG debugging, or TCI is enabled things are a lot slower @@ -102,9 +103,14 @@ ifeq ($(filter %-softmmu, $(TARGET)),) # then the target. If there are common tests shared between # sub-targets (e.g. ARM & AArch64) then it is up to # $(TARGET_NAME)/Makefile.target to include the common parent -# architecture in its VPATH. +# architecture in its VPATH. However some targets are so minimal we +# can't even build the multiarch tests. +ifneq ($(filter $(TARGET_NAME),aarch64_be),) +-include $(SRC_PATH)/tests/tcg/$(TARGET_NAME)/Makefile.target +else -include $(SRC_PATH)/tests/tcg/multiarch/Makefile.target -include $(SRC_PATH)/tests/tcg/$(TARGET_NAME)/Makefile.target +endif # Add the common build options CFLAGS+=-Wall -Werror -O0 -g -fno-strict-aliasing @@ -145,17 +151,23 @@ ifeq ($(CONFIG_PLUGIN),y) PLUGIN_SRC=$(SRC_PATH)/tests/tcg/plugins PLUGIN_LIB=../plugins VPATH+=$(PLUGIN_LIB) -PLUGINS=$(patsubst %.c, lib%.so, $(notdir $(wildcard $(PLUGIN_SRC)/*.c))) +# Some plugins need to be disabled for all tests to avoid exponential explosion. +# For example, libpatch.so only needs to run against the arch-specific patch +# target test, so we explicitly run it in the arch-specific Makefile. +DISABLE_PLUGINS=libpatch.so +PLUGINS=$(filter-out $(DISABLE_PLUGINS), \ + $(patsubst %.c, lib%.so, $(notdir $(wildcard $(PLUGIN_SRC)/*.c)))) # We need to ensure expand the run-plugin-TEST-with-PLUGIN # pre-requistes manually here as we can't use stems to handle it. We # only expand MULTIARCH_TESTS which are common on most of our targets # to avoid an exponential explosion as new tests are added. We also # add some special helpers the run-plugin- rules can use below. +# In more, extra tests can be added using ADDITIONAL_PLUGINS_TESTS variable. ifneq ($(MULTIARCH_TESTS),) $(foreach p,$(PLUGINS), \ - $(foreach t,$(MULTIARCH_TESTS),\ + $(foreach t,$(MULTIARCH_TESTS) $(ADDITIONAL_PLUGINS_TESTS),\ $(eval run-plugin-$(t)-with-$(p): $t $p) \ $(eval RUN_TESTS+=run-plugin-$(t)-with-$(p)))) endif # MULTIARCH_TESTS @@ -172,13 +184,17 @@ run-plugin-%-with-libmem.so: PLUGIN_ARGS=$(COMMA)inline=true ifeq ($(filter %-softmmu, $(TARGET)),) run-%: % - $(call run-test, $<, $(QEMU) $(QEMU_OPTS) $<) + $(call run-test, $<, env QEMU=$(QEMU) $(QEMU) $(QEMU_OPTS) $<) run-plugin-%: - $(call run-test, $@, $(QEMU) $(QEMU_OPTS) \ + $(call run-test, $@, env QEMU=$(QEMU) $(QEMU) $(QEMU_OPTS) \ -plugin $(PLUGIN_LIB)/$(call extract-plugin,$@)$(PLUGIN_ARGS) \ -d plugin -D $*.pout \ $(call strip-plugin,$<)) + $(if $(CHECK_PLUGIN_OUTPUT_COMMAND), \ + $(call quiet-command, $(CHECK_PLUGIN_OUTPUT_COMMAND) $*.pout, \ + TEST, check plugin $(call extract-plugin,$@) output \ + with $(call strip-plugin,$<))) else run-%: % $(call run-test, $<, \ @@ -193,6 +209,10 @@ run-plugin-%: -plugin $(PLUGIN_LIB)/$(call extract-plugin,$@)$(PLUGIN_ARGS) \ -d plugin -D $*.pout \ $(QEMU_OPTS) $(call strip-plugin,$<)) + $(if $(CHECK_PLUGIN_OUTPUT_COMMAND), \ + $(call quiet-command, $(CHECK_PLUGIN_OUTPUT_COMMAND) $*.pout, \ + TEST, check plugin $(call extract-plugin,$@) output \ + with $(call strip-plugin,$<))) endif gdb-%: % diff --git a/tests/tcg/aarch64/Makefile.softmmu-target b/tests/tcg/aarch64/Makefile.softmmu-target index 139e04d..f7a7d2b 100644 --- a/tests/tcg/aarch64/Makefile.softmmu-target +++ b/tests/tcg/aarch64/Makefile.softmmu-target @@ -2,14 +2,22 @@ # Aarch64 system tests # -AARCH64_SYSTEM_SRC=$(SRC_PATH)/tests/tcg/aarch64/system +AARCH64_SRC=$(SRC_PATH)/tests/tcg/aarch64 +AARCH64_SYSTEM_SRC=$(AARCH64_SRC)/system + VPATH+=$(AARCH64_SYSTEM_SRC) # These objects provide the basic boot code and helper functions for all tests CRT_OBJS=boot.o -AARCH64_TEST_SRCS=$(wildcard $(AARCH64_SYSTEM_SRC)/*.c) -AARCH64_TESTS = $(patsubst $(AARCH64_SYSTEM_SRC)/%.c, %, $(AARCH64_TEST_SRCS)) +AARCH64_TEST_C_SRCS=$(wildcard $(AARCH64_SYSTEM_SRC)/*.c) +AARCH64_TEST_S_SRCS=$(AARCH64_SYSTEM_SRC)/mte.S + +AARCH64_C_TESTS = $(patsubst $(AARCH64_SYSTEM_SRC)/%.c, %, $(AARCH64_TEST_C_SRCS)) +AARCH64_S_TESTS = $(patsubst $(AARCH64_SYSTEM_SRC)/%.S, %, $(AARCH64_TEST_S_SRCS)) + +AARCH64_TESTS = $(AARCH64_C_TESTS) +AARCH64_TESTS += $(AARCH64_S_TESTS) CRT_PATH=$(AARCH64_SYSTEM_SRC) LINK_SCRIPT=$(AARCH64_SYSTEM_SRC)/kernel.ld @@ -21,7 +29,8 @@ LDFLAGS+=-static -nostdlib $(CRT_OBJS) $(MINILIB_OBJS) -lgcc config-cc.mak: Makefile $(quiet-@)( \ - $(call cc-option,-march=armv8.3-a, CROSS_CC_HAS_ARMV8_3)) 3> config-cc.mak + $(call cc-option,-march=armv8.3-a, CROSS_CC_HAS_ARMV8_3); \ + $(call cc-option,-march=armv8.5-a+memtag, CROSS_CC_HAS_ARMV8_MTE)) 3> config-cc.mak -include config-cc.mak # building head blobs @@ -59,7 +68,8 @@ run-plugin-semiconsole-with-%: semiconsole # vtimer test needs EL2 QEMU_EL2_MACHINE=-machine virt,virtualization=on,gic-version=2 -cpu cortex-a57 -smp 4 -run-vtimer: QEMU_OPTS=$(QEMU_EL2_MACHINE) $(QEMU_BASE_ARGS) -kernel +QEMU_EL2_BASE_ARGS=-semihosting-config enable=on,target=native,chardev=output,arg="2" +run-vtimer: QEMU_OPTS=$(QEMU_EL2_MACHINE) $(QEMU_EL2_BASE_ARGS) -kernel # Simple Record/Replay Test .PHONY: memory-record @@ -82,9 +92,44 @@ EXTRA_RUNS+=run-memory-replay ifneq ($(CROSS_CC_HAS_ARMV8_3),) pauth-3: CFLAGS += $(CROSS_CC_HAS_ARMV8_3) +# This test explicitly checks the output of the pauth operation so we +# must force the use of the QARMA5 algorithm for it. +run-pauth-3: QEMU_BASE_MACHINE=-M virt -cpu max,pauth-qarma5=on -display none else pauth-3: $(call skip-test, "BUILD of $@", "missing compiler support") run-pauth-3: $(call skip-test, "RUN of pauth-3", "not built") endif + +ifneq ($(CROSS_CC_HAS_ARMV8_MTE),) +QEMU_MTE_ENABLED_MACHINE=-M virt,mte=on -cpu max -display none +QEMU_OPTS_WITH_MTE_ON = $(QEMU_MTE_ENABLED_MACHINE) $(QEMU_BASE_ARGS) -kernel +mte: CFLAGS+=-march=armv8.5-a+memtag +mte: mte.S $(LINK_SCRIPT) $(CRT_OBJS) $(MINILIB_OBJS) + $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $< -o $@ $(LDFLAGS) + +run-mte: QEMU_OPTS=$(QEMU_OPTS_WITH_MTE_ON) +run-mte: mte + +ifeq ($(GDB_SUPPORTS_MTE_IN_BAREMETAL),y) +run-gdbstub-mte: QEMU_OPTS=$(QEMU_OPTS_WITH_MTE_ON) +run-gdbstub-mte: mte + $(call run-test, $@, $(GDB_SCRIPT) \ + --output run-gdbstub-mte.out \ + --gdb $(GDB) \ + --qemu $(QEMU) --qargs "-chardev null$(COMMA)id=output $(QEMU_OPTS)" \ + --bin $< --test $(AARCH64_SRC)/gdbstub/test-mte.py -- --mode=system, \ + gdbstub MTE support) + +EXTRA_RUNS += run-gdbstub-mte +else # !GDB_SUPPORTS_MTE_IN_BAREMETAL +run-gdbstub-mte: + $(call skip-test "RUN of gdbstub-mte", "GDB does not support MTE in baremetal!") +endif +else # !CROSS_CC_HAS_ARMV8_MTE +mte: + $(call skip-test, "BUILD of $@", "missing compiler support") +run-mte: + $(call skip-test, "RUN of mte", "not build") +endif diff --git a/tests/tcg/aarch64/Makefile.target b/tests/tcg/aarch64/Makefile.target index 8cc62eb..16ddcf4 100644 --- a/tests/tcg/aarch64/Makefile.target +++ b/tests/tcg/aarch64/Makefile.target @@ -83,7 +83,8 @@ test-aes: CFLAGS += -O -march=armv8-a+aes test-aes: test-aes-main.c.inc # Vector SHA1 -sha1-vector: CFLAGS=-O3 +# Work around compiler false-positive warning, as we do for the 'sha1' test +sha1-vector: CFLAGS=-O3 -Wno-stringop-overread sha1-vector: sha1.c $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $< -o $@ $(LDFLAGS) run-sha1-vector: sha1-vector run-sha1 @@ -138,7 +139,8 @@ run-gdbstub-mte: mte-8 $(call run-test, $@, $(GDB_SCRIPT) \ --gdb $(GDB) \ --qemu $(QEMU) --qargs "$(QEMU_OPTS)" \ - --bin $< --test $(AARCH64_SRC)/gdbstub/test-mte.py, \ + --bin $< --test $(AARCH64_SRC)/gdbstub/test-mte.py \ + -- --mode=user, \ gdbstub MTE support) EXTRA_RUNS += run-gdbstub-mte diff --git a/tests/tcg/aarch64/gdbstub/test-mte.py b/tests/tcg/aarch64/gdbstub/test-mte.py index 66f9c25..9ad98e7 100644 --- a/tests/tcg/aarch64/gdbstub/test-mte.py +++ b/tests/tcg/aarch64/gdbstub/test-mte.py @@ -1,34 +1,59 @@ from __future__ import print_function # # Test GDB memory-tag commands that exercise the stubs for the qIsAddressTagged, -# qMemTag, and QMemTag packets. Logical tag-only commands rely on local -# operations, hence don't exercise any stub. +# qMemTag, and QMemTag packets, which are used for manipulating allocation tags. +# Logical tags-related commands rely on local operations, hence don't exercise +# any stub and so are not used in this test. # -# The test consists in breaking just after a atag() call (which sets the -# allocation tag -- see mte-8.c for details) and setting/getting tags in -# different memory locations and ranges starting at the address of the array -# 'a'. +# The test consists in breaking just after a tag is set in a specific memory +# chunk, and then using the GDB 'memory-tagging' subcommands to set/get tags in +# different memory locations and ranges in the MTE-enabled memory chunk. # # This is launched via tests/guest-debug/run-test.py # -import gdb +try: + import gdb +except ModuleNotFoundError: + from sys import exit + exit("This script must be launched via tests/guest-debug/run-test.py!") import re -from test_gdbstub import main, report +from sys import argv +from test_gdbstub import arg_parser, main, report -PATTERN_0 = "Memory tags for address 0x[0-9a-f]+ match \\(0x[0-9a-f]+\\)." -PATTERN_1 = ".*(0x[0-9a-f]+)" +PATTERN_0 = r"Memory tags for address 0x[0-9a-f]+ match \(0x[0-9a-f]+\)." +PATTERN_1 = r".*(0x[0-9a-f]+)" def run_test(): - gdb.execute("break 95", False, True) + p = arg_parser(prog="test-mte.py", description="TCG MTE tests.") + p.add_argument("--mode", help="Run test for QEMU system or user mode.", + required=True, choices=['system','user']) + + args = p.parse_args(args=argv) + + if args.mode == "system": + # Break address: where to break before performing the tests + # See mte.S for details about this label. + ba = "main_end" + # Tagged address: the start of the MTE-enabled memory chunk to be tested + # 'tagged_addr' (x1) is a pointer to the MTE-enabled page. See mte.S. + ta = "$x1" + else: # mode="user" + # Line 95 in mte-8.c + ba = "95" + # 'a' array. See mte-8.c + ta = "a" + + gdb.execute(f"break {ba}", False, True) gdb.execute("continue", False, True) + try: - # Test if we can check correctly that the allocation tag for - # array 'a' matches the logical tag after atag() is called. - co = gdb.execute("memory-tag check a", False, True) + # Test if we can check correctly that the allocation tag for the address + # in {ta} matches the logical tag in {ta}. + co = gdb.execute(f"memory-tag check {ta}", False, True) tags_match = re.findall(PATTERN_0, co, re.MULTILINE) if tags_match: report(True, f"{tags_match[0]}") @@ -39,20 +64,20 @@ def run_test(): # tags rely on local operation and so don't exercise any stub. # Set the allocation tag for the first granule (16 bytes) of - # address starting at 'a' address to a known value, i.e. 0x04. - gdb.execute("memory-tag set-allocation-tag a 1 04", False, True) + # address starting at {ta} address to a known value, i.e. 0x04. + gdb.execute(f"memory-tag set-allocation-tag {ta} 1 04", False, True) # Then set the allocation tag for the second granule to a known # value, i.e. 0x06. This tests that contiguous tag granules are - # set correct and don't run over each other. - gdb.execute("memory-tag set-allocation-tag a+16 1 06", False, True) + # set correctly and don't run over each other. + gdb.execute(f"memory-tag set-allocation-tag {ta}+16 1 06", False, True) # Read the known values back and check if they remain the same. - co = gdb.execute("memory-tag print-allocation-tag a", False, True) + co = gdb.execute(f"memory-tag print-allocation-tag {ta}", False, True) first_tag = re.match(PATTERN_1, co)[1] - co = gdb.execute("memory-tag print-allocation-tag a+16", False, True) + co = gdb.execute(f"memory-tag print-allocation-tag {ta}+16", False, True) second_tag = re.match(PATTERN_1, co)[1] if first_tag == "0x4" and second_tag == "0x6": @@ -61,15 +86,15 @@ def run_test(): report(False, "Can't set/print allocation tags!") # Now test fill pattern by setting a whole page with a pattern. - gdb.execute("memory-tag set-allocation-tag a 4096 0a0b", False, True) + gdb.execute(f"memory-tag set-allocation-tag {ta} 4096 0a0b", False, True) # And read back the tags of the last two granules in page so # we also test if the pattern is set correctly up to the end of # the page. - co = gdb.execute("memory-tag print-allocation-tag a+4096-32", False, True) + co = gdb.execute(f"memory-tag print-allocation-tag {ta}+4096-32", False, True) tag = re.match(PATTERN_1, co)[1] - co = gdb.execute("memory-tag print-allocation-tag a+4096-16", False, True) + co = gdb.execute(f"memory-tag print-allocation-tag {ta}+4096-16", False, True) last_tag = re.match(PATTERN_1, co)[1] if tag == "0xa" and last_tag == "0xb": @@ -78,8 +103,8 @@ def run_test(): report(False, "Fill pattern failed!") except gdb.error: - # This usually happens because a GDB version that does not - # support memory tagging was used to run the test. + # This usually happens because a GDB version that does not support + # memory tagging was used to run the test. report(False, "'memory-tag' command failed!") diff --git a/tests/tcg/aarch64/system/boot.S b/tests/tcg/aarch64/system/boot.S index 501685d..8bfa4e4 100644 --- a/tests/tcg/aarch64/system/boot.S +++ b/tests/tcg/aarch64/system/boot.S @@ -16,6 +16,7 @@ #define semihosting_call hlt 0xf000 #define SYS_WRITEC 0x03 /* character to debug channel */ #define SYS_WRITE0 0x04 /* string to debug channel */ +#define SYS_GET_CMDLINE 0x15 /* get command line */ #define SYS_EXIT 0x18 .align 12 @@ -70,22 +71,172 @@ lower_a32_sync: lower_a32_irq: lower_a32_fiq: lower_a32_serror: + adr x1, .unexp_excp +exit_msg: mov x0, SYS_WRITE0 - adr x1, .error - semihosting_call - mov x0, SYS_EXIT - mov x1, 1 semihosting_call + mov x0, 1 /* EXIT_FAILURE */ + bl _exit /* never returns */ .section .rodata -.error: - .string "Terminated by exception.\n" +.unexp_excp: + .string "Unexpected exception.\n" +.high_el_msg: + .string "Started in lower EL than requested.\n" +.unexp_el0: + .string "Started in invalid EL.\n" + + .align 8 +.get_cmd: + .quad cmdline + .quad 128 .text .align 4 .global __start __start: + /* + * Initialise the stack for whatever EL we are in before + * anything else, we need it to be able to _exit cleanly. + * It's smaller than the stack we pass to the C code but we + * don't need much. + */ + adrp x0, system_stack_end + add x0, x0, :lo12:system_stack_end + mov sp, x0 + + /* + * The test can set the semihosting command line to the target + * EL needed for the test. However if no semihosting args are set we will + * end up with -kernel/-append data (see semihosting_arg_fallback). + * Keep the normalised target in w11. + */ + mov x0, SYS_GET_CMDLINE + adr x1, .get_cmd + semihosting_call + adrp x10, cmdline + add x10, x10, :lo12:cmdline + ldrb w11, [x10] + + /* sanity check, normalise char to EL, clamp to 1 if outside range */ + subs w11, w11, #'0' + b.lt el_default + cmp w11, #3 + b.gt el_default + b 1f + +el_high: + adr x1, .high_el_msg + b exit_msg + +el_default: + mov w11, #1 + +1: + /* Determine current Exception Level */ + mrs x0, CurrentEL + lsr x0, x0, #2 /* CurrentEL[3:2] contains the current EL */ + + /* Are we already in a lower EL than we want? */ + cmp w11, w0 + bgt el_high + + /* Branch based on current EL */ + cmp x0, #3 + b.eq setup_el3 + cmp x0, #2 + b.eq setup_el2 + cmp x0, #1 + b.eq at_testel /* Already at EL1, skip transition */ + + /* Should not be at EL0 - error out */ + adr x1, .unexp_el0 + b exit_msg + +setup_el3: + /* Ensure we trap if we get anything wrong */ + adr x0, vector_table + msr vbar_el3, x0 + + /* Does the test want to be at EL3? */ + cmp w11, #3 + beq at_testel + + /* Configure EL3 to for lower states (EL2 or EL1) */ + mrs x0, scr_el3 + orr x0, x0, #(1 << 10) /* RW = 1: EL2/EL1 execution state is AArch64 */ + orr x0, x0, #(1 << 0) /* NS = 1: Non-secure state */ + msr scr_el3, x0 + + /* + * We need to check if EL2 is actually enabled via ID_AA64PFR0_EL1, + * otherwise we should just jump straight to EL1. + */ + mrs x0, id_aa64pfr0_el1 + ubfx x0, x0, #8, #4 /* Extract EL2 field (bits 11:8) */ + cbz x0, el2_not_present /* If field is 0 no EL2 */ + + + /* Prepare SPSR for exception return to EL2 */ + mov x0, #0x3c9 /* DAIF bits and EL2h mode (9) */ + msr spsr_el3, x0 + + /* Set EL2 entry point */ + adr x0, setup_el2 + msr elr_el3, x0 + + /* Return to EL2 */ + eret + +el2_not_present: + /* Initialize SCTLR_EL1 with reset value */ + msr sctlr_el1, xzr + + /* Set EL1 entry point */ + adr x0, at_testel + msr elr_el3, x0 + + /* Prepare SPSR for exception return to EL1h with interrupts masked */ + mov x0, #0x3c5 /* DAIF bits and EL1h mode (5) */ + msr spsr_el3, x0 + + isb /* Synchronization barrier */ + eret /* Jump to EL1 */ + +setup_el2: + /* Ensure we trap if we get anything wrong */ + adr x0, vector_table + msr vbar_el2, x0 + + /* Does the test want to be at EL2? */ + cmp w11, #2 + beq at_testel + + /* Configure EL2 to allow transition to EL1 */ + mrs x0, hcr_el2 + orr x0, x0, #(1 << 31) /* RW = 1: EL1 execution state is AArch64 */ + msr hcr_el2, x0 + + /* Initialize SCTLR_EL1 with reset value */ + msr sctlr_el1, xzr + + /* Set EL1 entry point */ + adr x0, at_testel + msr elr_el2, x0 + + /* Prepare SPSR for exception return to EL1 */ + mov x0, #(0x5 << 0) /* EL1h (SPx), with interrupts disabled */ + msr spsr_el2, x0 + + /* Return to EL1 */ + eret + + /* + * At the target EL for the test, usually EL1. Note we still + * set everything up as if we were at EL1. + */ +at_testel: /* Installs a table of exception vectors to catch and handle all exceptions by terminating the process with a diagnostic. */ adr x0, vector_table @@ -101,7 +252,7 @@ __start: * maps RAM to the first Gb. The stage2 tables have two 2mb * translation block entries covering a series of adjacent * 4k pages. - */ + */ /* Stage 1 entry: indexed by IA[38:30] */ adr x1, . /* phys address */ @@ -135,6 +286,17 @@ __start: orr x1, x1, x3 str x1, [x2] /* 2nd 2mb (.data & .bss)*/ + /* Third block: at 'mte_page', set in kernel.ld */ + adrp x1, mte_page + add x1, x1, :lo12:mte_page + bic x1, x1, #(1 << 21) - 1 + and x4, x1, x5 + add x2, x0, x4, lsr #(21 - 3) + /* attr(AF, NX, block, AttrIndx=Attr1) */ + ldr x3, =(3 << 53) | 0x401 | (1 << 2) + orr x1, x1, x3 + str x1, [x2] + /* Setup/enable the MMU. */ /* @@ -188,7 +350,8 @@ __start: orr x0, x0, #(3 << 16) msr cpacr_el1, x0 - /* Setup some stack space and enter the test code. + /* + * Setup some stack space before we enter the test code. * Assume everything except the return value is garbage when we * return, we won't need it. */ @@ -223,6 +386,11 @@ __sys_outc: ret .data + + .align 8 +cmdline: + .space 128, 0 + .align 12 /* Translation table @@ -236,6 +404,10 @@ ttb_stage2: .space 4096, 0 .align 12 +system_stack: + .space 4096, 0 +system_stack_end: + stack: .space 65536, 0 stack_end: diff --git a/tests/tcg/aarch64/system/feat-xs.c b/tests/tcg/aarch64/system/feat-xs.c new file mode 100644 index 0000000..f310fc8 --- /dev/null +++ b/tests/tcg/aarch64/system/feat-xs.c @@ -0,0 +1,27 @@ +/* + * FEAT_XS Test + * + * Copyright (c) 2024 Linaro Ltd + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include <minilib.h> +#include <stdint.h> + +int main(void) +{ + uint64_t isar1; + + asm volatile ("mrs %0, id_aa64isar1_el1" : "=r"(isar1)); + if (((isar1 >> 56) & 0xf) < 1) { + ml_printf("FEAT_XS not supported by CPU"); + return 1; + } + /* VMALLE1NXS */ + asm volatile (".inst 0xd508971f"); + /* VMALLE1OSNXS */ + asm volatile (".inst 0xd508911f"); + + return 0; +} diff --git a/tests/tcg/aarch64/system/kernel.ld b/tests/tcg/aarch64/system/kernel.ld index 7b3a76d..aef043e 100644 --- a/tests/tcg/aarch64/system/kernel.ld +++ b/tests/tcg/aarch64/system/kernel.ld @@ -1,23 +1,32 @@ ENTRY(__start) -SECTIONS -{ - /* virt machine, RAM starts at 1gb */ - . = (1 << 30); +MEMORY { + /* On virt machine RAM starts at 1 GiB. */ + + /* Align text and rodata to the 1st 2 MiB chunk. */ + TXT (rx) : ORIGIN = 1 << 30, LENGTH = 2M + /* Align r/w data to the 2nd 2 MiB chunk. */ + DAT (rw) : ORIGIN = (1 << 30) + 2M, LENGTH = 2M + /* Align the MTE-enabled page to the 3rd 2 MiB chunk. */ + TAG (rw) : ORIGIN = (1 << 30) + 4M, LENGTH = 2M +} + +SECTIONS { .text : { *(.text) - } - .rodata : { *(.rodata) - } - /* align r/w section to next 2mb */ - . = ALIGN(1 << 21); + } >TXT .data : { *(.data) - } - .bss : { *(.bss) - } + } >DAT + .tag : { + /* + * Symbol 'mte_page' is used in boot.S to setup the PTE and in the mte.S + * test as the address that the MTE instructions operate on. + */ + mte_page = .; + } >TAG /DISCARD/ : { *(.ARM.attributes) } diff --git a/tests/tcg/aarch64/system/mte.S b/tests/tcg/aarch64/system/mte.S new file mode 100644 index 0000000..b611240 --- /dev/null +++ b/tests/tcg/aarch64/system/mte.S @@ -0,0 +1,109 @@ +/* + * Code to help test the MTE gdbstubs in system mode. + * + * Copyright (c) 2024 Linaro Limited + * + * Author: Gustavo Romero <gustavo.romero@linaro.org> + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#define addr x0 /* Ptr to the start of the MTE-enabled page. */ +#define tagged_addr x1 /* 'addr' ptr with a random-generated tag added. */ +#define tmp0 x2 /* Scratch register. */ +#define tmp1 x3 /* Scratch register. */ +#define tmp2 x4 /* Scratch register. */ +#define tmp3 x5 /* Sctatch register. */ + + .file "mte.S" + + .text + .align 4 + + .globl main + .type main, @function + +main: + /* + * Set MAIR_EL1 (Memory Attribute Index Register). In boot.S, the + * attribute index for .mte_page is set to point to MAILR_EL field Attr1 + * (AttrIndx=Attr1), so set Attr1 as Tagged Normal (MTE) to enable MTE + * on this page. + * + * Attr1 = 0xF0 => Tagged Normal (MTE) + */ + mrs tmp0, mair_el1 + orr tmp0, tmp0, (0xF0 << 8) + msr mair_el1, tmp0 + + /* + * Set TCR_EL1 (Translation Control Registers) to ignore the top byte + * in the translated addresses so it can be used to keep the tags. + * + * TBI0[37] = 0b1 => Top Byte ignored and used for tagged addresses + */ + mrs tmp1, tcr_el1 + orr tmp1, tmp1, (1 << 37) + msr tcr_el1, tmp1 + + /* + * Set SCTLR_EL1 (System Control Register) to enable the use of MTE + * insns., like stg & friends, and to enable synchronous exception in + * case of a tag mismatch, i.e., when the logical tag in 'tagged_addr' + * is different from the allocation tag related to 'addr' address. + * + * ATA[43] = 0b1 => Enable access to allocation tags at EL1 + * TCF[41:40] = 0b01 => Tag Check Faults cause a synchronous exception + * + */ + mrs tmp2, sctlr_el1 + mov tmp3, (1 << 43) | (1 << 40) + orr tmp2, tmp2, tmp3 + msr sctlr_el1, tmp2 + + isb + + /* + * MTE-enabled page resides at the 3rd 2MB chunk in the second 1GB + * block, i.e., at 0x40400000 address. See .mte_page section in boot.S + * and kernel.ld (where the address is effectively computed). + * + * Load .mte_page address into 'addr' register. + */ + adrp addr, mte_page + add addr, addr, :lo12:mte_page + + /* + * Set GCR for random tag generation. 0xA5 is just a random value to set + * GCR != 0 so the tag generated by 'irg' insn. is not zero, which is + * more interesting for the tests than when tag is zero. + */ + mov tmp0, 0xA5 + msr gcr_el1, tmp0 + + /* + * Generate a logical tag, add it to 'addr' address and put it into + * 'tagged_addr'. + */ + irg tagged_addr, addr + + /* + * Store the generated tag to memory region pointed to by 'addr', i.e. + * set the allocation tag for granule at 'addr'. The tag is extracted + * by stg from tagged_addr pointer. + */ + stg tagged_addr, [addr] + + /* + * Store a random value (0xdeadbeef) to tagged_addr address. This must + * not cause any Tag Check Fault since logical tag in tagged_addr and + * allocation tag associated with the memory pointed by tagged_addr are + * set the same, otherwise something is off and the test fails -- an + * exception is generated. + */ + ldr tmp1, =0xdeadbeef + str tmp1, [tagged_addr] + + /* This label is used by GDB Python script test-mte.py. */ +main_end: + ret diff --git a/tests/tcg/aarch64_be/Makefile.target b/tests/tcg/aarch64_be/Makefile.target new file mode 100644 index 0000000..cbe5fa0 --- /dev/null +++ b/tests/tcg/aarch64_be/Makefile.target @@ -0,0 +1,17 @@ +# -*- Mode: makefile -*- +# +# A super basic AArch64 BE makefile. As we don't have any big-endian +# libc available the best we can do is a basic Hello World. + +AARCH64BE_SRC=$(SRC_PATH)/tests/tcg/aarch64_be +VPATH += $(AARCH64BE_SRC) + +AARCH64BE_TEST_SRCS=$(notdir $(wildcard $(AARCH64BE_SRC)/*.c)) +AARCH64BE_TESTS=$(AARCH64BE_TEST_SRCS:.c=) +#MULTIARCH_TESTS = $(MULTIARCH_SRCS:.c=) + +# We need to specify big-endian cflags +CFLAGS +=-mbig-endian -ffreestanding +LDFLAGS +=-nostdlib + +TESTS += $(AARCH64BE_TESTS) diff --git a/tests/tcg/aarch64_be/hello.c b/tests/tcg/aarch64_be/hello.c new file mode 100644 index 0000000..a9b2ab4 --- /dev/null +++ b/tests/tcg/aarch64_be/hello.c @@ -0,0 +1,35 @@ +/* + * Non-libc syscall hello world for Aarch64 BE + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#define __NR_write 64 +#define __NR_exit 93 + +int write(int fd, char *buf, int len) +{ + register int x0 __asm__("x0") = fd; + register char *x1 __asm__("x1") = buf; + register int x2 __asm__("x2") = len; + register int x8 __asm__("x8") = __NR_write; + + asm volatile("svc #0" : : "r"(x0), "r"(x1), "r"(x2), "r"(x8)); + + return len; +} + +void exit(int ret) +{ + register int x0 __asm__("x0") = ret; + register int x8 __asm__("x8") = __NR_exit; + + asm volatile("svc #0" : : "r"(x0), "r"(x8)); + __builtin_unreachable(); +} + +void _start(void) +{ + write(1, "Hello World\n", 12); + exit(0); +} diff --git a/tests/tcg/alpha/Makefile.softmmu-target b/tests/tcg/alpha/Makefile.softmmu-target index a0eca4d..a944102 100644 --- a/tests/tcg/alpha/Makefile.softmmu-target +++ b/tests/tcg/alpha/Makefile.softmmu-target @@ -28,7 +28,7 @@ LDFLAGS+=-static -nostdlib $(CRT_OBJS) $(MINILIB_OBJS) -lgcc %: %.c $(LINK_SCRIPT) $(CRT_OBJS) $(MINILIB_OBJS) $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $< -o $@ $(LDFLAGS) -memory: CFLAGS+=-DCHECK_UNALIGNED=0 +memory: CFLAGS+=-DCHECK_UNALIGNED=0 -mbwx # Running QEMU_OPTS+=-serial chardev:output -kernel diff --git a/tests/tcg/alpha/Makefile.target b/tests/tcg/alpha/Makefile.target index fdd7ddf..36d8ed1 100644 --- a/tests/tcg/alpha/Makefile.target +++ b/tests/tcg/alpha/Makefile.target @@ -12,4 +12,7 @@ test-cmov: EXTRA_CFLAGS=-DTEST_CMOV test-cmov: test-cond.c $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $< -o $@ $(LDFLAGS) +# Force generation of byte read/write +test-plugin-mem-access: CFLAGS+=-mbwx + run-test-cmov: test-cmov diff --git a/tests/tcg/arm/Makefile.target b/tests/tcg/arm/Makefile.target index 06ddf3e..6189d7a 100644 --- a/tests/tcg/arm/Makefile.target +++ b/tests/tcg/arm/Makefile.target @@ -20,13 +20,6 @@ ARM_TESTS = hello-arm hello-arm: CFLAGS+=-marm -ffreestanding -fno-stack-protector hello-arm: LDFLAGS+=-nostdlib -# IWMXT floating point extensions -ARM_TESTS += test-arm-iwmmxt -# Clang assembler does not support IWMXT, so use the external assembler. -test-arm-iwmmxt: CFLAGS += -marm -march=iwmmxt -mabi=aapcs -mfpu=fpv4-sp-d16 $(CROSS_CC_HAS_FNIA) -test-arm-iwmmxt: test-arm-iwmmxt.S - $(CC) $(CFLAGS) -Wa,--noexecstack $< -o $@ $(LDFLAGS) - # Float-convert Tests ARM_TESTS += fcvt fcvt: LDFLAGS += -lm @@ -68,7 +61,8 @@ endif ARM_TESTS += commpage # Vector SHA1 -sha1-vector: CFLAGS=-O3 +# Work around compiler false-positive warning, as we do for the 'sha1' test +sha1-vector: CFLAGS=-O3 -Wno-stringop-overread sha1-vector: sha1.c $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $< -o $@ $(LDFLAGS) run-sha1-vector: sha1-vector run-sha1 diff --git a/tests/tcg/arm/README b/tests/tcg/arm/README index e630711..aceccc1 100644 --- a/tests/tcg/arm/README +++ b/tests/tcg/arm/README @@ -4,8 +4,3 @@ hello-arm --------- A very simple inline assembly, write syscall based hello world - -test-arm-iwmmxt ---------------- - -A simple test case for older iwmmxt extended ARMs diff --git a/tests/tcg/arm/test-arm-iwmmxt.S b/tests/tcg/arm/test-arm-iwmmxt.S deleted file mode 100644 index d647f94..0000000 --- a/tests/tcg/arm/test-arm-iwmmxt.S +++ /dev/null @@ -1,49 +0,0 @@ -@ Checks whether iwMMXt is functional. -.code 32 -.globl main - -main: -ldr r0, =data0 -ldr r1, =data1 -ldr r2, =data2 -#ifndef FPA -wldrd wr0, [r0, #0] -wldrd wr1, [r0, #8] -wldrd wr2, [r1, #0] -wldrd wr3, [r1, #8] -wsubb wr2, wr2, wr0 -wsubb wr3, wr3, wr1 -wldrd wr0, [r2, #0] -wldrd wr1, [r2, #8] -waddb wr0, wr0, wr2 -waddb wr1, wr1, wr3 -wstrd wr0, [r2, #0] -wstrd wr1, [r2, #8] -#else -ldfe f0, [r0, #0] -ldfe f1, [r0, #8] -ldfe f2, [r1, #0] -ldfe f3, [r1, #8] -adfdp f2, f2, f0 -adfdp f3, f3, f1 -ldfe f0, [r2, #0] -ldfe f1, [r2, #8] -adfd f0, f0, f2 -adfd f1, f1, f3 -stfe f0, [r2, #0] -stfe f1, [r2, #8] -#endif -mov r0, #1 -mov r1, r2 -mov r2, #0x11 -swi #0x900004 -mov r0, #0 -swi #0x900001 - -.data -data0: -.string "aaaabbbbccccdddd" -data1: -.string "bbbbccccddddeeee" -data2: -.string "hvLLWs\x1fsdrs9\x1fNJ-\n" diff --git a/tests/tcg/cris/.gdbinit b/tests/tcg/cris/.gdbinit deleted file mode 100644 index 5e8c1d3..0000000 --- a/tests/tcg/cris/.gdbinit +++ /dev/null @@ -1,11 +0,0 @@ -b main -b _fail -b exit -display /i $pc -display /x $srp -display /x $r0 -display /x $r1 -display /x $r2 -display /x $r3 -display /x $r4 -display /t $ccs diff --git a/tests/tcg/cris/Makefile.target b/tests/tcg/cris/Makefile.target deleted file mode 100644 index 713e2a5..0000000 --- a/tests/tcg/cris/Makefile.target +++ /dev/null @@ -1,62 +0,0 @@ -# -*- Mode: makefile -*- -# -# Cris tests -# -# Currently we can only build the "bare" tests with the docker -# supplied cross-compiler. -# - -CRIS_SRC = $(SRC_PATH)/tests/tcg/cris/bare -CRIS_ALL = $(wildcard $(CRIS_SRC)/*.s) -CRIS_TESTS = $(patsubst $(CRIS_SRC)/%.s, %, $(CRIS_ALL)) -# Filter out common blobs and broken tests -CRIS_BROKEN_TESTS = crt check_jsr -# upstream GCC doesn't support v32 -CRIS_BROKEN_TESTS += check_mcp check_mulv32 check_addiv32 check_movpmv32 -CRIS_BROKEN_TESTS += check_movprv32 check_clearfv32 check_movemrv32 check_bas -CRIS_BROKEN_TESTS += check_lapc check_movei -# no sure why -CRIS_BROKEN_TESTS += check_scc check_xarith - -CRIS_USABLE_TESTS = $(filter-out $(CRIS_BROKEN_TESTS), $(CRIS_TESTS)) -CRIS_RUNS = $(patsubst %, run-%, $(CRIS_USABLE_TESTS)) - -# override the list of tests, as we can't build the multiarch tests -TESTS = $(CRIS_USABLE_TESTS) -EXTRA_RUNS = -VPATH = $(CRIS_SRC) - -AS = $(CC) -x assembler-with-cpp -LD = $(CC) - -# we rely on GCC inline:ing the stuff we tell it to in many places here. -CFLAGS = -Winline -Wall -g -O2 -static -fno-stack-protector -NOSTDFLAGS = -nostartfiles -nostdlib -ASFLAGS += -mcpu=v10 -g -Wa,-I,$(SRC_PATH)/tests/tcg/cris/bare -CRT_FILES = crt.o sys.o - -# stop make deleting crt files if build fails -.PRECIOUS: $(CRT_FILES) - -%.o: %.c - $(CC) -c $< -o $@ - -%.o: %.s - $(AS) $(ASFLAGS) -c $< -o $@ - -%: %.s $(CRT_FILES) - $(CC) $(ASFLAGS) $< -o $@ $(LDFLAGS) $(NOSTDFLAGS) $(CRT_FILES) - -# The default CPU breaks (possibly as it's max?) so force crisv17 -QEMU_OPTS=-cpu crisv17 - -# Additional runners to run under GNU SIM -CRIS_RUNS_ON_SIM=$(patsubst %, %-on-sim, $(CRIS_RUNS)) -SIMG:=cris-axis-linux-gnu-run - -# e.g.: make -f ../../tests/tcg/Makefile run-check_orm-on-sim -run-%-on-sim: - $(call run-test, $<, $(SIMG) $<) - -# We don't currently support the multiarch tests -undefine MULTIARCH_TESTS diff --git a/tests/tcg/cris/README b/tests/tcg/cris/README deleted file mode 100644 index 2e65a76..0000000 --- a/tests/tcg/cris/README +++ /dev/null @@ -1 +0,0 @@ -Test-suite for the cris port. Heavily based on the test-suite for the CRIS port of sim by Hans-Peter Nilsson. diff --git a/tests/tcg/cris/bare/check_addcv17.s b/tests/tcg/cris/bare/check_addcv17.s deleted file mode 100644 index 52ef7a9..0000000 --- a/tests/tcg/cris/bare/check_addcv17.s +++ /dev/null @@ -1,65 +0,0 @@ -# mach: crisv17 - - .include "testutils.inc" - - .macro addc Rs Rd inc=0 -# Create the instruction manually since there is no assembler support yet - .word (\Rd << 12) | \Rs | (\inc << 10) | 0x09a0 - .endm - - start - - .data -mem1: - .dword 0x0 -mem2: - .dword 0x12345678 - - .text - move.d mem1,r4 - clearf nzvc - addc 4 3 - test_cc 0 1 0 0 - checkr3 0 - - move.d mem1,r4 - clearf nzvc - ax - addc 4 3 - test_cc 0 0 0 0 - checkr3 0 - - move.d mem1,r4 - clearf nzvc - setf c - addc 4 3 - test_cc 0 0 0 0 - checkr3 1 - - move.d mem2,r4 - moveq 2, r3 - clearf nzvc - setf c - addc 4 3 - test_cc 0 0 0 0 - checkr3 1234567b - - move.d mem2,r5 - clearf nzvc - cmp.d r4,r5 - test_cc 0 1 0 0 - - move.d mem2,r4 - moveq 2, r3 - clearf nzvc - addc 4 3 inc=1 - test_cc 0 0 0 0 - checkr3 1234567a - - move.d mem2,r5 - clearf nzvc - addq 4,r5 - cmp.d r4,r5 - test_cc 0 1 0 0 - - quit diff --git a/tests/tcg/cris/bare/check_addi.s b/tests/tcg/cris/bare/check_addi.s deleted file mode 100644 index a00dec0..0000000 --- a/tests/tcg/cris/bare/check_addi.s +++ /dev/null @@ -1,57 +0,0 @@ -# mach: crisv0 crisv3 crisv8 crisv10 crisv32 -# output: 0\n1\n2\n4\nbe02460f\n69d035a6\nc16c14d4\n - - .include "testutils.inc" - start - moveq 0,r3 - moveq 0,r4 - clearf zcvn - addi r4.b,r3 - test_cc 0 0 0 0 - checkr3 0 - - moveq 0,r3 - moveq 1,r4 - setf zcvn - addi r4.b,r3 - test_cc 1 1 1 1 - checkr3 1 - - moveq 0,r3 - moveq 1,r4 - setf cv - clearf zn - addi r4.w,r3 - test_cc 0 0 1 1 - checkr3 2 - - moveq 0,r3 - moveq 1,r4 - clearf cv - setf zn - addi r4.d,r3 - test_cc 1 1 0 0 - checkr3 4 - - move.d 0x12345678,r3 - move.d 0xabcdef97,r4 - clearf cn - setf zv - addi r4.b,r3 - test_cc 0 1 1 0 - checkr3 be02460f - - move.d 0x12345678,r3 - move.d 0xabcdef97,r4 - setf cn - clearf zv - addi r4.w,r3 - test_cc 1 0 0 1 - checkr3 69d035a6 - - move.d 0x12345678,r3 - move.d 0xabcdef97,r4 - addi r4.d,r3 - checkr3 c16c14d4 - - quit diff --git a/tests/tcg/cris/bare/check_addiv32.s b/tests/tcg/cris/bare/check_addiv32.s deleted file mode 100644 index 20ba25d..0000000 --- a/tests/tcg/cris/bare/check_addiv32.s +++ /dev/null @@ -1,62 +0,0 @@ -# mach: crisv32 -# output: 4455aa77\n4455aa77\nee19ccff\nff22\n4455aa77\nff224455\n55aa77ff\n - - .include "testutils.inc" - .data -x: - .dword 0x55aa77ff - .dword 0xccff2244 - .dword 0x88ccee19 - - start - setf cv - moveq -1,r0 - move.d x-32768,r5 - move.d 32769,r6 - addi r6.b,r5,acr - test_cc 0 0 1 1 - move.d [acr],r3 - checkr3 4455aa77 - - addu.w 32771,r5 - setf znvc - moveq -1,r8 - addi r8.w,r5,acr - test_cc 1 1 1 1 - move.d [acr],r3 - checkr3 4455aa77 - - moveq 5,r10 - clearf znvc - addi r10.b,acr,acr - test_cc 0 0 0 0 - move.d [acr],r3 - checkr3 ee19ccff - - subq 1,r5 - move.d r5,r8 - subq 1,r8 - moveq 1,r9 - addi r9.d,r8,acr - test_cc 0 0 0 0 - movu.w [acr],r3 - checkr3 ff22 - - moveq -2,r11 - addi r11.w,acr,acr - move.d [acr],r3 - checkr3 4455aa77 - - moveq 5,r9 - addi r9.d,acr,acr - subq 18,acr - move.d [acr],r3 - checkr3 ff224455 - - move.d -76789888/4,r12 - addi r12.d,r5,acr - add.d 76789886,acr - move.d [acr],r3 - checkr3 55aa77ff - - quit diff --git a/tests/tcg/cris/bare/check_addm.s b/tests/tcg/cris/bare/check_addm.s deleted file mode 100644 index efece9f..0000000 --- a/tests/tcg/cris/bare/check_addm.s +++ /dev/null @@ -1,96 +0,0 @@ -# mach: crisv0 crisv3 crisv8 crisv10 crisv32 -# output: 1\n1\n1fffe\nfffffffe\ncc463bdb\nffff0001\n1\nfffe\nfedafffe\n78133bdb\nffffff01\n1\nfe\nfeda49fe\n781344db\n781344d0\n - - .include "testutils.inc" - .data -x: - .dword 2,-1,0xffff,-1,0x5432f789 - .word 2,-1,0xffff,0xf789 - .byte 2,0xff,0x89 - .byte 0x7e - - start - moveq -1,r3 - move.d x,r5 - add.d [r5+],r3 - test_cc 0 0 0 1 - checkr3 1 - - moveq 2,r3 - add.d [r5],r3 - test_cc 0 0 0 1 - addq 4,r5 - checkr3 1 - - move.d 0xffff,r3 - add.d [r5+],r3 - test_cc 0 0 0 0 - checkr3 1fffe - - moveq -1,r3 - add.d [r5+],r3 - test_cc 1 0 0 1 - checkr3 fffffffe - - move.d 0x78134452,r3 - add.d [r5+],r3 - test_cc 1 0 1 0 - checkr3 cc463bdb - - moveq -1,r3 - add.w [r5+],r3 - test_cc 0 0 0 1 - checkr3 ffff0001 - - moveq 2,r3 - add.w [r5+],r3 - test_cc 0 0 0 1 - checkr3 1 - - move.d 0xffff,r3 - add.w [r5],r3 - test_cc 1 0 0 1 - checkr3 fffe - - move.d 0xfedaffff,r3 - add.w [r5+],r3 - test_cc 1 0 0 1 - checkr3 fedafffe - - move.d 0x78134452,r3 - add.w [r5+],r3 - test_cc 0 0 0 1 - checkr3 78133bdb - - moveq -1,r3 - add.b [r5],r3 - test_cc 0 0 0 1 - addq 1,r5 - checkr3 ffffff01 - - moveq 2,r3 - add.b [r5],r3 - test_cc 0 0 0 1 - checkr3 1 - - move.d 0xff,r3 - add.b [r5],r3 - test_cc 1 0 0 1 - checkr3 fe - - move.d 0xfeda49ff,r3 - add.b [r5+],r3 - test_cc 1 0 0 1 - checkr3 feda49fe - - move.d 0x78134452,r3 - add.b [r5+],r3 - test_cc 1 0 0 0 - checkr3 781344db - - move.d 0x78134452,r3 - add.b [r5],r3 - test_cc 1 0 1 0 - checkr3 781344d0 - - quit diff --git a/tests/tcg/cris/bare/check_addq.s b/tests/tcg/cris/bare/check_addq.s deleted file mode 100644 index e6f874f..0000000 --- a/tests/tcg/cris/bare/check_addq.s +++ /dev/null @@ -1,47 +0,0 @@ -# mach: crisv3 crisv8 crisv10 crisv32 -# output: ffffffff\n0\n1\n100\n10000\n47\n67\na6\n80000001\n - - .include "testutils.inc" - start - moveq -2,r3 - addq 1,r3 - test_cc 1 0 0 0 - checkr3 ffffffff - - addq 1,r3 - test_cc 0 1 0 1 - checkr3 0 - - addq 1,r3 - test_cc 0 0 0 0 - checkr3 1 - - move.d 0xff,r3 - addq 1,r3 - test_cc 0 0 0 0 - checkr3 100 - - move.d 0xffff,r3 - addq 1,r3 - test_cc 0 0 0 0 - checkr3 10000 - - move.d 0x42,r3 - addq 5,r3 - test_cc 0 0 0 0 - checkr3 47 - - addq 32,r3 - test_cc 0 0 0 0 - checkr3 67 - - addq 63,r3 - test_cc 0 0 0 0 - checkr3 a6 - - move.d 0x7ffffffe,r3 - addq 3,r3 - test_cc 1 0 1 0 - checkr3 80000001 - - quit diff --git a/tests/tcg/cris/bare/check_addr.s b/tests/tcg/cris/bare/check_addr.s deleted file mode 100644 index 7f55cdc..0000000 --- a/tests/tcg/cris/bare/check_addr.s +++ /dev/null @@ -1,96 +0,0 @@ -# mach: crisv0 crisv3 crisv8 crisv10 crisv32 -# output: 1\n1\n1fffe\nfffffffe\ncc463bdb\nffff0001\n1\nfffe\nfedafffe\n78133bdb\nffffff01\n1\nfe\nfeda49fe\n781344db\n - - .include "testutils.inc" - start - moveq -1,r3 - moveq 2,r4 - add.d r4,r3 - test_cc 0 0 0 1 - checkr3 1 - - moveq 2,r3 - moveq -1,r4 - add.d r4,r3 - test_cc 0 0 0 1 - checkr3 1 - - move.d 0xffff,r4 - move.d r4,r3 - add.d r4,r3 - test_cc 0 0 0 0 - checkr3 1fffe - - moveq -1,r4 - move.d r4,r3 - add.d r4,r3 - test_cc 1 0 0 1 - checkr3 fffffffe - - move.d 0x5432f789,r4 - move.d 0x78134452,r3 - add.d r4,r3 - test_cc 1 0 1 0 - checkr3 cc463bdb - - moveq -1,r3 - moveq 2,r4 - add.w r4,r3 - test_cc 0 0 0 1 - checkr3 ffff0001 - - moveq 2,r3 - moveq -1,r4 - add.w r4,r3 - test_cc 0 0 0 1 - checkr3 1 - - move.d 0xffff,r4 - move.d r4,r3 - add.w r4,r3 - test_cc 1 0 0 1 - checkr3 fffe - - move.d 0xfedaffff,r4 - move.d r4,r3 - add.w r4,r3 - test_cc 1 0 0 1 - checkr3 fedafffe - - move.d 0x5432f789,r4 - move.d 0x78134452,r3 - add.w r4,r3 - test_cc 0 0 0 1 - checkr3 78133bdb - - moveq -1,r3 - moveq 2,r4 - add.b r4,r3 - test_cc 0 0 0 1 - checkr3 ffffff01 - - moveq 2,r3 - moveq -1,r4 - add.b r4,r3 - test_cc 0 0 0 1 - checkr3 1 - - move.d 0xff,r4 - move.d r4,r3 - add.b r4,r3 - test_cc 1 0 0 1 - checkr3 fe - - move.d 0xfeda49ff,r4 - move.d r4,r3 - add.b r4,r3 - test_cc 1 0 0 1 - checkr3 feda49fe - - move.d 0x5432f789,r4 - move.d 0x78134452,r3 - add.b r4,r3 - test_cc 1 0 0 0 - checkr3 781344db - - quit diff --git a/tests/tcg/cris/bare/check_addxc.s b/tests/tcg/cris/bare/check_addxc.s deleted file mode 100644 index 09c8355..0000000 --- a/tests/tcg/cris/bare/check_addxc.s +++ /dev/null @@ -1,91 +0,0 @@ -# mach: crisv0 crisv3 crisv8 crisv10 crisv32 -# output: 1\n1\n101\n10001\n100fe\n1fffe\nfffe\nfffe\nfffffffe\nfe\nfffffffe\n781344db\n781343db\n78143bdb\n78133bdb\n800000ed\n0\n - - .include "testutils.inc" - start - moveq 2,r3 - adds.b 0xff,r3 - test_cc 0 0 0 1 - checkr3 1 - - moveq 2,r3 - adds.w 0xffff,r3 - test_cc 0 0 0 1 - checkr3 1 - - moveq 2,r3 - addu.b 0xff,r3 - checkr3 101 - - moveq 2,r3 - move.d 0xffffffff,r4 - addu.w -1,r3 - test_cc 0 0 0 0 - checkr3 10001 - - move.d 0xffff,r3 - addu.b -1,r3 - test_cc 0 0 0 0 - checkr3 100fe - - move.d 0xffff,r3 - addu.w -1,r3 - test_cc 0 0 0 0 - checkr3 1fffe - - move.d 0xffff,r3 - adds.b 0xff,r3 - test_cc 0 0 0 1 - checkr3 fffe - - move.d 0xffff,r3 - adds.w 0xffff,r3 - test_cc 0 0 0 1 - checkr3 fffe - - moveq -1,r3 - adds.b 0xff,r3 - test_cc 1 0 0 1 - checkr3 fffffffe - - moveq -1,r3 - adds.w 0xff,r3 - test_cc 0 0 0 1 - checkr3 fe - - moveq -1,r3 - adds.w 0xffff,r3 - test_cc 1 0 0 1 - checkr3 fffffffe - - move.d 0x78134452,r3 - addu.b 0x89,r3 - test_cc 0 0 0 0 - checkr3 781344db - - move.d 0x78134452,r3 - adds.b 0x89,r3 - test_cc 0 0 0 1 - checkr3 781343db - - move.d 0x78134452,r3 - addu.w 0xf789,r3 - test_cc 0 0 0 0 - checkr3 78143bdb - - move.d 0x78134452,r3 - adds.w 0xf789,r3 - test_cc 0 0 0 1 - checkr3 78133bdb - - move.d 0x7fffffee,r3 - addu.b 0xff,r3 - test_cc 1 0 1 0 - checkr3 800000ed - - move.d 0x1,r3 - adds.w 0xffff,r3 - test_cc 0 1 0 1 - checkr3 0 - - quit diff --git a/tests/tcg/cris/bare/check_addxm.s b/tests/tcg/cris/bare/check_addxm.s deleted file mode 100644 index 7563494..0000000 --- a/tests/tcg/cris/bare/check_addxm.s +++ /dev/null @@ -1,106 +0,0 @@ -# mach: crisv0 crisv3 crisv8 crisv10 crisv32 -# output: 1\n1\n101\n10001\n100fe\n1fffe\nfffe\nfffe\nfffffffe\nfe\nfffffffe\n781344db\n781343db\n78143bdb\n78133bdb\n800000ed\n0\n - - .include "testutils.inc" - .data -x: - .byte 0xff - .word 0xffff - .word 0xff - .word 0xffff - .byte 0x89 - .word 0xf789 - .byte 0xff - .word 0xffff - - start - moveq 2,r3 - move.d x,r5 - adds.b [r5+],r3 - test_cc 0 0 0 1 - checkr3 1 - - moveq 2,r3 - adds.w [r5+],r3 - test_cc 0 0 0 1 - checkr3 1 - - moveq 2,r3 - subq 3,r5 - addu.b [r5+],r3 - test_cc 0 0 0 0 - checkr3 101 - - moveq 2,r3 - addu.w [r5+],r3 - subq 3,r5 - test_cc 0 0 0 0 - checkr3 10001 - - move.d 0xffff,r3 - addu.b [r5],r3 - test_cc 0 0 0 0 - checkr3 100fe - - move.d 0xffff,r3 - addu.w [r5],r3 - test_cc 0 0 0 0 - checkr3 1fffe - - move.d 0xffff,r3 - adds.b [r5],r3 - test_cc 0 0 0 1 - checkr3 fffe - - move.d 0xffff,r3 - adds.w [r5],r3 - test_cc 0 0 0 1 - checkr3 fffe - - moveq -1,r3 - adds.b [r5],r3 - test_cc 1 0 0 1 - addq 3,r5 - checkr3 fffffffe - - moveq -1,r3 - adds.w [r5+],r3 - test_cc 0 0 0 1 - checkr3 fe - - moveq -1,r3 - adds.w [r5+],r3 - test_cc 1 0 0 1 - checkr3 fffffffe - - move.d 0x78134452,r3 - addu.b [r5],r3 - test_cc 0 0 0 0 - checkr3 781344db - - move.d 0x78134452,r3 - adds.b [r5+],r3 - test_cc 0 0 0 1 - checkr3 781343db - - move.d 0x78134452,r3 - addu.w [r5],r3 - test_cc 0 0 0 0 - checkr3 78143bdb - - move.d 0x78134452,r3 - adds.w [r5+],r3 - test_cc 0 0 0 1 - checkr3 78133bdb - - move.d 0x7fffffee,r3 - addu.b [r5+],r3 - test_cc 1 0 1 0 - checkr3 800000ed - - move.d 0x1,r3 - adds.w [r5+],r3 - test_cc 0 1 0 1 - checkr3 0 - - quit diff --git a/tests/tcg/cris/bare/check_addxr.s b/tests/tcg/cris/bare/check_addxr.s deleted file mode 100644 index 7f55cdc..0000000 --- a/tests/tcg/cris/bare/check_addxr.s +++ /dev/null @@ -1,96 +0,0 @@ -# mach: crisv0 crisv3 crisv8 crisv10 crisv32 -# output: 1\n1\n1fffe\nfffffffe\ncc463bdb\nffff0001\n1\nfffe\nfedafffe\n78133bdb\nffffff01\n1\nfe\nfeda49fe\n781344db\n - - .include "testutils.inc" - start - moveq -1,r3 - moveq 2,r4 - add.d r4,r3 - test_cc 0 0 0 1 - checkr3 1 - - moveq 2,r3 - moveq -1,r4 - add.d r4,r3 - test_cc 0 0 0 1 - checkr3 1 - - move.d 0xffff,r4 - move.d r4,r3 - add.d r4,r3 - test_cc 0 0 0 0 - checkr3 1fffe - - moveq -1,r4 - move.d r4,r3 - add.d r4,r3 - test_cc 1 0 0 1 - checkr3 fffffffe - - move.d 0x5432f789,r4 - move.d 0x78134452,r3 - add.d r4,r3 - test_cc 1 0 1 0 - checkr3 cc463bdb - - moveq -1,r3 - moveq 2,r4 - add.w r4,r3 - test_cc 0 0 0 1 - checkr3 ffff0001 - - moveq 2,r3 - moveq -1,r4 - add.w r4,r3 - test_cc 0 0 0 1 - checkr3 1 - - move.d 0xffff,r4 - move.d r4,r3 - add.w r4,r3 - test_cc 1 0 0 1 - checkr3 fffe - - move.d 0xfedaffff,r4 - move.d r4,r3 - add.w r4,r3 - test_cc 1 0 0 1 - checkr3 fedafffe - - move.d 0x5432f789,r4 - move.d 0x78134452,r3 - add.w r4,r3 - test_cc 0 0 0 1 - checkr3 78133bdb - - moveq -1,r3 - moveq 2,r4 - add.b r4,r3 - test_cc 0 0 0 1 - checkr3 ffffff01 - - moveq 2,r3 - moveq -1,r4 - add.b r4,r3 - test_cc 0 0 0 1 - checkr3 1 - - move.d 0xff,r4 - move.d r4,r3 - add.b r4,r3 - test_cc 1 0 0 1 - checkr3 fe - - move.d 0xfeda49ff,r4 - move.d r4,r3 - add.b r4,r3 - test_cc 1 0 0 1 - checkr3 feda49fe - - move.d 0x5432f789,r4 - move.d 0x78134452,r3 - add.b r4,r3 - test_cc 1 0 0 0 - checkr3 781344db - - quit diff --git a/tests/tcg/cris/bare/check_andc.s b/tests/tcg/cris/bare/check_andc.s deleted file mode 100644 index a947b77..0000000 --- a/tests/tcg/cris/bare/check_andc.s +++ /dev/null @@ -1,80 +0,0 @@ -# mach: crisv0 crisv3 crisv8 crisv10 crisv32 -# output: 2\n2\nffff\nffffffff\n50124400\nffff0002\n2\nfffff\nfedaff0f\n78134400\nffffff02\n2\nf02\n78134401\n78134400\n - - .include "testutils.inc" - start - moveq -1,r3 - and.d 2,r3 - test_move_cc 0 0 0 0 - checkr3 2 - - moveq 2,r3 - and.d -1,r3 - test_move_cc 0 0 0 0 - checkr3 2 - - move.d 0xffff,r3 - and.d 0xffff,r3 - test_move_cc 0 0 0 0 - checkr3 ffff - - moveq -1,r3 - and.d -1,r3 - test_move_cc 1 0 0 0 - checkr3 ffffffff - - move.d 0x78134452,r3 - and.d 0x5432f789,r3 - test_move_cc 0 0 0 0 - checkr3 50124400 - - moveq -1,r3 - and.w 2,r3 - test_move_cc 0 0 0 0 - checkr3 ffff0002 - - moveq 2,r3 - and.w -1,r3 - test_move_cc 0 0 0 0 - checkr3 2 - - move.d 0xfffff,r3 - and.w 0xffff,r3 - test_move_cc 1 0 0 0 - checkr3 fffff - - move.d 0xfedaffaf,r3 - and.w 0xff5f,r3 - test_move_cc 1 0 0 0 - checkr3 fedaff0f - - move.d 0x78134452,r3 - and.w 0xf789,r3 - test_move_cc 0 0 0 0 - checkr3 78134400 - - moveq -1,r3 - and.b 2,r3 - test_move_cc 0 0 0 0 - checkr3 ffffff02 - - moveq 2,r3 - and.b -1,r3 - test_move_cc 0 0 0 0 - checkr3 2 - - move.d 0xfa7,r3 - and.b 0x5a,r3 - test_move_cc 0 0 0 0 - checkr3 f02 - - move.d 0x78134453,r3 - and.b 0x89,r3 - test_move_cc 0 0 0 0 - checkr3 78134401 - - and.b 0,r3 - test_move_cc 0 1 0 0 - checkr3 78134400 - - quit diff --git a/tests/tcg/cris/bare/check_andm.s b/tests/tcg/cris/bare/check_andm.s deleted file mode 100644 index 9385886..0000000 --- a/tests/tcg/cris/bare/check_andm.s +++ /dev/null @@ -1,90 +0,0 @@ -# mach: crisv0 crisv3 crisv8 crisv10 crisv32 -# output: 2\n2\nffff\nffffffff\n50124400\nffff0002\n2\nfffff\nfedaff0f\n78134400\nffffff02\n2\nf02\n78134401\n78134400\n - - .include "testutils.inc" - .data -x: - .dword 2,-1,0xffff,-1,0x5432f789 - .word 2,-1,0xffff,0xff5f,0xf789 - .byte 2,-1,0x5a,0x89,0 - - start - moveq -1,r3 - move.d x,r5 - and.d [r5+],r3 - test_move_cc 0 0 0 0 - checkr3 2 - - moveq 2,r3 - and.d [r5],r3 - test_move_cc 0 0 0 0 - addq 4,r5 - checkr3 2 - - move.d 0xffff,r3 - and.d [r5+],r3 - test_move_cc 0 0 0 0 - checkr3 ffff - - moveq -1,r3 - and.d [r5+],r3 - test_move_cc 1 0 0 0 - checkr3 ffffffff - - move.d 0x78134452,r3 - and.d [r5+],r3 - test_move_cc 0 0 0 0 - checkr3 50124400 - - moveq -1,r3 - and.w [r5+],r3 - test_move_cc 0 0 0 0 - checkr3 ffff0002 - - moveq 2,r3 - and.w [r5+],r3 - test_move_cc 0 0 0 0 - checkr3 2 - - move.d 0xfffff,r3 - and.w [r5],r3 - test_move_cc 1 0 0 0 - addq 2,r5 - checkr3 fffff - - move.d 0xfedaffaf,r3 - and.w [r5+],r3 - test_move_cc 1 0 0 0 - checkr3 fedaff0f - - move.d 0x78134452,r3 - and.w [r5+],r3 - test_move_cc 0 0 0 0 - checkr3 78134400 - - moveq -1,r3 - and.b [r5],r3 - test_move_cc 0 0 0 0 - addq 1,r5 - checkr3 ffffff02 - - moveq 2,r3 - and.b [r5+],r3 - test_move_cc 0 0 0 0 - checkr3 2 - - move.d 0xfa7,r3 - and.b [r5+],r3 - test_move_cc 0 0 0 0 - checkr3 f02 - - move.d 0x78134453,r3 - and.b [r5+],r3 - test_move_cc 0 0 0 0 - checkr3 78134401 - - and.b [r5],r3 - test_move_cc 0 1 0 0 - checkr3 78134400 - - quit diff --git a/tests/tcg/cris/bare/check_andq.s b/tests/tcg/cris/bare/check_andq.s deleted file mode 100644 index 55aa7b0..0000000 --- a/tests/tcg/cris/bare/check_andq.s +++ /dev/null @@ -1,46 +0,0 @@ -# mach: crisv0 crisv3 crisv8 crisv10 crisv32 -# output: 2\n2\nffff\nffffffff\n1f\nffffffe0\n78134452\n0\n - - .include "testutils.inc" - start - moveq -1,r3 - andq 2,r3 - test_move_cc 0 0 0 0 - checkr3 2 - - moveq 2,r3 - andq -1,r3 - test_move_cc 0 0 0 0 - checkr3 2 - - move.d 0xffff,r3 - andq -1,r3 - test_move_cc 0 0 0 0 - checkr3 ffff - - moveq -1,r3 - andq -1,r3 - test_move_cc 1 0 0 0 - checkr3 ffffffff - - moveq -1,r3 - andq 31,r3 - test_move_cc 0 0 0 0 - checkr3 1f - - moveq -1,r3 - andq -32,r3 - test_move_cc 1 0 0 0 - checkr3 ffffffe0 - - move.d 0x78134457,r3 - andq -14,r3 - test_move_cc 0 0 0 0 - checkr3 78134452 - - moveq 0,r3 - andq -14,r3 - test_move_cc 0 1 0 0 - checkr3 0 - - quit diff --git a/tests/tcg/cris/bare/check_andr.s b/tests/tcg/cris/bare/check_andr.s deleted file mode 100644 index 61aa1dc..0000000 --- a/tests/tcg/cris/bare/check_andr.s +++ /dev/null @@ -1,95 +0,0 @@ -# mach: crisv0 crisv3 crisv8 crisv10 crisv32 -# output: 2\n2\nffff\nffffffff\n50124400\nffff0002\n2\nfffff\nfedaff0f\n78134400\nffffff02\n2\nf02\n78134401\n78134400\n - - .include "testutils.inc" - start - moveq -1,r3 - moveq 2,r4 - and.d r4,r3 - test_move_cc 0 0 0 0 - checkr3 2 - - moveq 2,r3 - moveq -1,r4 - and.d r4,r3 - test_move_cc 0 0 0 0 - checkr3 2 - - move.d 0xffff,r4 - move.d r4,r3 - and.d r4,r3 - test_move_cc 0 0 0 0 - checkr3 ffff - - moveq -1,r4 - move.d r4,r3 - and.d r4,r3 - test_move_cc 1 0 0 0 - checkr3 ffffffff - - move.d 0x5432f789,r4 - move.d 0x78134452,r3 - and.d r4,r3 - test_move_cc 0 0 0 0 - checkr3 50124400 - - moveq -1,r3 - moveq 2,r4 - and.w r4,r3 - test_move_cc 0 0 0 0 - checkr3 ffff0002 - - moveq 2,r3 - moveq -1,r4 - and.w r4,r3 - test_move_cc 0 0 0 0 - checkr3 2 - - move.d 0xfffff,r3 - move.d 0xffff,r4 - and.w r4,r3 - test_move_cc 1 0 0 0 - checkr3 fffff - - move.d 0xfedaffaf,r3 - move.d 0xff5f,r4 - and.w r4,r3 - test_move_cc 1 0 0 0 - checkr3 fedaff0f - - move.d 0x5432f789,r4 - move.d 0x78134452,r3 - and.w r4,r3 - test_move_cc 0 0 0 0 - checkr3 78134400 - - moveq -1,r3 - moveq 2,r4 - and.b r4,r3 - test_move_cc 0 0 0 0 - checkr3 ffffff02 - - moveq 2,r3 - moveq -1,r4 - and.b r4,r3 - test_move_cc 0 0 0 0 - checkr3 2 - - move.d 0x5a,r4 - move.d 0xfa7,r3 - and.b r4,r3 - test_move_cc 0 0 0 0 - checkr3 f02 - - move.d 0x5432f789,r4 - move.d 0x78134453,r3 - and.b r4,r3 - test_move_cc 0 0 0 0 - checkr3 78134401 - - moveq 0,r7 - and.b r7,r3 - test_move_cc 0 1 0 0 - checkr3 78134400 - - quit diff --git a/tests/tcg/cris/bare/check_asr.s b/tests/tcg/cris/bare/check_asr.s deleted file mode 100644 index 0a02ae6..0000000 --- a/tests/tcg/cris/bare/check_asr.s +++ /dev/null @@ -1,230 +0,0 @@ -# mach: crisv0 crisv3 crisv8 crisv10 crisv32 -# output: ffffffff\n1\nffffffff\nffffffff\n5a67f\nffffffff\nffffffff\nffffffff\nf699fc67\nffffffff\n1\nffffffff\nffffffff\n5a67f\nda67ffff\nda67ffff\nda67ffff\nda67fc67\nffffffff\nffffffff\n1\nffffffff\nffffffff\n5a670007\nda67f1ff\nda67f1ff\nda67f1ff\nda67f1e7\nffffffff\nffffffff\n1\nffffffff\nffffffff\nffffffff\n5a67f1ff\n5a67f1f9\n0\n5a670000\n - - .include "testutils.inc" - start - moveq -1,r3 - asrq 0,r3 - test_move_cc 1 0 0 0 - checkr3 ffffffff - - moveq 2,r3 - asrq 1,r3 - test_move_cc 0 0 0 0 - checkr3 1 - - moveq -1,r3 - asrq 31,r3 - test_move_cc 1 0 0 0 - checkr3 ffffffff - - moveq -1,r3 - asrq 15,r3 - test_move_cc 1 0 0 0 - checkr3 ffffffff - - move.d 0x5a67f19f,r3 - asrq 12,r3 - test_move_cc 0 0 0 0 - checkr3 5a67f - - move.d 0xda67f19f,r3 - move.d 31,r4 - asr.d r4,r3 - test_move_cc 1 0 0 0 - checkr3 ffffffff - - move.d 0xda67f19f,r3 - move.d 32,r4 - asr.d r4,r3 - test_move_cc 1 0 0 0 - checkr3 ffffffff - - move.d 0xda67f19f,r3 - move.d 33,r4 - asr.d r4,r3 - test_move_cc 1 0 0 0 - checkr3 ffffffff - - move.d 0xda67f19f,r3 - move.d 66,r4 - asr.d r4,r3 - test_move_cc 1 0 0 0 - checkr3 f699fc67 - - moveq -1,r3 - moveq 0,r4 - asr.d r4,r3 - test_move_cc 1 0 0 0 - checkr3 ffffffff - - moveq 2,r3 - moveq 1,r4 - asr.d r4,r3 - test_move_cc 0 0 0 0 - checkr3 1 - - moveq -1,r3 - moveq 31,r4 - asr.d r4,r3 - test_move_cc 1 0 0 0 - checkr3 ffffffff - - moveq -1,r3 - moveq 15,r4 - asr.d r4,r3 - test_move_cc 1 0 0 0 - checkr3 ffffffff - - move.d 0x5a67f19f,r3 - moveq 12,r4 - asr.d r4,r3 - test_move_cc 0 0 0 0 - checkr3 5a67f - - move.d 0xda67f19f,r3 - move.d 31,r4 - asr.w r4,r3 - test_move_cc 1 0 0 0 - checkr3 da67ffff - - move.d 0xda67f19f,r3 - move.d 32,r4 - asr.w r4,r3 - test_move_cc 1 0 0 0 - checkr3 da67ffff - - move.d 0xda67f19f,r3 - move.d 33,r4 - asr.w r4,r3 - test_move_cc 1 0 0 0 - checkr3 da67ffff - - move.d 0xda67f19f,r3 - move.d 66,r4 - asr.w r4,r3 - test_move_cc 1 0 0 0 - checkr3 da67fc67 - - moveq -1,r3 - moveq 0,r4 - asr.w r4,r3 - test_move_cc 1 0 0 0 - checkr3 ffffffff - - moveq -1,r3 - moveq 1,r4 - asr.w r4,r3 - test_move_cc 1 0 0 0 - checkr3 ffffffff - - moveq 2,r3 - moveq 1,r4 - asr.w r4,r3 - test_move_cc 0 0 0 0 - checkr3 1 - - moveq -1,r3 - moveq 31,r4 - asr.w r4,r3 - test_move_cc 1 0 0 0 - checkr3 ffffffff - - moveq -1,r3 - moveq 15,r4 - asr.w r4,r3 - test_move_cc 1 0 0 0 - checkr3 ffffffff - - move.d 0x5a67719f,r3 - moveq 12,r4 - asr.w r4,r3 - test_move_cc 0 0 0 0 - checkr3 5a670007 - - move.d 0xda67f19f,r3 - move.d 31,r4 - asr.b r4,r3 - test_move_cc 1 0 0 0 - checkr3 da67f1ff - - move.d 0xda67f19f,r3 - move.d 32,r4 - asr.b r4,r3 - test_move_cc 1 0 0 0 - checkr3 da67f1ff - - move.d 0xda67f19f,r3 - move.d 33,r4 - asr.b r4,r3 - test_move_cc 1 0 0 0 - checkr3 da67f1ff - - move.d 0xda67f19f,r3 - move.d 66,r4 - asr.b r4,r3 - test_move_cc 1 0 0 0 - checkr3 da67f1e7 - - moveq -1,r3 - moveq 0,r4 - asr.b r4,r3 - test_move_cc 1 0 0 0 - checkr3 ffffffff - - moveq -1,r3 - moveq 1,r4 - asr.b r4,r3 - test_move_cc 1 0 0 0 - checkr3 ffffffff - - moveq 2,r3 - moveq 1,r4 - asr.b r4,r3 - test_move_cc 0 0 0 0 - checkr3 1 - - moveq -1,r3 - moveq 31,r4 - asr.b r4,r3 - test_move_cc 1 0 0 0 - checkr3 ffffffff - - moveq -1,r3 - moveq 15,r4 - asr.b r4,r3 - test_move_cc 1 0 0 0 - checkr3 ffffffff - - moveq -1,r3 - moveq 7,r4 - asr.b r4,r3 - test_move_cc 1 0 0 0 - checkr3 ffffffff - -; FIXME: was wrong. - move.d 0x5a67f19f,r3 - moveq 12,r4 - asr.b r4,r3 - test_move_cc 1 0 0 0 - checkr3 5a67f1ff - -; FIXME: was wrong. - move.d 0x5a67f19f,r3 - moveq 4,r4 - asr.b r4,r3 - test_move_cc 1 0 0 0 - checkr3 5a67f1f9 - - move.d 0x5a67f19f,r3 - asrq 31,r3 - test_move_cc 0 1 0 0 - checkr3 0 - - move.d 0x5a67419f,r3 - moveq 16,r4 - asr.w r4,r3 - test_move_cc 0 1 0 0 - checkr3 5a670000 - - quit diff --git a/tests/tcg/cris/bare/check_ba.s b/tests/tcg/cris/bare/check_ba.s deleted file mode 100644 index 873a408..0000000 --- a/tests/tcg/cris/bare/check_ba.s +++ /dev/null @@ -1,93 +0,0 @@ -# mach: crisv0 crisv3 crisv8 crisv10 crisv32 -# output: a\n - - - .set smalloffset,0 - .set largeoffset,0 - - - .macro fail - jump _fail - .endm - - .global main -main: - moveq 0,$r3 - -; Short forward branch. - ba 0f - addq 1,$r3 - fail - -; Max short forward branch. -1: - ba 2f - addq 1,$r3 - fail - -; Short backward branch. -0: - ba 1b - addq 1,$r3 - fail - - .space 254-2+smalloffset+1b-.,0 - moveq 0,$r3 - -2: -; Transit branch (long). - ba 3f - addq 1,$r3 - fail - - moveq 0,$r3 -4: -; Long forward branch. - ba 5f - addq 1,$r3 - fail - - .space 256-2-smalloffset+4b-.,0 - - moveq 0,$r3 - -; Max short backward branch. -3: - ba 4b - addq 1,$r3 - fail - -5: -; Max long forward branch. - ba 6f - addq 1,$r3 - fail - - .space 32766+largeoffset-2+5b-.,0 - - moveq 0,$r3 -6: -; Transit branch. - ba 7f - addq 1,$r3 - fail - - moveq 0,$r3 -9: - jsr pass - nop - -; Transit branch. - moveq 0,$r3 -7: - ba 8f - addq 1,$r3 - fail - - .space 32768-largeoffset+9b-.,0 - -8: -; Max long backward branch. - ba 9b - addq 1,$r3 - fail diff --git a/tests/tcg/cris/bare/check_bas.s b/tests/tcg/cris/bare/check_bas.s deleted file mode 100644 index 11929d4..0000000 --- a/tests/tcg/cris/bare/check_bas.s +++ /dev/null @@ -1,102 +0,0 @@ -# mach: crisv32 -# output: 0\n0\n0\nfb349abc\n0\n12124243\n0\n0\neab5baad\n0\nefb37832\n - - .include "testutils.inc" - start -x: - setf zncv - bsr 0f - nop -0: - test_cc 1 1 1 1 - move srp,r3 - sub.d 0b,r3 - checkr3 0 - - bas 1f,mof - moveq 0,r0 -6: - nop - quit - -2: - move srp,r3 - sub.d 3f,r3 - checkr3 0 - move srp,r4 - subq 4,r4 - move.d [r4],r3 - checkr3 fb349abc - - basc 4f,mof - nop - .dword 0x12124243 -7: - nop - quit - -8: - move mof,r3 - sub.d 7f,r3 - checkr3 0 - - move mof,r4 - subq 4,r4 - move.d [r4],r3 - checkr3 eab5baad - - jasc 9f,mof - nop - .dword 0xefb37832 -0: - quit - - quit -9: - move mof,r3 - sub.d 0b,r3 - checkr3 0 - - move mof,r4 - subq 4,r4 - move.d [r4],r3 - checkr3 efb37832 - - quit - -4: - move mof,r3 - sub.d 7b,r3 - checkr3 0 - move mof,r4 - subq 4,r4 - move.d [r4],r3 - checkr3 12124243 - basc 5f,bz - moveq 0,r3 - .dword 0x7634aeba - quit - - .space 32770,0 -1: - move mof,r3 - sub.d 6b,r3 - checkr3 0 - - bsrc 2b - nop - .dword 0xfb349abc -3: - - quit - -5: - move mof,r3 - sub.d 7b,r3 - checkr3 0 - move.d 8b,r6 - jasc r6,mof - nop - .dword 0xeab5baad -7: - quit diff --git a/tests/tcg/cris/bare/check_bcc.s b/tests/tcg/cris/bare/check_bcc.s deleted file mode 100644 index c57ffa6..0000000 --- a/tests/tcg/cris/bare/check_bcc.s +++ /dev/null @@ -1,197 +0,0 @@ - .global main - .type main, @function -main: - clearf nzvc - setf nzv - bcc 0f - addq 1, $r3 - jump dofail - -0: - clearf nzvc - setf nzv - bcs dofail - addq 1,$r3 - - clearf nzvc - setf ncv - bne 1f - addq 1, $r3 - -fail: -dofail: - jump _fail - -1: - clearf nzvc - setf ncv - beq dofail - addq 1,$r3 - - clearf nzvc - setf ncz - bvc 2f - addq 1,$r3 - jump dofail - -2: - clearf nzvc - setf ncz - bvs dofail - addq 1,$r3 - - clearf nzvc - setf vcz - bpl 3f - addq 1,$r3 - jump fail -3: - clearf nzvc - setf vcz - bmi dofail - addq 1,$r3 - - clearf nzvc - setf nv - bls dofail - addq 1,$r3 - - clearf nzvc - setf nv - bhi 4f - addq 1,$r3 - jump dofail - -4: - clearf nzvc - setf zc - bge 5f - addq 1,$r3 - jump dofail - -5: - clearf nzvc - setf zc - blt dofail - addq 1,$r3 - - clearf nzvc - setf c - bgt 6f - addq 1,$r3 - jump fail - -6: - clearf nzvc - setf c - ble dofail - addq 1,$r3 - -;;;;;;;;;; - - setf nzvc - clearf nzv - bcc dofail - addq 1,$r3 - - setf nzvc - clearf nzv - bcs 0f - addq 1,$r3 - jump fail - -0: - setf nzvc - clearf ncv - bne dofail - addq 1,$r3 - - setf nzvc - clearf ncv - beq 1f - addq 1,$r3 - jump fail - -1: - setf nzvc - clearf ncz - bvc dofail - addq 1,$r3 - - setf nzvc - clearf ncz - bvs 2f - addq 1,$r3 - jump fail - -2: - setf nzvc - clearf vcz - bpl dofail - addq 1,$r3 - - setf nzvc - clearf vcz - bmi 3f - addq 1,$r3 - jump fail - -3: - setf nzvc - clearf nv - bls 4f - addq 1,$r3 - jump fail - -4: - setf nzvc - clearf nv - bhi dofail - addq 1,$r3 - - setf zvc - clearf nzc - bge dofail - addq 1,$r3 - - setf nzc - clearf vzc - blt 5f - addq 1,$r3 - jump fail - -5: - setf nzvc - clearf c - bgt dofail - addq 1,$r3 - - setf nzvc - clearf c - ble 6f - addq 1,$r3 - jump fail - -6: - ; do a forward branch. - ba 2f - nop - .fill 100 -1: - ba 3f - nop - .fill 800 -2: - ba 1b - nop - .fill 1024 -3: - - moveq 31, $r0 -1: bne 1b - subq 1, $r0 - - jsr pass - moveq 0, $r10 - ret - nop diff --git a/tests/tcg/cris/bare/check_boundc.s b/tests/tcg/cris/bare/check_boundc.s deleted file mode 100644 index fb9e5bc..0000000 --- a/tests/tcg/cris/bare/check_boundc.s +++ /dev/null @@ -1,101 +0,0 @@ -# mach: crisv0 crisv3 crisv8 crisv10 crisv32 -# output: 2\n2\nffff\nffffffff\n5432f789\n2\nffff\n2\nffff\nffff\nf789\n2\n2\nff\nff\nff\n89\n0\nff\n - - .include "testutils.inc" - start - moveq -1,r3 - moveq 2,r4 - bound.d 2,r3 - test_move_cc 0 0 0 0 - checkr3 2 - - moveq 2,r3 - bound.d 0xffffffff,r3 - test_move_cc 0 0 0 0 - checkr3 2 - - move.d 0xffff,r3 - bound.d 0xffff,r3 - test_move_cc 0 0 0 0 - checkr3 ffff - - moveq -1,r3 - bound.d 0xffffffff,r3 - test_move_cc 1 0 0 0 - checkr3 ffffffff - - move.d 0x78134452,r3 - bound.d 0x5432f789,r3 - test_move_cc 0 0 0 0 - checkr3 5432f789 - - moveq -1,r3 - bound.w 2,r3 - test_move_cc 0 0 0 0 - checkr3 2 - - moveq -1,r3 - bound.w 0xffff,r3 - test_move_cc 0 0 0 0 - checkr3 ffff - - moveq 2,r3 - bound.w 0xffff,r3 - test_move_cc 0 0 0 0 - checkr3 2 - - move.d 0xffff,r3 - bound.w 0xffff,r3 - test_move_cc 0 0 0 0 - checkr3 ffff - - move.d 0xfedaffff,r3 - bound.w 0xffff,r3 - test_move_cc 0 0 0 0 - checkr3 ffff - - move.d 0x78134452,r3 - bound.w 0xf789,r3 - test_move_cc 0 0 0 0 - checkr3 f789 - - moveq -1,r3 - bound.b 2,r3 - test_move_cc 0 0 0 0 - checkr3 2 - - moveq 2,r3 - bound.b 0xff,r3 - test_move_cc 0 0 0 0 - checkr3 2 - - moveq -1,r3 - bound.b 0xff,r3 - test_move_cc 0 0 0 0 - checkr3 ff - - move.d 0xff,r3 - bound.b 0xff,r3 - test_move_cc 0 0 0 0 - checkr3 ff - - move.d 0xfeda49ff,r3 - bound.b 0xff,r3 - test_move_cc 0 0 0 0 - checkr3 ff - - move.d 0x78134452,r3 - bound.b 0x89,r3 - test_move_cc 0 0 0 0 - checkr3 89 - - bound.w 0,r3 - test_move_cc 0 1 0 0 - checkr3 0 - - move.d 0xffff,r3 - bound.b -1,r3 - test_move_cc 0 0 0 0 - checkr3 ff - - quit diff --git a/tests/tcg/cris/bare/check_boundr.s b/tests/tcg/cris/bare/check_boundr.s deleted file mode 100644 index 5c50cc5..0000000 --- a/tests/tcg/cris/bare/check_boundr.s +++ /dev/null @@ -1,125 +0,0 @@ -# mach: crisv0 crisv3 crisv8 crisv10 crisv32 -# output: 2\n2\nffff\nffffffff\n5432f789\n2\n2\nffff\nffff\nffff\nf789\n2\n2\nff\nff\n89\nfeda4953\nfeda4962\n0\n0\n - - .include "testutils.inc" - start - moveq -1,r3 - moveq 2,r4 - bound.d r4,r3 - test_move_cc 0 0 0 0 - checkr3 2 - - moveq 2,r3 - moveq -1,r4 - bound.d r4,r3 - test_move_cc 0 0 0 0 - checkr3 2 - - move.d 0xffff,r4 - move.d r4,r3 - bound.d r4,r3 - test_move_cc 0 0 0 0 - checkr3 ffff - - moveq -1,r4 - move.d r4,r3 - bound.d r4,r3 - test_move_cc 1 0 0 0 - checkr3 ffffffff - - move.d 0x5432f789,r4 - move.d 0x78134452,r3 - bound.d r4,r3 - test_move_cc 0 0 0 0 - checkr3 5432f789 - - moveq -1,r3 - moveq 2,r4 - bound.w r4,r3 - test_move_cc 0 0 0 0 - checkr3 2 - - moveq 2,r3 - moveq -1,r4 - bound.w r4,r3 - test_move_cc 0 0 0 0 - checkr3 2 - - moveq -1,r3 - bound.w r3,r3 - test_move_cc 0 0 0 0 - checkr3 ffff - - move.d 0xffff,r4 - move.d r4,r3 - bound.w r4,r3 - test_move_cc 0 0 0 0 - checkr3 ffff - - move.d 0xfedaffff,r4 - move.d r4,r3 - bound.w r4,r3 - test_move_cc 0 0 0 0 - checkr3 ffff - - move.d 0x5432f789,r4 - move.d 0x78134452,r3 - bound.w r4,r3 - test_move_cc 0 0 0 0 - checkr3 f789 - - moveq -1,r3 - moveq 2,r4 - bound.b r4,r3 - test_move_cc 0 0 0 0 - checkr3 2 - - moveq 2,r3 - moveq -1,r4 - bound.b r4,r3 - test_move_cc 0 0 0 0 - checkr3 2 - - move.d 0xff,r4 - move.d r4,r3 - bound.b r4,r3 - test_move_cc 0 0 0 0 - checkr3 ff - - move.d 0xfeda49ff,r4 - move.d r4,r3 - bound.b r4,r3 - test_move_cc 0 0 0 0 - checkr3 ff - - move.d 0x5432f789,r4 - move.d 0x78134452,r3 - bound.b r4,r3 - test_move_cc 0 0 0 0 - checkr3 89 - - move.d 0xfeda4956,r3 - move.d 0xfeda4953,r4 - bound.d r4,r3 - test_move_cc 1 0 0 0 - checkr3 feda4953 - - move.d 0xfeda4962,r3 - move.d 0xfeda4963,r4 - bound.d r4,r3 - test_move_cc 1 0 0 0 - checkr3 feda4962 - - move.d 0xfeda4956,r3 - move.d 0,r4 - bound.d r4,r3 - test_move_cc 0 1 0 0 - checkr3 0 - - move.d 0xfeda4956,r4 - move.d 0,r3 - bound.d r4,r3 - test_move_cc 0 1 0 0 - checkr3 0 - - quit diff --git a/tests/tcg/cris/bare/check_btst.s b/tests/tcg/cris/bare/check_btst.s deleted file mode 100644 index 485deb2..0000000 --- a/tests/tcg/cris/bare/check_btst.s +++ /dev/null @@ -1,96 +0,0 @@ -# mach: crisv0 crisv3 crisv8 crisv10 crisv32 -# output: 1111\n - - .include "testutils.inc" - start - clearf nzvc - moveq -1,r3 - .if 1 ;..asm.arch.cris.v32 - .else - setf vc - .endif - btstq 0,r3 - test_cc 1 0 0 0 - - moveq 2,r3 - btstq 1,r3 - test_cc 1 0 0 0 - - moveq 4,r3 - btstq 1,r3 - test_cc 0 1 0 0 - - moveq -1,r3 - btstq 31,r3 - test_cc 1 0 0 0 - - move.d 0x5a67f19f,r3 - btstq 12,r3 - test_cc 1 0 0 0 - - move.d 0xda67f19f,r3 - move.d 29,r4 - btst r4,r3 - test_cc 0 0 0 0 - - move.d 0xda67f19f,r3 - move.d 32,r4 - btst r4,r3 - test_cc 1 0 0 0 - - move.d 0xda67f191,r3 - move.d 33,r4 - btst r4,r3 - test_cc 0 0 0 0 - - moveq -1,r3 - moveq 0,r4 - btst r4,r3 - test_cc 1 0 0 0 - - moveq 2,r3 - moveq 1,r4 - btst r4,r3 - test_cc 1 0 0 0 - - moveq -1,r3 - moveq 31,r4 - btst r4,r3 - test_cc 1 0 0 0 - - moveq 4,r3 - btstq 1,r3 - test_cc 0 1 0 0 - - moveq -1,r3 - moveq 15,r4 - btst r4,r3 - test_cc 1 0 0 0 - - move.d 0x5a67f19f,r3 - moveq 12,r4 - btst r4,r3 - test_cc 1 0 0 0 - - move.d 0x5a678000,r3 - moveq 11,r4 - btst r4,r3 - test_cc 0 1 0 0 - - move.d 0x5a67f19f,r3 - btst r3,r3 - test_cc 0 0 0 0 - - move.d 0x1111,r3 - checkr3 1111 - - ; check that X gets cleared and that only the NZ flags are touched. - ;; move.d 0xff, $r0 - ;; move $r0, $ccs - ;; btst r3,r3 - ;; move $ccs, $r0 - ;; and.d 0xff, $r0 - ;; cmp.d 0xe3, $r0 - ;; test_cc 0 1 0 0 - - quit diff --git a/tests/tcg/cris/bare/check_clearfv32.s b/tests/tcg/cris/bare/check_clearfv32.s deleted file mode 100644 index 4e91360..0000000 --- a/tests/tcg/cris/bare/check_clearfv32.s +++ /dev/null @@ -1,19 +0,0 @@ -# mach: crisv32 -# output: ef\nef\n - -; Check that "clearf x" doesn't trivially fail. - - .include "testutils.inc" - start - setf puixnzvc - clearf x ; Actually, x would be cleared by almost-all other insns. - move ccs,r3 - and.d 0xff, $r3 - checkr3 ef - - setf puixnzvc - moveq 0, $r3 ; moveq should only clear the xflag. - move ccs,r3 - and.d 0xff, $r3 - checkr3 ef - quit diff --git a/tests/tcg/cris/bare/check_clrjmp1.s b/tests/tcg/cris/bare/check_clrjmp1.s deleted file mode 100644 index 45a7005..0000000 --- a/tests/tcg/cris/bare/check_clrjmp1.s +++ /dev/null @@ -1,36 +0,0 @@ -# mach: crisv3 crisv8 crisv10 crisv32 -# output: ffffff00\n - -; A bug resulting in a non-effectual clear.b discovered running the GCC -; testsuite; jump actually wrote to p0. - - .include "testutils.inc" - - start - jump 1f - nop - .p2align 8 -1: - move.d y,r4 - - .if 0 ;0 == ..asm.arch.cris.v32 -; There was a bug causing this insn to set special register p0 -; (byte-clear) to 8 (low 8 bits of location after insn). - jump [r4+] - .endif - -1: - move.d 0f,r4 - -; The corresponding bug would cause this insn too, to set p0. - jump r4 - nop - quit -0: - moveq -1,r3 - clear.b r3 - checkr3 ffffff00 - quit - -y: - .dword 1b diff --git a/tests/tcg/cris/bare/check_cmp-2.s b/tests/tcg/cris/bare/check_cmp-2.s deleted file mode 100644 index 414d370..0000000 --- a/tests/tcg/cris/bare/check_cmp-2.s +++ /dev/null @@ -1,15 +0,0 @@ - - -.include "testutils.inc" - - start - - move.d 4294967283, $r0 - move.d $r0, $r10 - cmp.d $r0, $r10 - beq 1f - move.d $r10, $r3 - fail -1: - pass - quit diff --git a/tests/tcg/cris/bare/check_cmpc.s b/tests/tcg/cris/bare/check_cmpc.s deleted file mode 100644 index 267c9ba..0000000 --- a/tests/tcg/cris/bare/check_cmpc.s +++ /dev/null @@ -1,86 +0,0 @@ -# mach: crisv0 crisv3 crisv8 crisv10 crisv32 -# output: ffffffff\n2\nffff\nffffffff\n78134452\nffffffff\n2\nffff\nfedaffff\n78134452\nffffffff\n2\nff\nfeda49ff\n78134452\n85649282\n - - .include "testutils.inc" - start - moveq -1,r3 - cmp.d -2,r3 - test_cc 0 0 0 0 - checkr3 ffffffff - - moveq 2,r3 - cmp.d 1,r3 - test_cc 0 0 0 0 - checkr3 2 - - move.d 0xffff,r3 - cmp.d -0xffff,r3 - test_cc 0 0 0 1 - checkr3 ffff - - moveq -1,r3 - cmp.d 1,r3 - test_cc 1 0 0 0 - checkr3 ffffffff - - move.d 0x78134452,r3 - cmp.d -0x5432f789,r3 - test_cc 1 0 1 1 - checkr3 78134452 - - moveq -1,r3 - cmp.w -2,r3 - test_cc 0 0 0 0 - checkr3 ffffffff - - moveq 2,r3 - cmp.w 1,r3 - test_cc 0 0 0 0 - checkr3 2 - - move.d 0xffff,r3 - cmp.w 1,r3 - test_cc 1 0 0 0 - checkr3 ffff - - move.d 0xfedaffff,r3 - cmp.w 1,r3 - test_cc 1 0 0 0 - checkr3 fedaffff - - move.d 0x78134452,r3 - cmp.w 0x877,r3 - test_cc 0 0 0 0 - checkr3 78134452 - - moveq -1,r3 - cmp.b -2,r3 - test_cc 0 0 0 0 - checkr3 ffffffff - - moveq 2,r3 - cmp.b 1,r3 - test_cc 0 0 0 0 - checkr3 2 - - move.d 0xff,r3 - cmp.b 1,r3 - test_cc 1 0 0 0 - checkr3 ff - - move.d 0xfeda49ff,r3 - cmp.b 1,r3 - test_cc 1 0 0 0 - checkr3 feda49ff - - move.d 0x78134452,r3 - cmp.b 0x77,r3 - test_cc 1 0 0 1 - checkr3 78134452 - - move.d 0x85649282,r3 - cmp.b 0x82,r3 - test_cc 0 1 0 0 - checkr3 85649282 - - quit diff --git a/tests/tcg/cris/bare/check_cmpm.s b/tests/tcg/cris/bare/check_cmpm.s deleted file mode 100644 index e4dde15..0000000 --- a/tests/tcg/cris/bare/check_cmpm.s +++ /dev/null @@ -1,96 +0,0 @@ -# mach: crisv0 crisv3 crisv8 crisv10 crisv32 -# output: ffffffff\n2\nffff\nffffffff\n78134452\nffffffff\n2\nffff\nfedaffff\n78134452\nffffffff\n2\nff\nfeda49ff\n78134452\n85649222\n - - .include "testutils.inc" - .data -x: - .dword -2,1,-0xffff,1,-0x5432f789 - .word -2,1,1,0x877 - .byte -2,1,0x77 - .byte 0x22 - - start - moveq -1,r3 - move.d x,r5 - cmp.d [r5+],r3 - test_cc 0 0 0 0 - checkr3 ffffffff - - moveq 2,r3 - cmp.d [r5],r3 - test_cc 0 0 0 0 - addq 4,r5 - checkr3 2 - - move.d 0xffff,r3 - cmp.d [r5+],r3 - test_cc 0 0 0 1 - checkr3 ffff - - moveq -1,r3 - cmp.d [r5+],r3 - test_cc 1 0 0 0 - checkr3 ffffffff - - move.d 0x78134452,r3 - cmp.d [r5+],r3 - test_cc 1 0 1 1 - checkr3 78134452 - - moveq -1,r3 - cmp.w [r5+],r3 - test_cc 0 0 0 0 - checkr3 ffffffff - - moveq 2,r3 - cmp.w [r5+],r3 - test_cc 0 0 0 0 - checkr3 2 - - move.d 0xffff,r3 - cmp.w [r5],r3 - test_cc 1 0 0 0 - checkr3 ffff - - move.d 0xfedaffff,r3 - cmp.w [r5+],r3 - test_cc 1 0 0 0 - checkr3 fedaffff - - move.d 0x78134452,r3 - cmp.w [r5+],r3 - test_cc 0 0 0 0 - checkr3 78134452 - - moveq -1,r3 - cmp.b [r5],r3 - test_cc 0 0 0 0 - addq 1,r5 - checkr3 ffffffff - - moveq 2,r3 - cmp.b [r5],r3 - test_cc 0 0 0 0 - checkr3 2 - - move.d 0xff,r3 - cmp.b [r5],r3 - test_cc 1 0 0 0 - checkr3 ff - - move.d 0xfeda49ff,r3 - cmp.b [r5+],r3 - test_cc 1 0 0 0 - checkr3 feda49ff - - move.d 0x78134452,r3 - cmp.b [r5+],r3 - test_cc 1 0 0 1 - checkr3 78134452 - - move.d 0x85649222,r3 - cmp.b [r5],r3 - test_cc 0 1 0 0 - checkr3 85649222 - - quit diff --git a/tests/tcg/cris/bare/check_cmpq.s b/tests/tcg/cris/bare/check_cmpq.s deleted file mode 100644 index 5469141..0000000 --- a/tests/tcg/cris/bare/check_cmpq.s +++ /dev/null @@ -1,75 +0,0 @@ -# mach: crisv3 crisv8 crisv10 crisv32 -# output: 1\n1\n1\n1f\n1f\nffffffe1\nffffffe1\nffffffe0\n0\n0\nffffffff\nffffffff\n10000\n100\n5678900\n - - .include "testutils.inc" - start - moveq 1,r3 - cmpq 1,r3 - test_cc 0 1 0 0 - checkr3 1 - - cmpq -1,r3 - test_cc 0 0 0 1 - checkr3 1 - - cmpq 31,r3 - test_cc 1 0 0 1 - checkr3 1 - - moveq 31,r3 - cmpq 31,r3 - test_cc 0 1 0 0 - checkr3 1f - - cmpq -31,r3 - test_cc 0 0 0 1 - checkr3 1f - - movs.b -31,r3 - cmpq -31,r3 - test_cc 0 1 0 0 - checkr3 ffffffe1 - - cmpq -32,r3 - test_cc 0 0 0 0 - checkr3 ffffffe1 - - movs.b -32,r3 - cmpq -32,r3 - test_cc 0 1 0 0 - checkr3 ffffffe0 - - moveq 0,r3 - cmpq 1,r3 - test_cc 1 0 0 1 - checkr3 0 - - cmpq -32,r3 - test_cc 0 0 0 1 - checkr3 0 - - moveq -1,r3 - cmpq 1,r3 - test_cc 1 0 0 0 - checkr3 ffffffff - - cmpq -1,r3 - test_cc 0 1 0 0 - checkr3 ffffffff - - move.d 0x10000,r3 - cmpq 1,r3 - test_cc 0 0 0 0 - checkr3 10000 - - move.d 0x100,r3 - cmpq 1,r3 - test_cc 0 0 0 0 - checkr3 100 - - move.d 0x5678900,r3 - cmpq 7,r3 - test_cc 0 0 0 0 - checkr3 5678900 - - quit diff --git a/tests/tcg/cris/bare/check_cmpr.s b/tests/tcg/cris/bare/check_cmpr.s deleted file mode 100644 index b30af7a..0000000 --- a/tests/tcg/cris/bare/check_cmpr.s +++ /dev/null @@ -1,102 +0,0 @@ -# mach: crisv0 crisv3 crisv8 crisv10 crisv32 -# output: ffffffff\n2\nffff\nffffffff\n78134452\nffffffff\n2\nffff\nfedaffff\n78134452\nffffffff\n2\nff\nfeda49ff\n78134452\n85649222\n - - .include "testutils.inc" - start - moveq -1,r3 - moveq -2,r4 - cmp.d r4,r3 - test_cc 0 0 0 0 - checkr3 ffffffff - - moveq 2,r3 - moveq 1,r4 - cmp.d r4,r3 - test_cc 0 0 0 0 - checkr3 2 - - move.d 0xffff,r3 - move.d -0xffff,r4 - cmp.d r4,r3 - test_cc 0 0 0 1 - checkr3 ffff - - moveq 1,r4 - moveq -1,r3 - cmp.d r4,r3 - test_cc 1 0 0 0 - checkr3 ffffffff - - move.d -0x5432f789,r4 - move.d 0x78134452,r3 - cmp.d r4,r3 - test_cc 1 0 1 1 - checkr3 78134452 - - moveq -1,r3 - moveq -2,r4 - cmp.w r4,r3 - test_cc 0 0 0 0 - checkr3 ffffffff - - moveq 2,r3 - moveq 1,r4 - cmp.w r4,r3 - test_cc 0 0 0 0 - checkr3 2 - - move.d 0xffff,r3 - move.d -0xffff,r4 - cmp.w r4,r3 - test_cc 1 0 0 0 - checkr3 ffff - - move.d 0xfedaffff,r3 - move.d -0xfedaffff,r4 - cmp.w r4,r3 - test_cc 1 0 0 0 - checkr3 fedaffff - - move.d -0x5432f789,r4 - move.d 0x78134452,r3 - cmp.w r4,r3 - test_cc 0 0 0 0 - checkr3 78134452 - - moveq -1,r3 - moveq -2,r4 - cmp.b r4,r3 - test_cc 0 0 0 0 - checkr3 ffffffff - - moveq 2,r3 - moveq 1,r4 - cmp.b r4,r3 - test_cc 0 0 0 0 - checkr3 2 - - move.d -0xff,r4 - move.d 0xff,r3 - cmp.b r4,r3 - test_cc 1 0 0 0 - checkr3 ff - - move.d -0xfeda49ff,r4 - move.d 0xfeda49ff,r3 - cmp.b r4,r3 - test_cc 1 0 0 0 - checkr3 feda49ff - - move.d -0x5432f789,r4 - move.d 0x78134452,r3 - cmp.b r4,r3 - test_cc 1 0 0 1 - checkr3 78134452 - - move.d 0x85649222,r3 - move.d 0x77445622,r4 - cmp.b r4,r3 - test_cc 0 1 0 0 - checkr3 85649222 - - quit diff --git a/tests/tcg/cris/bare/check_cmpxc.s b/tests/tcg/cris/bare/check_cmpxc.s deleted file mode 100644 index b237a93..0000000 --- a/tests/tcg/cris/bare/check_cmpxc.s +++ /dev/null @@ -1,92 +0,0 @@ -# mach: crisv0 crisv3 crisv8 crisv10 crisv32 -# output: 2\n2\n2\n2\nffff\nffff\nffff\nffff\nffffffff\nffffffff\nffffffff\n78134452\n78134452\n78134452\n78134452\n4452\n80000032\n - - .include "testutils.inc" - start - moveq 2,r3 - cmps.b 0xff,r3 - test_cc 0 0 0 1 - checkr3 2 - - moveq 2,r3 - cmps.w 0xffff,r3 - test_cc 0 0 0 1 - checkr3 2 - - moveq 2,r3 - cmpu.b 0xff,r3 - test_cc 1 0 0 1 - checkr3 2 - - moveq 2,r3 - move.d 0xffffffff,r4 - cmpu.w -1,r3 - test_cc 1 0 0 1 - checkr3 2 - - move.d 0xffff,r3 - cmpu.b -1,r3 - test_cc 0 0 0 0 - checkr3 ffff - - move.d 0xffff,r3 - cmpu.w -1,r3 - test_cc 0 1 0 0 - checkr3 ffff - - move.d 0xffff,r3 - cmps.b 0xff,r3 - test_cc 0 0 0 1 - checkr3 ffff - - move.d 0xffff,r3 - cmps.w 0xffff,r3 - test_cc 0 0 0 1 - checkr3 ffff - - moveq -1,r3 - cmps.b 0xff,r3 - test_cc 0 1 0 0 - checkr3 ffffffff - - moveq -1,r3 - cmps.w 0xff,r3 - test_cc 1 0 0 0 - checkr3 ffffffff - - moveq -1,r3 - cmps.w 0xffff,r3 - test_cc 0 1 0 0 - checkr3 ffffffff - - move.d 0x78134452,r3 - cmpu.b 0x89,r3 - test_cc 0 0 0 0 - checkr3 78134452 - - move.d 0x78134452,r3 - cmps.b 0x89,r3 - test_cc 0 0 0 1 - checkr3 78134452 - - move.d 0x78134452,r3 - cmpu.w 0xf789,r3 - test_cc 0 0 0 0 - checkr3 78134452 - - move.d 0x78134452,r3 - cmps.w 0xf789,r3 - test_cc 0 0 0 1 - checkr3 78134452 - - move.d 0x4452,r3 - cmps.w 0x8002,r3 - test_cc 0 0 0 1 - checkr3 4452 - - move.d 0x80000032,r3 - cmpu.w 0x764,r3 - test_cc 0 0 1 0 - checkr3 80000032 - - quit diff --git a/tests/tcg/cris/bare/check_cmpxm.s b/tests/tcg/cris/bare/check_cmpxm.s deleted file mode 100644 index 87ea5bf..0000000 --- a/tests/tcg/cris/bare/check_cmpxm.s +++ /dev/null @@ -1,106 +0,0 @@ -# mach: crisv0 crisv3 crisv8 crisv10 crisv32 -# output: 2\n2\n2\n2\nffff\nffff\nffff\nffff\nffffffff\nffffffff\nffffffff\n78134452\n78134452\n78134452\n78134452\n4452\n80000032\n - - .include "testutils.inc" - .data -x: - .byte 0xff - .word 0xffff - .word 0xff - .word 0xffff - .byte 0x89 - .word 0xf789 - .word 0x8002 - .word 0x764 - - start - moveq 2,r3 - move.d x,r5 - cmps.b [r5+],r3 - test_cc 0 0 0 1 - checkr3 2 - - moveq 2,r3 - cmps.w [r5+],r3 - test_cc 0 0 0 1 - checkr3 2 - - moveq 2,r3 - subq 3,r5 - cmpu.b [r5+],r3 - test_cc 1 0 0 1 - checkr3 2 - - moveq 2,r3 - cmpu.w [r5+],r3 - test_cc 1 0 0 1 - subq 3,r5 - checkr3 2 - - move.d 0xffff,r3 - cmpu.b [r5],r3 - test_cc 0 0 0 0 - checkr3 ffff - - move.d 0xffff,r3 - cmpu.w [r5],r3 - test_cc 0 1 0 0 - checkr3 ffff - - move.d 0xffff,r3 - cmps.b [r5],r3 - test_cc 0 0 0 1 - checkr3 ffff - - move.d 0xffff,r3 - cmps.w [r5],r3 - test_cc 0 0 0 1 - checkr3 ffff - - moveq -1,r3 - cmps.b [r5],r3 - test_cc 0 1 0 0 - addq 3,r5 - checkr3 ffffffff - - moveq -1,r3 - cmps.w [r5+],r3 - test_cc 1 0 0 0 - checkr3 ffffffff - - moveq -1,r3 - cmps.w [r5+],r3 - test_cc 0 1 0 0 - checkr3 ffffffff - - move.d 0x78134452,r3 - cmpu.b [r5],r3 - test_cc 0 0 0 0 - checkr3 78134452 - - move.d 0x78134452,r3 - cmps.b [r5+],r3 - test_cc 0 0 0 1 - checkr3 78134452 - - move.d 0x78134452,r3 - cmpu.w [r5],r3 - test_cc 0 0 0 0 - checkr3 78134452 - - move.d 0x78134452,r3 - cmps.w [r5+],r3 - test_cc 0 0 0 1 - checkr3 78134452 - - move.d 0x4452,r3 - cmps.w [r5+],r3 - test_cc 0 0 0 1 - checkr3 4452 - - move.d 0x80000032,r3 - cmpu.w [r5+],r3 - test_cc 0 0 1 0 - checkr3 80000032 - - quit diff --git a/tests/tcg/cris/bare/check_dstep.s b/tests/tcg/cris/bare/check_dstep.s deleted file mode 100644 index bd43b83..0000000 --- a/tests/tcg/cris/bare/check_dstep.s +++ /dev/null @@ -1,42 +0,0 @@ -# mach: crisv0 crisv3 crisv8 crisv10 crisv32 -# output: fffffffc\n4\nffff\nfffffffe\n9bf3911b\n0\n - - .include "testutils.inc" - start - moveq -1,r3 - moveq 2,r4 - dstep r4,r3 - test_move_cc 1 0 0 0 - checkr3 fffffffc - - moveq 2,r3 - moveq -1,r4 - dstep r4,r3 - test_move_cc 0 0 0 0 - checkr3 4 - - move.d 0xffff,r4 - move.d r4,r3 - dstep r4,r3 - test_move_cc 0 0 0 0 - checkr3 ffff - - moveq -1,r4 - move.d r4,r3 - dstep r4,r3 - test_move_cc 1 0 0 0 - checkr3 fffffffe - - move.d 0x5432f789,r4 - move.d 0x78134452,r3 - dstep r4,r3 - test_move_cc 1 0 0 0 - checkr3 9bf3911b - - move.d 0xffff,r3 - move.d 0x1fffe,r4 - dstep r4,r3 - test_move_cc 0 1 0 0 - checkr3 0 - - quit diff --git a/tests/tcg/cris/bare/check_jsr.s b/tests/tcg/cris/bare/check_jsr.s deleted file mode 100644 index 1060237..0000000 --- a/tests/tcg/cris/bare/check_jsr.s +++ /dev/null @@ -1,85 +0,0 @@ -# mach: crisv3 crisv8 crisv10 crisv32 -# output: 0\n0\n0\n0\n0\n0\n - -# Test that jsr Rn and jsr [PC+] work. - - .include "testutils.inc" - start -x: - move.d 0f,r6 - setf nzvc - jsr r6 - .if 1; ..asm.arch.cris.v32 - nop - .endif -0: - test_move_cc 1 1 1 1 - move srp,r3 - sub.d 0b,r3 - checkr3 0 - - move.d 1f,r0 - setf nzvc - jsr r0 - .if 1 ; ..asm.arch.cris.v32 - moveq 0,r0 - .endif -6: - nop - quit - -2: - test_move_cc 0 0 0 0 - move srp,r3 - sub.d 3f,r3 - checkr3 0 - jsr 4f - .if 1 ; ..asm.arch.cris.v32 - nop - .endif -7: - nop - quit - -8: - move srp,r3 - sub.d 7b,r3 - checkr3 0 - quit - -4: - move srp,r3 - sub.d 7b,r3 - checkr3 0 - move.d 5f,r3 - jump r3 - .if 1; ..asm.arch.cris.v32 - moveq 0,r3 - .endif - quit - - .space 32770,0 -1: - test_move_cc 1 1 1 1 - move srp,r3 - sub.d 6b,r3 - checkr3 0 - - clearf cznv - jsr 2b - .if 1; ..asm.arch.cris.v32 - nop - .endif -3: - - quit - -5: - move srp,r3 - sub.d 7b,r3 - checkr3 0 - jump 8b - .if 1 ; ..asm.arch.cris.v32 - nop - .endif - quit diff --git a/tests/tcg/cris/bare/check_lapc.s b/tests/tcg/cris/bare/check_lapc.s deleted file mode 100644 index 9a6150b..0000000 --- a/tests/tcg/cris/bare/check_lapc.s +++ /dev/null @@ -1,78 +0,0 @@ -# mach: crisv32 -# output: 0\n0\nfffffffa\nfffffffe\nffffffda\n1e\n1e\n0\n - -.include "testutils.inc" - -; To accommodate dumpr3 with more than one instruction, keep it -; out of lapc operand ranges and difference calculations. - - start - lapc.d 0f,r3 -0: - sub.d .,r3 - checkr3 0 - - lapcq 0f,r3 -0: - sub.d .,r3 - checkr3 0 - - lapc.d .,r3 - sub.d .,r3 - checkr3 fffffffa - - lapcq .,r3 - sub.d .,r3 - checkr3 fffffffe - -0: - .rept 16 - nop - .endr - lapc.d 0b,r3 - sub.d .,r3 - checkr3 ffffffda - - setf zcvn - lapc.d 0f,r3 - test_cc 1 1 1 1 - sub.d .,r3 - nop - nop - nop - nop - nop - nop - nop - nop - nop - nop - nop - nop -0: - checkr3 1e -0: - lapcq 0f,r3 - sub.d 0b,r3 - nop - nop - nop - nop - nop - nop - nop - nop - nop - nop - nop -0: - checkr3 1e - clearf cn - setf zv -1: - lapcq .,r3 - test_cc 0 1 1 0 - sub.d 1b,r3 - checkr3 0 - - quit diff --git a/tests/tcg/cris/bare/check_lsl.s b/tests/tcg/cris/bare/check_lsl.s deleted file mode 100644 index 9e2ddd7..0000000 --- a/tests/tcg/cris/bare/check_lsl.s +++ /dev/null @@ -1,217 +0,0 @@ -# mach: crisv0 crisv3 crisv8 crisv10 crisv32 -# output: ffffffff\n4\n80000000\nffff8000\n7f19f000\n80000000\n0\n0\n699fc67c\nffffffff\n4\n80000000\nffff8000\n7f19f000\nda670000\nda670000\nda670000\nda67c67c\nffffffff\nfffafffe\n4\nffff0000\nffff8000\n5a67f000\nda67f100\nda67f100\nda67f100\nda67f17c\nfff3faff\nfff3fafe\n4\nffffff00\nffffff00\nffffff80\n5a67f100\n5a67f1f0\n - - .include "testutils.inc" - start - moveq -1,r3 - lslq 0,r3 - test_move_cc 1 0 0 0 - checkr3 ffffffff - - moveq 2,r3 - lslq 1,r3 - test_move_cc 0 0 0 0 - checkr3 4 - - moveq -1,r3 - lslq 31,r3 - test_move_cc 1 0 0 0 - checkr3 80000000 - - moveq -1,r3 - lslq 15,r3 - test_move_cc 1 0 0 0 - checkr3 ffff8000 - - move.d 0x5a67f19f,r3 - lslq 12,r3 - test_move_cc 0 0 0 0 - checkr3 7f19f000 - - move.d 0xda67f19f,r3 - move.d 31,r4 - lsl.d r4,r3 - test_move_cc 1 0 0 0 - checkr3 80000000 - - move.d 0xda67f19f,r3 - move.d 32,r4 - lsl.d r4,r3 - test_move_cc 0 1 0 0 - checkr3 0 - - move.d 0xda67f19f,r3 - move.d 33,r4 - lsl.d r4,r3 - test_move_cc 0 1 0 0 - checkr3 0 - - move.d 0xda67f19f,r3 - move.d 66,r4 - lsl.d r4,r3 - test_move_cc 0 0 0 0 - checkr3 699fc67c - - moveq -1,r3 - moveq 0,r4 - lsl.d r4,r3 - test_move_cc 1 0 0 0 - checkr3 ffffffff - - moveq 2,r3 - moveq 1,r4 - lsl.d r4,r3 - test_move_cc 0 0 0 0 - checkr3 4 - - moveq -1,r3 - moveq 31,r4 - lsl.d r4,r3 - test_move_cc 1 0 0 0 - checkr3 80000000 - - moveq -1,r3 - moveq 15,r4 - lsl.d r4,r3 - test_move_cc 1 0 0 0 - checkr3 ffff8000 - - move.d 0x5a67f19f,r3 - moveq 12,r4 - lsl.d r4,r3 - test_move_cc 0 0 0 0 - checkr3 7f19f000 - - move.d 0xda67f19f,r3 - move.d 31,r4 - lsl.w r4,r3 - test_move_cc 0 1 0 0 - checkr3 da670000 - - move.d 0xda67f19f,r3 - move.d 32,r4 - lsl.w r4,r3 - test_move_cc 0 1 0 0 - checkr3 da670000 - - move.d 0xda67f19f,r3 - move.d 33,r4 - lsl.w r4,r3 - test_move_cc 0 1 0 0 - checkr3 da670000 - - move.d 0xda67f19f,r3 - move.d 66,r4 - lsl.w r4,r3 - test_move_cc 1 0 0 0 - checkr3 da67c67c - - moveq -1,r3 - moveq 0,r4 - lsl.w r4,r3 - test_move_cc 1 0 0 0 - checkr3 ffffffff - - move.d 0xfffaffff,r3 - moveq 1,r4 - lsl.w r4,r3 - test_move_cc 1 0 0 0 - checkr3 fffafffe - - moveq 2,r3 - moveq 1,r4 - lsl.w r4,r3 - test_move_cc 0 0 0 0 - checkr3 4 - - moveq -1,r3 - moveq 31,r4 - lsl.w r4,r3 - test_move_cc 0 1 0 0 - checkr3 ffff0000 - - moveq -1,r3 - moveq 15,r4 - lsl.w r4,r3 - test_move_cc 1 0 0 0 - checkr3 ffff8000 - - move.d 0x5a67f19f,r3 - moveq 12,r4 - lsl.w r4,r3 - test_move_cc 1 0 0 0 - checkr3 5a67f000 - - move.d 0xda67f19f,r3 - move.d 31,r4 - lsl.b r4,r3 - test_move_cc 0 1 0 0 - checkr3 da67f100 - - move.d 0xda67f19f,r3 - move.d 32,r4 - lsl.b r4,r3 - test_move_cc 0 1 0 0 - checkr3 da67f100 - - move.d 0xda67f19f,r3 - move.d 33,r4 - lsl.b r4,r3 - test_move_cc 0 1 0 0 - checkr3 da67f100 - - move.d 0xda67f19f,r3 - move.d 66,r4 - lsl.b r4,r3 - test_move_cc 0 0 0 0 - checkr3 da67f17c - - move.d 0xfff3faff,r3 - moveq 0,r4 - lsl.b r4,r3 - test_move_cc 1 0 0 0 - checkr3 fff3faff - - move.d 0xfff3faff,r3 - moveq 1,r4 - lsl.b r4,r3 - test_move_cc 1 0 0 0 - checkr3 fff3fafe - - moveq 2,r3 - moveq 1,r4 - lsl.b r4,r3 - test_move_cc 0 0 0 0 - checkr3 4 - - moveq -1,r3 - moveq 31,r4 - lsl.b r4,r3 - test_move_cc 0 1 0 0 - checkr3 ffffff00 - - moveq -1,r3 - moveq 15,r4 - lsl.b r4,r3 - test_move_cc 0 1 0 0 - checkr3 ffffff00 - - moveq -1,r3 - moveq 7,r4 - lsl.b r4,r3 - test_move_cc 1 0 0 0 - checkr3 ffffff80 - - move.d 0x5a67f19f,r3 - moveq 12,r4 - lsl.b r4,r3 - test_move_cc 0 1 0 0 - checkr3 5a67f100 - - move.d 0x5a67f19f,r3 - moveq 4,r4 - lsl.b r4,r3 - test_move_cc 1 0 0 0 - checkr3 5a67f1f0 - - quit diff --git a/tests/tcg/cris/bare/check_lsr.s b/tests/tcg/cris/bare/check_lsr.s deleted file mode 100644 index 18fdbef..0000000 --- a/tests/tcg/cris/bare/check_lsr.s +++ /dev/null @@ -1,218 +0,0 @@ -# mach: crisv0 crisv3 crisv8 crisv10 crisv32 -# output: ffffffff\n1\n1\n1ffff\n5a67f\n1\n0\n0\n3699fc67\nffffffff\n1\n1\n1ffff\n5a67f\nda670000\nda670000\nda670000\nda673c67\nffffffff\nffff7fff\n1\nffff0000\nffff0001\n5a67000f\nda67f100\nda67f100\nda67f100\nda67f127\nffffffff\nffffff7f\n1\nffffff00\nffffff00\nffffff01\n5a67f100\n5a67f109\n - - .include "testutils.inc" - start - moveq -1,r3 - lsrq 0,r3 - test_move_cc 1 0 0 0 - checkr3 ffffffff - - moveq 2,r3 - lsrq 1,r3 - test_move_cc 0 0 0 0 - checkr3 1 - - moveq -1,r3 - lsrq 31,r3 - test_move_cc 0 0 0 0 - checkr3 1 - - moveq -1,r3 - lsrq 15,r3 - test_move_cc 0 0 0 0 - checkr3 1ffff - - move.d 0x5a67f19f,r3 - lsrq 12,r3 - test_move_cc 0 0 0 0 - checkr3 5a67f - - move.d 0xda67f19f,r3 - move.d 31,r4 - lsr.d r4,r3 - test_move_cc 0 0 0 0 - checkr3 1 - - move.d 0xda67f19f,r3 - move.d 32,r4 - lsr.d r4,r3 - test_move_cc 0 1 0 0 - checkr3 0 - - move.d 0xda67f19f,r3 - move.d 33,r4 - lsr.d r4,r3 - test_move_cc 0 1 0 0 - checkr3 0 - - move.d 0xda67f19f,r3 - move.d 66,r4 - lsr.d r4,r3 - test_move_cc 0 0 0 0 - checkr3 3699fc67 - - moveq -1,r3 - moveq 0,r4 - lsr.d r4,r3 - test_move_cc 1 0 0 0 - checkr3 ffffffff - - moveq 2,r3 - moveq 1,r4 - lsr.d r4,r3 - test_move_cc 0 0 0 0 - checkr3 1 - - moveq -1,r3 - moveq 31,r4 - lsr.d r4,r3 - test_move_cc 0 0 0 0 - checkr3 1 - - moveq -1,r3 - moveq 15,r4 - lsr.d r4,r3 - test_move_cc 0 0 0 0 - checkr3 1ffff - - move.d 0x5a67f19f,r3 - moveq 12,r4 - lsr.d r4,r3 - test_move_cc 0 0 0 0 - checkr3 5a67f - - move.d 0xda67f19f,r3 - move.d 31,r4 - lsr.w r4,r3 - test_move_cc 0 1 0 0 - checkr3 da670000 - - move.d 0xda67f19f,r3 - move.d 32,r4 - lsr.w r4,r3 - test_move_cc 0 1 0 0 - checkr3 da670000 - - move.d 0xda67f19f,r3 - move.d 33,r4 - lsr.w r4,r3 - test_move_cc 0 1 0 0 - checkr3 da670000 - - move.d 0xda67f19f,r3 - move.d 66,r4 - lsr.w r4,r3 - test_move_cc 0 0 0 0 - checkr3 da673c67 - - moveq -1,r3 - moveq 0,r4 - lsr.w r4,r3 - test_move_cc 1 0 0 0 - checkr3 ffffffff - - moveq -1,r3 - moveq 1,r4 - lsr.w r4,r3 - test_move_cc 0 0 0 0 - checkr3 ffff7fff - - moveq 2,r3 - moveq 1,r4 - lsr.w r4,r3 - test_move_cc 0 0 0 0 - checkr3 1 - -;; FIXME: this was wrong. Z should be set. - moveq -1,r3 - moveq 31,r4 - lsr.w r4,r3 - test_move_cc 0 1 0 0 - checkr3 ffff0000 - - moveq -1,r3 - moveq 15,r4 - lsr.w r4,r3 - test_move_cc 0 0 0 0 - checkr3 ffff0001 - - move.d 0x5a67f19f,r3 - moveq 12,r4 - lsr.w r4,r3 - test_move_cc 0 0 0 0 - checkr3 5a67000f - - move.d 0xda67f19f,r3 - move.d 31,r4 - lsr.b r4,r3 - test_move_cc 0 1 0 0 - checkr3 da67f100 - - move.d 0xda67f19f,r3 - move.d 32,r4 - lsr.b r4,r3 - test_move_cc 0 1 0 0 - checkr3 da67f100 - - move.d 0xda67f19f,r3 - move.d 33,r4 - lsr.b r4,r3 - test_move_cc 0 1 0 0 - checkr3 da67f100 - - move.d 0xda67f19f,r3 - move.d 66,r4 - lsr.b r4,r3 - test_move_cc 0 0 0 0 - checkr3 da67f127 - - moveq -1,r3 - moveq 0,r4 - lsr.b r4,r3 - test_move_cc 1 0 0 0 - checkr3 ffffffff - - moveq -1,r3 - moveq 1,r4 - lsr.b r4,r3 - test_move_cc 0 0 0 0 - checkr3 ffffff7f - - moveq 2,r3 - moveq 1,r4 - lsr.b r4,r3 - test_move_cc 0 0 0 0 - checkr3 1 - - moveq -1,r3 - moveq 31,r4 - lsr.b r4,r3 - test_move_cc 0 1 0 0 - checkr3 ffffff00 - - moveq -1,r3 - moveq 15,r4 - lsr.b r4,r3 - test_move_cc 0 1 0 0 - checkr3 ffffff00 - - moveq -1,r3 - moveq 7,r4 - lsr.b r4,r3 - test_move_cc 0 0 0 0 - checkr3 ffffff01 - - move.d 0x5a67f19f,r3 - moveq 12,r4 - lsr.b r4,r3 - test_move_cc 0 1 0 0 - checkr3 5a67f100 - - move.d 0x5a67f19f,r3 - moveq 4,r4 - lsr.b r4,r3 - test_move_cc 0 0 0 0 - checkr3 5a67f109 - - quit diff --git a/tests/tcg/cris/bare/check_mcp.s b/tests/tcg/cris/bare/check_mcp.s deleted file mode 100644 index e65ccdd..0000000 --- a/tests/tcg/cris/bare/check_mcp.s +++ /dev/null @@ -1,49 +0,0 @@ -# mach: crisv32 -# output: fffffffe\n1\n1ffff\nfffffffe\ncc463bdc\n4c463bdc\n0\n - - .include "testutils.inc" - start - -; Set R, clear C. - move 0x100,ccs - moveq -5,r3 - move 2,mof - mcp mof,r3 - test_cc 1 0 0 0 - checkr3 fffffffe - - moveq 2,r3 - move -1,srp - mcp srp,r3 - test_cc 0 0 0 0 - checkr3 1 - - move 0xffff,srp - move srp,r3 - mcp srp,r3 - test_cc 0 0 0 0 - checkr3 1ffff - - move -1,mof - move mof,r3 - mcp mof,r3 - test_cc 1 0 0 0 - checkr3 fffffffe - - move 0x5432f789,mof - move.d 0x78134452,r3 - mcp mof,r3 - test_cc 1 0 1 0 - checkr3 cc463bdc - - move 0x80000000,srp - mcp srp,r3 - test_cc 0 0 1 0 - checkr3 4c463bdc - - move 0xb3b9c423,srp - mcp srp,r3 - test_cc 0 1 0 0 - checkr3 0 - - quit diff --git a/tests/tcg/cris/bare/check_movdelsr1.s b/tests/tcg/cris/bare/check_movdelsr1.s deleted file mode 100644 index 300cc87..0000000 --- a/tests/tcg/cris/bare/check_movdelsr1.s +++ /dev/null @@ -1,33 +0,0 @@ -# mach: crisv0 crisv3 crisv8 crisv10 crisv32 -# output: aa117acd\n -# output: eeaabb42\n - -; Bug with move to special register in delay slot, due to -; special flush-insn-cache simulator use. Ordinary move worked; -; special register caused branch to fail. - - .include "testutils.inc" - start - move -1,srp - - move.d 0xaa117acd,r1 - moveq 3,r9 - cmpq 1,r9 - bhi 0f - move.d r1,r3 - - fail -0: - checkr3 aa117acd - - move.d 0xeeaabb42,r1 - moveq 3,r9 - cmpq 1,r9 - bhi 0f - move r1,srp - - fail -0: - move srp,r3 - checkr3 eeaabb42 - quit diff --git a/tests/tcg/cris/bare/check_movecr.s b/tests/tcg/cris/bare/check_movecr.s deleted file mode 100644 index da8ec26..0000000 --- a/tests/tcg/cris/bare/check_movecr.s +++ /dev/null @@ -1,37 +0,0 @@ -# mach: crisv3 crisv8 crisv10 crisv32 -# output: ffffff42\n94\nffff4321\n9234\n76543210\n76540000\n - -; Move constant byte, word, dword to register. Check that no extension is -; performed, that only part of the register is set. - - .include "testutils.inc" - startnostack - moveq -1,r3 - move.b 0x42,r3 - test_move_cc 0 0 0 0 - checkr3 ffffff42 - - moveq 0,r3 - move.b 0x94,r3 - test_move_cc 1 0 0 0 - checkr3 94 - - moveq -1,r3 - move.w 0x4321,r3 - test_move_cc 0 0 0 0 - checkr3 ffff4321 - - moveq 0,r3 - move.w 0x9234,r3 - test_move_cc 1 0 0 0 - checkr3 9234 - - move.d 0x76543210,r3 - test_move_cc 0 0 0 0 - checkr3 76543210 - - move.w 0,r3 - test_move_cc 0 1 0 0 - checkr3 76540000 - - quit diff --git a/tests/tcg/cris/bare/check_movei.s b/tests/tcg/cris/bare/check_movei.s deleted file mode 100644 index bbfa633..0000000 --- a/tests/tcg/cris/bare/check_movei.s +++ /dev/null @@ -1,50 +0,0 @@ -# mach: crisv32 -# output: fffffffe\n -# output: fffffffe\n - -; Check basic integral-write semantics regarding flags. - - .include "testutils.inc" - start - - move.d 0, $r3 -; A write that works. Check that flags are set correspondingly. - move.d d,r4 - ;; store to bring it into the tlb with the right prot bits - move.d r3,[r4] - moveq -2,r5 - setf c - clearf p - move.d [r4],r3 - ax - move.d r5,[r4] - move.d [r4],r3 - - bcc 0f - nop - fail - -0: - checkr3 fffffffe - -; A write that fails; check flags too. - move.d d,r4 - moveq 23,r5 - setf p - clearf c - move.d [r4],r3 - ax - move.d r5,[r4] - move.d [r4],r3 - - bcs 0f - nop - fail - -0: - checkr3 fffffffe - quit - - .data -d: - .dword 42424242 diff --git a/tests/tcg/cris/bare/check_movemr.s b/tests/tcg/cris/bare/check_movemr.s deleted file mode 100644 index 88489de..0000000 --- a/tests/tcg/cris/bare/check_movemr.s +++ /dev/null @@ -1,78 +0,0 @@ -# mach: crisv3 crisv8 crisv10 crisv32 -# output: 12345678\n10234567\n12345678\n12344567\n12344523\n76543210\nffffffaa\naa\n9911\nffff9911\n78\n56\n3456\n6712\n - - .include "testutils.inc" - start - - .data -mem1: - .dword 0x12345678 -mem2: - .word 0x4567 -mem3: - .byte 0x23 - .dword 0x76543210 - .byte 0xaa,0x11,0x99 - - .text - move.d mem1,r2 - move.d [r2],r3 - test_move_cc 0 0 0 0 - checkr3 12345678 - - move.d mem2,r3 - move.d [r3],r3 - test_move_cc 0 0 0 0 - checkr3 10234567 - - move.d mem1,r2 - move.d [r2+],r3 - test_move_cc 0 0 0 0 - checkr3 12345678 - - move.w [r2+],r3 - test_move_cc 0 0 0 0 - checkr3 12344567 - - move.b [r2+],r3 - test_move_cc 0 0 0 0 - checkr3 12344523 - - move.d [r2+],r3 - test_move_cc 0 0 0 0 - checkr3 76543210 - - movs.b [r2],r3 - test_move_cc 1 0 0 0 - checkr3 ffffffaa - - movu.b [r2+],r3 - test_move_cc 0 0 0 0 - checkr3 aa - - movu.w [r2],r3 - test_move_cc 0 0 0 0 - checkr3 9911 - - movs.w [r2+],r3 - test_move_cc 1 0 0 0 - checkr3 ffff9911 - - move.d mem1,r13 - movs.b [r13+],r3 - test_move_cc 0 0 0 0 - checkr3 78 - - movu.b [r13],r3 - test_move_cc 0 0 0 0 - checkr3 56 - - movs.w [r13+],r3 - test_move_cc 0 0 0 0 - checkr3 3456 - - movu.w [r13+],r3 - test_move_cc 0 0 0 0 - checkr3 6712 - - quit diff --git a/tests/tcg/cris/bare/check_movemrv32.s b/tests/tcg/cris/bare/check_movemrv32.s deleted file mode 100644 index 53950ab..0000000 --- a/tests/tcg/cris/bare/check_movemrv32.s +++ /dev/null @@ -1,96 +0,0 @@ -# mach: crisv32 -# output: 15\n7\n2\nffff1234\nb\n16\nf\n2\nffffffef\nf\nffff1234\nf\nfffffff4\nd\nfffffff2\n10\nfffffff2\nd\n - - .include "testutils.inc" - .data -x: - .dword 8,9,10,11 -y: - .dword -12,13,-14,15,16 - - start - moveq 7,r0 - moveq 2,r1 - move.d 0xffff1234,r2 - moveq 21,r3 - move.d x,r4 - setf zcvn - movem r2,[r4+] - test_cc 1 1 1 1 - subq 12,r4 - - checkr3 15 - - move.d [r4+],r3 - checkr3 7 - - move.d [r4+],r3 - checkr3 2 - - move.d [r4+],r3 - checkr3 ffff1234 - - move.d [r4+],r3 - checkr3 b - - subq 16,r4 - moveq 22,r0 - moveq 15,r1 - clearf zcvn - movem r0,[r4] - test_cc 0 0 0 0 - move.d [r4+],r3 - checkr3 16 - - move.d r1,r3 - checkr3 f - - move.d [r4+],r3 - checkr3 2 - - subq 8,r4 - moveq 10,r2 - moveq -17,r0 - clearf zc - setf vn - movem r1,[r4] - test_cc 1 0 1 0 - move.d [r4+],r3 - checkr3 ffffffef - - move.d [r4+],r3 - checkr3 f - - move.d [r4+],r3 - checkr3 ffff1234 - - move.d y,r4 - setf zc - clearf vn - movem [r4+],r3 - test_cc 0 1 0 1 - checkr3 f - - move.d r0,r3 - checkr3 fffffff4 - - move.d r1,r3 - checkr3 d - - move.d r2,r3 - checkr3 fffffff2 - - move.d [r4],r3 - checkr3 10 - - subq 8,r4 - setf zcvn - movem [r4+],r0 - test_cc 1 1 1 1 - move.d r0,r3 - checkr3 fffffff2 - - move.d r1,r3 - checkr3 d - - quit diff --git a/tests/tcg/cris/bare/check_mover.s b/tests/tcg/cris/bare/check_mover.s deleted file mode 100644 index b4db595..0000000 --- a/tests/tcg/cris/bare/check_mover.s +++ /dev/null @@ -1,28 +0,0 @@ -# mach: crisv3 crisv8 crisv10 crisv32 -# output: ffffff05\nffff0005\n5\nffffff00\n - -; Move between registers. Check that just the subreg is copied. - - .include "testutils.inc" - startnostack - moveq -30,r3 - moveq 5,r4 - move.b r4,r3 - test_move_cc 0 0 0 0 ; FIXME - checkr3 ffffff05 - - move.w r4,r3 - test_move_cc 0 0 0 0 - checkr3 ffff0005 - - move.d r4,r3 - test_move_cc 0 0 0 0 - checkr3 5 - - moveq -1,r3 - moveq 0,r4 - move.b r4,r3 - test_move_cc 0 1 0 0 - checkr3 ffffff00 - - quit diff --git a/tests/tcg/cris/bare/check_moverm.s b/tests/tcg/cris/bare/check_moverm.s deleted file mode 100644 index eabc958..0000000 --- a/tests/tcg/cris/bare/check_moverm.s +++ /dev/null @@ -1,45 +0,0 @@ -# mach: crisv3 crisv8 crisv10 crisv32 -# output: 7823fec2\n10231879\n102318fe\n - - .include "testutils.inc" - start - - .data -mem1: - .dword 0x12345678 -mem2: - .word 0x4567 -mem3: - .byte 0x23 - .dword 0x76543210 - .byte 0xaa,0x11,0x99 - - .text - move.d mem1,r2 - move.d 0x7823fec2,r4 - setf nzvc - move.d r4,[r2+] - test_cc 1 1 1 1 - subq 4,r2 - move.d [r2],r3 - checkr3 7823fec2 - - move.d mem2,r3 - move.d 0x45231879,r4 - clearf nzvc - move.w r4,[r3] - test_cc 0 0 0 0 - move.d [r3],r3 - checkr3 10231879 - - move.d mem2,r2 - moveq -2,r4 - clearf nc - setf zv - move.b r4,[r2+] - test_cc 0 1 1 0 - subq 1,r2 - move.d [r2],r3 - checkr3 102318fe - - quit diff --git a/tests/tcg/cris/bare/check_movmp.s b/tests/tcg/cris/bare/check_movmp.s deleted file mode 100644 index 7fc11f0..0000000 --- a/tests/tcg/cris/bare/check_movmp.s +++ /dev/null @@ -1,131 +0,0 @@ -# mach: crisv3 crisv8 crisv10 crisv32 -# output: ffffff00\nffff0000\n0\nffffff00\nffff0000\n0\nffffff00\nffff0000\n0\nbb113344\n664433aa\ncc557788\nabcde012\nabcde000\n77880000\n0\n - -# Test generic "move Ps,[]" and "move [],Pd" insns; the ones with -# functionality common to all models. - - .include "testutils.inc" - start - - .data -filler: - .byte 0xaa - .word 0x4433 - .dword 0x55778866 - .byte 0xcc - - .text -; Test that writing to zero-registers is a nop - .if 0 - ; We used to just ignore the writes, but now an error is emitted. We - ; keep the test-code but disabled, in case we need to change this again. - move 0xaa,p0 - move 0x4433,p4 - move 0x55774433,p8 - .endif - - moveq -1,r3 - setf zcvn - clear.b r3 - test_cc 1 1 1 1 - checkr3 ffffff00 - - moveq -1,r3 - clearf zcvn - clear.w r3 - test_cc 0 0 0 0 - checkr3 ffff0000 - - moveq -1,r3 - clear.d r3 - checkr3 0 - -; "Write" using ordinary memory references too. - .if 0 ; See ".if 0" above. - move.d filler,r6 - move [r6],p0 - move [r6],p4 - move [r6],p8 - .endif - -# ffffff00\nffff0000\n0\nffffff00\nffff0000\n0\nbb113344\n664433aa\ncc557788\nabcde012\nabcde000\n77880000\n0\n - - moveq -1,r3 - clear.b r3 - checkr3 ffffff00 - - moveq -1,r3 - clear.w r3 - checkr3 ffff0000 - - moveq -1,r3 - clear.d r3 - checkr3 0 - -; And postincremented. - .if 0 ; See ".if 0" above. - move [r6+],p0 - move [r6+],p4 - move [r6+],p8 - .endif - -# ffffff00\nffff0000\n0\nbb113344\n664433aa\ncc557788\nabcde012\nabcde000\n77880000\n0\n - - moveq -1,r3 - clear.b r3 - checkr3 ffffff00 - - moveq -1,r3 - clear.w r3 - checkr3 ffff0000 - - moveq -1,r3 - clear.d r3 - checkr3 0 - -; Now see that we can write to the registers too. -# bb113344\n664433aa\ncc557788\nabcde012\nabcde000\n77880000\n0\n -; [PC+] - move.d filler,r9 - move 0xbb113344,srp - move srp,r3 - checkr3 bb113344 - -; [R+] - move [r9+],srp - move srp,r3 - checkr3 664433aa - -; [R] - move [r9],srp - move srp,r3 - checkr3 cc557788 - -; And check writing to memory, clear and srp. - - move.d filler,r9 - move 0xabcde012,srp - setf zcvn - move srp,[r9+] - test_cc 1 1 1 1 - subq 4,r9 - move.d [r9],r3 - checkr3 abcde012 - - clearf zcvn - clear.b [r9] - test_cc 0 0 0 0 - move.d [r9],r3 - checkr3 abcde000 - - addq 2,r9 - clear.w [r9+] - subq 2,r9 - move.d [r9],r3 - checkr3 77880000 - - clear.d [r9] - move.d [r9],r3 - checkr3 0 - - quit diff --git a/tests/tcg/cris/bare/check_movpmv32.s b/tests/tcg/cris/bare/check_movpmv32.s deleted file mode 100644 index daf0970..0000000 --- a/tests/tcg/cris/bare/check_movpmv32.s +++ /dev/null @@ -1,35 +0,0 @@ -# mach: crisv32 -# output: 11223320\nbb113344\naa557711\n - -# Test v32-specific special registers. FIXME: more registers. - - .include "testutils.inc" - start - .data -store: - .dword 0x11223344 - .dword 0x77665544 - - .text - moveq -1,r3 - move.d store,r4 - move vr,[r4] - move [r4+],mof - move mof,r3 - checkr3 11223320 - - moveq -1,r3 - clearf zcvn - move 0xbb113344,mof - test_cc 0 0 0 0 - move mof,r3 - checkr3 bb113344 - - setf zcvn - move 0xaa557711,mof - test_cc 1 1 1 1 - move mof,[r4] - move.d [r4],r3 - checkr3 aa557711 - - quit diff --git a/tests/tcg/cris/bare/check_movpr.s b/tests/tcg/cris/bare/check_movpr.s deleted file mode 100644 index eef9bdb..0000000 --- a/tests/tcg/cris/bare/check_movpr.s +++ /dev/null @@ -1,28 +0,0 @@ -# mach: crisv3 crisv8 crisv10 crisv32 -# output: ffffff00\nffff0000\n0\nbb113344\n - -# Test generic "move Ps,Rd" and "move Rs,Pd" insns; the ones with -# functionality common to all models. - - .include "testutils.inc" - start - moveq -1,r3 - clear.b r3 - checkr3 ffffff00 - - moveq -1,r3 - clear.w r3 - checkr3 ffff0000 - - moveq -1,r3 - clear.d r3 - checkr3 0 - - moveq -1,r3 - move.d 0xbb113344,r4 - setf zcvn - move r4,srp - move srp,r3 - test_cc 1 1 1 1 - checkr3 bb113344 - quit diff --git a/tests/tcg/cris/bare/check_movprv32.s b/tests/tcg/cris/bare/check_movprv32.s deleted file mode 100644 index d0d90e1..0000000 --- a/tests/tcg/cris/bare/check_movprv32.s +++ /dev/null @@ -1,21 +0,0 @@ -# mach: crisv32 -# output: ffffff20\nbb113344\n - -# Test v32-specific special registers. FIXME: more registers. - - .include "testutils.inc" - start - moveq -1,r3 - setf zcvn - move vr,r3 - test_cc 1 1 1 1 - checkr3 ffffff20 - - moveq -1,r3 - move.d 0xbb113344,r4 - clearf cvnz - move r4,mof - test_cc 0 0 0 0 - move mof,r3 - checkr3 bb113344 - quit diff --git a/tests/tcg/cris/bare/check_movscr.s b/tests/tcg/cris/bare/check_movscr.s deleted file mode 100644 index 53c8ce6..0000000 --- a/tests/tcg/cris/bare/check_movscr.s +++ /dev/null @@ -1,29 +0,0 @@ -# mach: crisv3 crisv8 crisv10 crisv32 -# output: 42\nffffff85\n7685\nffff8765\n0\n - -; Move constant byte, word, dword to register. Check that sign-extension -; is performed. - - .include "testutils.inc" - start - moveq -1,r3 - movs.b 0x42,r3 - checkr3 42 - - movs.b 0x85,r3 - test_move_cc 1 0 0 0 - checkr3 ffffff85 - - movs.w 0x7685,r3 - test_move_cc 0 0 0 0 - checkr3 7685 - - movs.w 0x8765,r3 - test_move_cc 1 0 0 0 - checkr3 ffff8765 - - movs.w 0,r3 - test_move_cc 0 1 0 0 - checkr3 0 - - quit diff --git a/tests/tcg/cris/bare/check_movsm.s b/tests/tcg/cris/bare/check_movsm.s deleted file mode 100644 index 7074336..0000000 --- a/tests/tcg/cris/bare/check_movsm.s +++ /dev/null @@ -1,44 +0,0 @@ -# mach: crisv3 crisv8 crisv10 crisv32 -# output: 5\nfffffff5\n5\nfffffff5\n0\n - -; Movs between registers. Check that sign-extension is performed and the -; full register is set. - - .include "testutils.inc" - - .data -x: - .byte 5,-11 - .word 5,-11 - .word 0 - - start - move.d x,r5 - - moveq -1,r3 - movs.b [r5+],r3 - test_move_cc 0 0 0 0 - checkr3 5 - - moveq 0,r3 - movs.b [r5],r3 - test_move_cc 1 0 0 0 - addq 1,r5 - checkr3 fffffff5 - - moveq -1,r3 - movs.w [r5+],r3 - test_move_cc 0 0 0 0 - checkr3 5 - - moveq 0,r3 - movs.w [r5],r3 - test_move_cc 1 0 0 0 - addq 2,r5 - checkr3 fffffff5 - - movs.w [r5],r3 - test_move_cc 0 1 0 0 - checkr3 0 - - quit diff --git a/tests/tcg/cris/bare/check_movsr.s b/tests/tcg/cris/bare/check_movsr.s deleted file mode 100644 index d1889a7..0000000 --- a/tests/tcg/cris/bare/check_movsr.s +++ /dev/null @@ -1,46 +0,0 @@ -# mach: crisv3 crisv8 crisv10 crisv32 -# output: 5\nfffffff5\n5\nfffffff5\n0\n - -; Movs between registers. Check that sign-extension is performed and the -; full register is set. - - .include "testutils.inc" - start - moveq -1,r5 - moveq 5,r4 - move.b r4,r5 - moveq -1,r3 - movs.b r5,r3 - test_move_cc 0 0 0 0 - checkr3 5 - - moveq 0,r5 - moveq -11,r4 - move.b r4,r5 - moveq 0,r3 - movs.b r5,r3 - test_move_cc 1 0 0 0 - checkr3 fffffff5 - - moveq -1,r5 - moveq 5,r4 - move.w r4,r5 - moveq -1,r3 - movs.w r5,r3 - test_move_cc 0 0 0 0 - checkr3 5 - - moveq 0,r5 - moveq -11,r4 - move.w r4,r5 - moveq 0,r3 - movs.w r5,r3 - test_move_cc 1 0 0 0 - checkr3 fffffff5 - - moveq 0,r5 - movs.b r5,r3 - test_move_cc 0 1 0 0 - checkr3 0 - - quit diff --git a/tests/tcg/cris/bare/check_movucr.s b/tests/tcg/cris/bare/check_movucr.s deleted file mode 100644 index 7c8487d..0000000 --- a/tests/tcg/cris/bare/check_movucr.s +++ /dev/null @@ -1,33 +0,0 @@ -# mach: crisv3 crisv8 crisv10 crisv32 -# output: 42\n85\n7685\n8765\n0\n - -; Move constant byte, word, dword to register. Check that zero-extension -; is performed. - - .include "testutils.inc" - start - moveq -1,r3 - movu.b 0x42,r3 - test_move_cc 0 0 0 0 - checkr3 42 - - moveq -1,r3 - movu.b 0x85,r3 - test_move_cc 0 0 0 0 - checkr3 85 - - moveq -1,r3 - movu.w 0x7685,r3 - test_move_cc 0 0 0 0 - checkr3 7685 - - moveq -1,r3 - movu.w 0x8765,r3 - test_move_cc 0 0 0 0 - checkr3 8765 - - movu.b 0,r3 - test_move_cc 0 1 0 0 - checkr3 0 - - quit diff --git a/tests/tcg/cris/bare/check_movum.s b/tests/tcg/cris/bare/check_movum.s deleted file mode 100644 index 038e539..0000000 --- a/tests/tcg/cris/bare/check_movum.s +++ /dev/null @@ -1,40 +0,0 @@ -# mach: crisv3 crisv8 crisv10 crisv32 -# output: 5\nf5\n5\nfff5\n0\n - -; Movu between registers. Check that zero-extension is performed and the -; full register is set. - - .include "testutils.inc" - - .data -x: - .byte 5,-11 - .word 5,-11 - .word 0 - - start - move.d x,r5 - - movu.b [r5+],r3 - test_move_cc 0 0 0 0 - checkr3 5 - - movu.b [r5],r3 - test_move_cc 0 0 0 0 - addq 1,r5 - checkr3 f5 - - movu.w [r5+],r3 - test_move_cc 0 0 0 0 - checkr3 5 - - movu.w [r5],r3 - test_move_cc 0 0 0 0 - addq 2,r5 - checkr3 fff5 - - movu.w [r5],r3 - test_move_cc 0 1 0 0 - checkr3 0 - - quit diff --git a/tests/tcg/cris/bare/check_movur.s b/tests/tcg/cris/bare/check_movur.s deleted file mode 100644 index 3ecf475..0000000 --- a/tests/tcg/cris/bare/check_movur.s +++ /dev/null @@ -1,45 +0,0 @@ -# mach: crisv3 crisv8 crisv10 crisv32 -# output: 5\nf5\n5\nfff5\n0\n - -; Movu between registers. Check that zero-extension is performed and the -; full register is set. - - .include "testutils.inc" - start - moveq -1,r5 - moveq 5,r4 - move.b r4,r5 - moveq -1,r3 - movu.b r5,r3 - test_move_cc 0 0 0 0 - checkr3 5 - - moveq 0,r5 - moveq -11,r4 - move.b r4,r5 - moveq -1,r3 - movu.b r5,r3 - test_move_cc 0 0 0 0 - checkr3 f5 - - moveq -1,r5 - moveq 5,r4 - move.w r4,r5 - moveq -1,r3 - movu.w r5,r3 - test_move_cc 0 0 0 0 - checkr3 5 - - moveq 0,r5 - moveq -11,r4 - move.w r4,r5 - moveq -1,r3 - movu.w r5,r3 - test_move_cc 0 0 0 0 - checkr3 fff5 - - movu.w 0,r3 - test_move_cc 0 1 0 0 - checkr3 0 - - quit diff --git a/tests/tcg/cris/bare/check_mulv32.s b/tests/tcg/cris/bare/check_mulv32.s deleted file mode 100644 index f379358..0000000 --- a/tests/tcg/cris/bare/check_mulv32.s +++ /dev/null @@ -1,51 +0,0 @@ -# mach: crisv32 -# output: fffffffe\n -# output: ffffffff\n -# output: fffffffe\n -# output: 1\n -# output: fffffffe\n -# output: ffffffff\n -# output: fffffffe\n -# output: 1\n - -; Check that carry is not modified on v32. - - .include "testutils.inc" - start - moveq -1,r3 - moveq 2,r4 - setf c - muls.d r4,r3 - test_cc 1 0 0 1 - checkr3 fffffffe - move mof,r3 - checkr3 ffffffff - - moveq -1,r3 - moveq 2,r4 - setf c - mulu.d r4,r3 - test_cc 0 0 1 1 - checkr3 fffffffe - move mof,r3 - checkr3 1 - - moveq -1,r3 - moveq 2,r4 - clearf c - muls.d r4,r3 - test_cc 1 0 0 0 - checkr3 fffffffe - move mof,r3 - checkr3 ffffffff - - moveq -1,r3 - moveq 2,r4 - clearf c - mulu.d r4,r3 - test_cc 0 0 1 0 - checkr3 fffffffe - move mof,r3 - checkr3 1 - - quit diff --git a/tests/tcg/cris/bare/check_mulx.s b/tests/tcg/cris/bare/check_mulx.s deleted file mode 100644 index a7a1f82..0000000 --- a/tests/tcg/cris/bare/check_mulx.s +++ /dev/null @@ -1,257 +0,0 @@ -# mach: crisv10 crisv32 -# output: fffffffe\nffffffff\nfffffffe\n1\nfffffffe\nffffffff\nfffffffe\n1\nfffe0001\n0\nfffe0001\n0\n1\n0\n1\nfffffffe\n193eade2\n277e3a49\n193eade2\n277e3a49\nfffffffe\nffffffff\n1fffe\n0\nfffffffe\nffffffff\n1fffe\n0\n1\n0\nfffe0001\n0\nfdbdade2\nffffffff\n420fade2\n0\nfffffffe\nffffffff\n1fe\n0\nfffffffe\nffffffff\n1fe\n0\n1\n0\nfe01\n0\n1\n0\nfe01\n0\nffffd9e2\nffffffff\n2be2\n0\n0\n0\n0\n0\n - - .include "testutils.inc" - start - - .align 4 - moveq -1,r3 - moveq 2,r4 - muls.d r4,r3 - test_cc 1 0 0 0 - checkr3 fffffffe - move mof,r3 - checkr3 ffffffff - - .align 4 - moveq -1,r3 - moveq 2,r4 - mulu.d r4,r3 - test_cc 0 0 1 0 - checkr3 fffffffe - move mof,r3 - checkr3 1 - - .align 4 - moveq 2,r3 - moveq -1,r4 - muls.d r4,r3 - test_cc 1 0 0 0 - checkr3 fffffffe - move mof,r3 - checkr3 ffffffff - - .align 4 - moveq 2,r3 - moveq -1,r4 - mulu.d r4,r3 - test_cc 0 0 1 0 - checkr3 fffffffe - move mof,r3 - checkr3 1 - - move.d 0xffff,r4 - move.d r4,r3 - muls.d r4,r3 - test_cc 0 0 1 0 - checkr3 fffe0001 - move mof,r3 - checkr3 0 - - move.d 0xffff,r4 - move.d r4,r3 - mulu.d r4,r3 - test_cc 0 0 0 0 - checkr3 fffe0001 - move mof,r3 - checkr3 0 - - moveq -1,r4 - move.d r4,r3 - muls.d r4,r3 - test_cc 0 0 0 0 - checkr3 1 - move mof,r3 - checkr3 0 - - moveq -1,r4 - move.d r4,r3 - mulu.d r4,r3 - test_cc 1 0 1 0 - checkr3 1 - move mof,r3 - checkr3 fffffffe - - move.d 0x5432f789,r4 - move.d 0x78134452,r3 - muls.d r4,r3 - test_cc 0 0 1 0 - checkr3 193eade2 - move mof,r3 - checkr3 277e3a49 - - move.d 0x5432f789,r4 - move.d 0x78134452,r3 - mulu.d r4,r3 - test_cc 0 0 1 0 - checkr3 193eade2 - move mof,r3 - checkr3 277e3a49 - - move.d 0xffff,r3 - moveq 2,r4 - muls.w r4,r3 - test_cc 1 0 0 0 - checkr3 fffffffe - move mof,r3 - checkr3 ffffffff - - moveq -1,r3 - moveq 2,r4 - mulu.w r4,r3 - test_cc 0 0 0 0 - checkr3 1fffe - move mof,r3 - checkr3 0 - nop - - moveq 2,r3 - move.d 0xffff,r4 - muls.w r4,r3 - test_cc 1 0 0 0 - checkr3 fffffffe - move mof,r3 - checkr3 ffffffff - - moveq 2,r3 - moveq -1,r4 - mulu.w r4,r3 - test_cc 0 0 0 0 - checkr3 1fffe - move mof,r3 - checkr3 0 - - move.d 0xffff,r4 - move.d r4,r3 - muls.w r4,r3 - test_cc 0 0 0 0 - checkr3 1 - move mof,r3 - checkr3 0 - - moveq -1,r4 - move.d r4,r3 - mulu.w r4,r3 - test_cc 0 0 0 0 - checkr3 fffe0001 - move mof,r3 - checkr3 0 - - move.d 0x5432f789,r4 - move.d 0x78134452,r3 - muls.w r4,r3 - test_cc 1 0 0 0 - checkr3 fdbdade2 - move mof,r3 - checkr3 ffffffff - nop - - move.d 0x5432f789,r4 - move.d 0x78134452,r3 - mulu.w r4,r3 - test_cc 0 0 0 0 - checkr3 420fade2 - move mof,r3 - checkr3 0 - nop - - move.d 0xff,r3 - moveq 2,r4 - muls.b r4,r3 - test_cc 1 0 0 0 - checkr3 fffffffe - move mof,r3 - checkr3 ffffffff - - moveq -1,r3 - moveq 2,r4 - mulu.b r4,r3 - test_cc 0 0 0 0 - checkr3 1fe - move mof,r3 - checkr3 0 - - moveq 2,r3 - moveq -1,r4 - muls.b r4,r3 - test_cc 1 0 0 0 - checkr3 fffffffe - move mof,r3 - checkr3 ffffffff - - moveq 2,r3 - moveq -1,r4 - mulu.b r4,r3 - test_cc 0 0 0 0 - checkr3 1fe - move mof,r3 - checkr3 0 - - move.d 0xff,r4 - move.d r4,r3 - muls.b r4,r3 - test_cc 0 0 0 0 - checkr3 1 - move mof,r3 - checkr3 0 - nop - - moveq -1,r4 - move.d r4,r3 - mulu.b r4,r3 - test_cc 0 0 0 0 - checkr3 fe01 - move mof,r3 - checkr3 0 - nop - - move.d 0xfeda49ff,r4 - move.d r4,r3 - muls.b r4,r3 - test_cc 0 0 0 0 - checkr3 1 - move mof,r3 - checkr3 0 - nop - - move.d 0xfeda49ff,r4 - move.d r4,r3 - mulu.b r4,r3 - test_cc 0 0 0 0 - checkr3 fe01 - move mof,r3 - checkr3 0 - - move.d 0x5432f789,r4 - move.d 0x78134452,r3 - muls.b r4,r3 - test_cc 1 0 0 0 - checkr3 ffffd9e2 - move mof,r3 - checkr3 ffffffff - - move.d 0x5432f789,r4 - move.d 0x78134452,r3 - mulu.b r4,r3 - test_cc 0 0 0 0 - checkr3 2be2 - move mof,r3 - checkr3 0 - - moveq 0,r3 - move.d 0xf87f4aeb,r4 - muls.d r4,r3 - test_cc 0 1 0 0 - checkr3 0 - move mof,r3 - checkr3 0 - - move.d 0xf87f4aeb,r3 - moveq 0,r4 - mulu.d r4,r3 - test_cc 0 1 0 0 - checkr3 0 - move mof,r3 - checkr3 0 - - quit diff --git a/tests/tcg/cris/bare/check_neg.s b/tests/tcg/cris/bare/check_neg.s deleted file mode 100644 index 963c4b6..0000000 --- a/tests/tcg/cris/bare/check_neg.s +++ /dev/null @@ -1,104 +0,0 @@ -# mach: crisv0 crisv3 crisv8 crisv10 crisv32 -# output: ffffffff\nffffffff\n0\n80000000\n1\nba987655\nffff\nffff\n0\n89ab8000\nffff0001\n45677655\nff\nff\n0\n89abae80\nffffff01\n45678955\n - - .include "testutils.inc" - start - moveq 0,r3 - moveq 1,r4 - neg.d r4,r3 - test_move_cc 1 0 0 0 - checkr3 ffffffff - - moveq 1,r3 - moveq 0,r4 - neg.d r3,r3 - test_move_cc 1 0 0 0 - checkr3 ffffffff - -;; FIXME: this was wrong. - moveq 0,r3 - neg.d r3,r3 - test_move_cc 0 1 0 0 - checkr3 0 - - move.d 0x80000000,r3 - neg.d r3,r3 - test_move_cc 1 0 0 0 - checkr3 80000000 - - moveq -1,r3 - neg.d r3,r3 - test_move_cc 0 0 0 0 - checkr3 1 - - move.d 0x456789ab,r3 - neg.d r3,r3 - test_move_cc 1 0 0 0 - checkr3 ba987655 - - moveq 0,r3 - moveq 1,r4 - neg.w r4,r3 - test_move_cc 1 0 0 0 - checkr3 ffff - - moveq 1,r3 - moveq 0,r4 - neg.w r3,r3 - test_move_cc 1 0 0 0 - checkr3 ffff - - moveq 0,r3 - neg.w r3,r3 - test_move_cc 0 1 0 0 - checkr3 0 - - move.d 0x89ab8000,r3 - neg.w r3,r3 - test_move_cc 1 0 0 0 - checkr3 89ab8000 - - moveq -1,r3 - neg.w r3,r3 - test_move_cc 0 0 0 0 - checkr3 ffff0001 - - move.d 0x456789ab,r3 - neg.w r3,r3 - test_move_cc 0 0 0 0 - checkr3 45677655 - - moveq 0,r3 - moveq 1,r4 - neg.b r4,r3 - test_move_cc 1 0 0 0 - checkr3 ff - - moveq 1,r3 - moveq 0,r4 - neg.b r3,r3 - test_move_cc 1 0 0 0 - checkr3 ff - - moveq 0,r3 - neg.b r3,r3 - test_move_cc 0 1 0 0 - checkr3 0 - -;; FIXME: was wrong. - move.d 0x89abae80,r3 - neg.b r3,r3 - test_move_cc 1 0 0 1 - checkr3 89abae80 - - moveq -1,r3 - neg.b r3,r3 - test_move_cc 0 0 0 0 - checkr3 ffffff01 - - move.d 0x456789ab,r3 - neg.b r3,r3 - test_move_cc 0 0 0 0 - checkr3 45678955 - - quit diff --git a/tests/tcg/cris/bare/check_not.s b/tests/tcg/cris/bare/check_not.s deleted file mode 100644 index 33bcf15..0000000 --- a/tests/tcg/cris/bare/check_not.s +++ /dev/null @@ -1,31 +0,0 @@ -# mach: crisv0 crisv3 crisv8 crisv10 crisv32 -# output: fffffffe\nfffffffd\nffff0f00\n0\n87ecbbad\n - - .include "testutils.inc" - start - moveq 1,r3 - not r3 - test_move_cc 1 0 0 0 - checkr3 fffffffe - - moveq 2,r3 - not r3 - test_move_cc 1 0 0 0 - checkr3 fffffffd - - move.d 0xf0ff,r3 - not r3 - test_move_cc 1 0 0 0 - checkr3 ffff0f00 - - moveq -1,r3 - not r3 - test_move_cc 0 1 0 0 - checkr3 0 - - move.d 0x78134452,r3 - not r3 - test_move_cc 1 0 0 0 - checkr3 87ecbbad - - quit diff --git a/tests/tcg/cris/bare/check_orc.s b/tests/tcg/cris/bare/check_orc.s deleted file mode 100644 index c733f03..0000000 --- a/tests/tcg/cris/bare/check_orc.s +++ /dev/null @@ -1,71 +0,0 @@ -# mach: crisv0 crisv3 crisv8 crisv10 crisv32 -# output: 3\n3\nffff\nffffffff\n7c33f7db\nffff0003\n3\nfedaffff\n7813f7db\n3\n3\nfeb\n781344db\n - - .include "testutils.inc" - start - moveq 1,r3 - or.d 2,r3 - test_move_cc 0 0 0 0 - checkr3 3 - - moveq 2,r3 - or.d 1,r3 - test_move_cc 0 0 0 0 - checkr3 3 - - move.d 0xf0ff,r3 - or.d 0xff0f,r3 - test_move_cc 0 0 0 0 - checkr3 ffff - - moveq -1,r3 - or.d -1,r3 - test_move_cc 1 0 0 0 - checkr3 ffffffff - - move.d 0x78134452,r3 - or.d 0x5432f789,r3 - test_move_cc 0 0 0 0 - checkr3 7c33f7db - - move.d 0xffff0001,r3 - or.w 2,r3 - test_move_cc 0 0 0 0 - checkr3 ffff0003 - - moveq 2,r3 - or.w 1,r3 - test_move_cc 0 0 0 0 - checkr3 3 - - move.d 0xfedaffaf,r3 - or.w 0xff5f,r3 - test_move_cc 1 0 0 0 - checkr3 fedaffff - - move.d 0x78134452,r3 - or.w 0xf789,r3 - test_move_cc 1 0 0 0 - checkr3 7813f7db - - moveq 1,r3 - or.b 2,r3 - test_move_cc 0 0 0 0 - checkr3 3 - - moveq 2,r3 - or.b 1,r3 - test_move_cc 0 0 0 0 - checkr3 3 - - move.d 0xfa3,r3 - or.b 0x4a,r3 - test_move_cc 1 0 0 0 - checkr3 feb - - move.d 0x78134453,r3 - or.b 0x89,r3 - test_move_cc 1 0 0 0 - checkr3 781344db - - quit diff --git a/tests/tcg/cris/bare/check_orm.s b/tests/tcg/cris/bare/check_orm.s deleted file mode 100644 index ee723a6..0000000 --- a/tests/tcg/cris/bare/check_orm.s +++ /dev/null @@ -1,75 +0,0 @@ -# mach: crisv0 crisv3 crisv8 crisv10 crisv32 -# output: 3\n3\nffff\nffffffff\n7c33f7db\nffff0003\n3\nfedaffff\n7813f7db\n3\n3\nfeb\n781344db\n - - .include "testutils.inc" - .data -x: - .dword 2,1,0xff0f,-1,0x5432f789 - .word 2,1,0xff5f,0xf789 - .byte 2,1,0x4a,0x89 - - start - moveq 1,r3 - move.d x,r5 - or.d [r5+],r3 - checkr3 3 - - moveq 2,r3 - or.d [r5],r3 - addq 4,r5 - checkr3 3 - - move.d 0xf0ff,r3 - or.d [r5+],r3 - checkr3 ffff - - moveq -1,r3 - or.d [r5+],r3 - checkr3 ffffffff - - move.d 0x78134452,r3 - or.d [r5+],r3 - checkr3 7c33f7db - - move.d 0xffff0001,r3 - or.w [r5+],r3 - checkr3 ffff0003 - - moveq 2,r3 - or.w [r5],r3 - addq 2,r5 - test_move_cc 0 0 0 0 - checkr3 3 - - move.d 0xfedaffaf,r3 - or.w [r5+],r3 - test_move_cc 1 0 0 0 - checkr3 fedaffff - - move.d 0x78134452,r3 - or.w [r5+],r3 - test_move_cc 1 0 0 0 - checkr3 7813f7db - - moveq 1,r3 - or.b [r5+],r3 - test_move_cc 0 0 0 0 - checkr3 3 - - moveq 2,r3 - or.b [r5],r3 - addq 1,r5 - test_move_cc 0 0 0 0 - checkr3 3 - - move.d 0xfa3,r3 - or.b [r5+],r3 - test_move_cc 1 0 0 0 - checkr3 feb - - move.d 0x78134453,r3 - or.b [r5],r3 - test_move_cc 1 0 0 0 - checkr3 781344db - - quit diff --git a/tests/tcg/cris/bare/check_orq.s b/tests/tcg/cris/bare/check_orq.s deleted file mode 100644 index 5060edc..0000000 --- a/tests/tcg/cris/bare/check_orq.s +++ /dev/null @@ -1,41 +0,0 @@ -# mach: crisv0 crisv3 crisv8 crisv10 crisv32 -# output: 3\n3\nffffffff\nffffffff\n1f\nffffffe0\n7813445e\n - - .include "testutils.inc" - start - moveq 1,r3 - orq 2,r3 - test_move_cc 0 0 0 0 - checkr3 3 - - moveq 2,r3 - orq 1,r3 - test_move_cc 0 0 0 0 - checkr3 3 - - move.d 0xf0ff,r3 - orq -1,r3 - test_move_cc 1 0 0 0 - checkr3 ffffffff - - moveq 0,r3 - orq -1,r3 - test_move_cc 1 0 0 0 - checkr3 ffffffff - - moveq 0,r3 - orq 31,r3 - test_move_cc 0 0 0 0 - checkr3 1f - - moveq 0,r3 - orq -32,r3 - test_move_cc 1 0 0 0 - checkr3 ffffffe0 - - move.d 0x78134452,r3 - orq 12,r3 - test_move_cc 0 0 0 0 - checkr3 7813445e - - quit diff --git a/tests/tcg/cris/bare/check_orr.s b/tests/tcg/cris/bare/check_orr.s deleted file mode 100644 index a514c11..0000000 --- a/tests/tcg/cris/bare/check_orr.s +++ /dev/null @@ -1,84 +0,0 @@ -# mach: crisv0 crisv3 crisv8 crisv10 crisv32 -# output: 3\n3\nffff\nffffffff\n7c33f7db\nffff0003\n3\nfedaffff\n7813f7db\n3\n3\nfeb\n781344db\n - - .include "testutils.inc" - start - moveq 1,r3 - moveq 2,r4 - or.d r4,r3 - test_move_cc 0 0 0 0 - checkr3 3 - - moveq 2,r3 - moveq 1,r4 - or.d r4,r3 - test_move_cc 0 0 0 0 - checkr3 3 - - move.d 0xff0f,r4 - move.d 0xf0ff,r3 - or.d r4,r3 - test_move_cc 0 0 0 0 - checkr3 ffff - - moveq -1,r4 - move.d r4,r3 - or.d r4,r3 - test_move_cc 1 0 0 0 - checkr3 ffffffff - - move.d 0x5432f789,r4 - move.d 0x78134452,r3 - or.d r4,r3 - test_move_cc 0 0 0 0 - checkr3 7c33f7db - - move.d 0xffff0001,r3 - moveq 2,r4 - or.w r4,r3 - test_move_cc 0 0 0 0 - checkr3 ffff0003 - - moveq 2,r3 - move.d 0xffff0001,r4 - or.w r4,r3 - test_move_cc 0 0 0 0 - checkr3 3 - - move.d 0xfedaffaf,r3 - move.d 0xffffff5f,r4 - or.w r4,r3 - test_move_cc 1 0 0 0 - checkr3 fedaffff - - move.d 0x5432f789,r4 - move.d 0x78134452,r3 - or.w r4,r3 - test_move_cc 1 0 0 0 - checkr3 7813f7db - - moveq 1,r3 - move.d 0xffffff02,r4 - or.b r4,r3 - test_move_cc 0 0 0 0 - checkr3 3 - - moveq 2,r3 - moveq 1,r4 - or.b r4,r3 - test_move_cc 0 0 0 0 - checkr3 3 - - move.d 0x4a,r4 - move.d 0xfa3,r3 - or.b r4,r3 - test_move_cc 1 0 0 0 - checkr3 feb - - move.d 0x5432f789,r4 - move.d 0x78134453,r3 - or.b r4,r3 - test_move_cc 1 0 0 0 - checkr3 781344db - - quit diff --git a/tests/tcg/cris/bare/check_ret.s b/tests/tcg/cris/bare/check_ret.s deleted file mode 100644 index b44fb25..0000000 --- a/tests/tcg/cris/bare/check_ret.s +++ /dev/null @@ -1,25 +0,0 @@ -# mach: crisv3 crisv8 crisv10 -# output: 3\n - -# Test that ret works. - - .include "testutils.inc" - start -x: - moveq 0,r3 - jsr z -w: - quit -y: - addq 1,r3 - checkr3 3 - quit - -z: - addq 1,r3 - move srp,r2 - add.d y-w,r2 - move r2,srp - ret - addq 1,r3 - quit diff --git a/tests/tcg/cris/bare/check_scc.s b/tests/tcg/cris/bare/check_scc.s deleted file mode 100644 index 4a8674c..0000000 --- a/tests/tcg/cris/bare/check_scc.s +++ /dev/null @@ -1,95 +0,0 @@ -# mach: crisv0 crisv3 crisv8 crisv10 crisv32 -# output: 1\n0\n1\n0\n1\n0\n1\n0\n0\n1\n1\n0\n1\n0\n1\n0\n1\n0\n0\n1\n0\n1\n1\n0\n1\n0\n0\n1\n1\n0\n1\n1\n0\n - - .include "testutils.inc" - - .macro lcheckr3 v - move $ccs, $r9 - checkr3 \v - move $r9, $ccs - .endm - - start - clearf nzvc - scc r3 - lcheckr3 1 - scs r3 - lcheckr3 0 - sne r3 - lcheckr3 1 - seq r3 - lcheckr3 0 - svc r3 - lcheckr3 1 - svs r3 - lcheckr3 0 - spl r3 - lcheckr3 1 - smi r3 - lcheckr3 0 - sls r3 - lcheckr3 0 - shi r3 - lcheckr3 1 - sge r3 - lcheckr3 1 - slt r3 - lcheckr3 0 - sgt r3 - lcheckr3 1 - sle r3 - lcheckr3 0 - sa r3 - lcheckr3 1 - setf nzvc - scc r3 - lcheckr3 0 - scs r3 - lcheckr3 1 - sne r3 - lcheckr3 0 - svc r3 - lcheckr3 0 - svs r3 - lcheckr3 1 - spl r3 - lcheckr3 0 - smi r3 - lcheckr3 1 - sls r3 - lcheckr3 1 - shi r3 - lcheckr3 0 - sge r3 - lcheckr3 1 - slt r3 - lcheckr3 0 - sgt r3 - lcheckr3 0 - sle r3 - lcheckr3 1 - sa r3 - lcheckr3 1 - clearf n - sge r3 - lcheckr3 0 - slt r3 - lcheckr3 1 - - .if 1 ;..asm.arch.cris.v32 - setf p - ssb r3 - .else - moveq 1,r3 - .endif - lcheckr3 1 - - .if 1 ;..asm.arch.cris.v32 - clearf p - ssb r3 - .else - moveq 0,r3 - .endif - lcheckr3 0 - - quit diff --git a/tests/tcg/cris/bare/check_subc.s b/tests/tcg/cris/bare/check_subc.s deleted file mode 100644 index e34b544..0000000 --- a/tests/tcg/cris/bare/check_subc.s +++ /dev/null @@ -1,87 +0,0 @@ -# mach: crisv0 crisv3 crisv8 crisv10 crisv32 -# output: 1\n1\n1fffe\nfffffffe\ncc463bdb\nffff0001\n1\nfffe\nfedafffe\n78133bdb\nffffff01\n1\nfe\nfeda49fe\n781344db\n85649200\n - - .include "testutils.inc" - start - - moveq -1,r3 - sub.d -2,r3 - test_cc 0 0 0 0 - checkr3 1 - - moveq 2,r3 - sub.d 1,r3 - test_cc 0 0 0 0 - checkr3 1 - - move.d 0xffff,r3 - sub.d -0xffff,r3 - test_cc 0 0 0 1 - checkr3 1fffe - - moveq -1,r3 - sub.d 1,r3 - test_cc 1 0 0 0 - checkr3 fffffffe - - move.d 0x78134452,r3 - sub.d -0x5432f789,r3 - test_cc 1 0 1 1 - checkr3 cc463bdb - - moveq -1,r3 - sub.w -2,r3 - test_cc 0 0 0 0 - checkr3 ffff0001 - - moveq 2,r3 - sub.w 1,r3 - test_cc 0 0 0 0 - checkr3 1 - - move.d 0xffff,r3 - sub.w 1,r3 - test_cc 1 0 0 0 - checkr3 fffe - - move.d 0xfedaffff,r3 - sub.w 1,r3 - test_cc 1 0 0 0 - checkr3 fedafffe - - move.d 0x78134452,r3 - sub.w 0x877,r3 - test_cc 0 0 0 0 - checkr3 78133bdb - - moveq -1,r3 - sub.b -2,r3 - test_cc 0 0 0 0 - checkr3 ffffff01 - - moveq 2,r3 - sub.b 1,r3 - test_cc 0 0 0 0 - checkr3 1 - - move.d 0xff,r3 - sub.b 1,r3 - test_cc 1 0 0 0 - checkr3 fe - - move.d 0xfeda49ff,r3 - sub.b 1,r3 - test_cc 1 0 0 0 - checkr3 feda49fe - - move.d 0x78134452,r3 - sub.b 0x77,r3 - test_cc 1 0 0 1 - checkr3 781344db - - move.d 0x85649282,r3 - sub.b 0x82,r3 - test_cc 0 1 0 0 - checkr3 85649200 - - quit diff --git a/tests/tcg/cris/bare/check_subm.s b/tests/tcg/cris/bare/check_subm.s deleted file mode 100644 index e07ea02..0000000 --- a/tests/tcg/cris/bare/check_subm.s +++ /dev/null @@ -1,96 +0,0 @@ -# mach: crisv0 crisv3 crisv8 crisv10 crisv32 -# output: 1\n1\n1fffe\nfffffffe\ncc463bdb\nffff0001\n1\nfffe\nfedafffe\n78133bdb\nffffff01\n1\nfe\nfeda49fe\n781344db\n85649200\n - - .include "testutils.inc" - .data -x: - .dword -2,1,-0xffff,1,-0x5432f789 - .word -2,1,1,0x877 - .byte -2,1,0x77 - .byte 0x22 - - start - moveq -1,r3 - move.d x,r5 - sub.d [r5+],r3 - test_cc 0 0 0 0 - checkr3 1 - - moveq 2,r3 - sub.d [r5],r3 - test_cc 0 0 0 0 - addq 4,r5 - checkr3 1 - - move.d 0xffff,r3 - sub.d [r5+],r3 - test_cc 0 0 0 1 - checkr3 1fffe - - moveq -1,r3 - sub.d [r5+],r3 - test_cc 1 0 0 0 - checkr3 fffffffe - - move.d 0x78134452,r3 - sub.d [r5+],r3 - test_cc 1 0 1 1 - checkr3 cc463bdb - - moveq -1,r3 - sub.w [r5+],r3 - test_cc 0 0 0 0 - checkr3 ffff0001 - - moveq 2,r3 - sub.w [r5+],r3 - test_cc 0 0 0 0 - checkr3 1 - - move.d 0xffff,r3 - sub.w [r5],r3 - test_cc 1 0 0 0 - checkr3 fffe - - move.d 0xfedaffff,r3 - sub.w [r5+],r3 - test_cc 1 0 0 0 - checkr3 fedafffe - - move.d 0x78134452,r3 - sub.w [r5+],r3 - test_cc 0 0 0 0 - checkr3 78133bdb - - moveq -1,r3 - sub.b [r5],r3 - test_cc 0 0 0 0 - addq 1,r5 - checkr3 ffffff01 - - moveq 2,r3 - sub.b [r5],r3 - test_cc 0 0 0 0 - checkr3 1 - - move.d 0xff,r3 - sub.b [r5],r3 - test_cc 1 0 0 0 - checkr3 fe - - move.d 0xfeda49ff,r3 - sub.b [r5+],r3 - test_cc 1 0 0 0 - checkr3 feda49fe - - move.d 0x78134452,r3 - sub.b [r5+],r3 - test_cc 1 0 0 1 - checkr3 781344db - - move.d 0x85649222,r3 - sub.b [r5],r3 - test_cc 0 1 0 0 - checkr3 85649200 - - quit diff --git a/tests/tcg/cris/bare/check_subq.s b/tests/tcg/cris/bare/check_subq.s deleted file mode 100644 index 9e34fa3..0000000 --- a/tests/tcg/cris/bare/check_subq.s +++ /dev/null @@ -1,52 +0,0 @@ -# mach: crisv3 crisv8 crisv10 crisv32 -# output: 0\nffffffff\nfffffffe\nffff\nff\n56788f9\n56788d9\n567889a\n0\n7ffffffc\n - - .include "testutils.inc" - start - moveq 1,r3 - subq 1,r3 - test_cc 0 1 0 0 - checkr3 0 - - subq 1,r3 - test_cc 1 0 0 1 - checkr3 ffffffff - - subq 1,r3 - test_cc 1 0 0 0 - checkr3 fffffffe - - move.d 0x10000,r3 - subq 1,r3 - test_cc 0 0 0 0 - checkr3 ffff - - move.d 0x100,r3 - subq 1,r3 - test_cc 0 0 0 0 - checkr3 ff - - move.d 0x5678900,r3 - subq 7,r3 - test_cc 0 0 0 0 - checkr3 56788f9 - - subq 32,r3 - test_cc 0 0 0 0 - checkr3 56788d9 - - subq 63,r3 - test_cc 0 0 0 0 - checkr3 567889a - - move.d 34,r3 - subq 34,r3 - test_cc 0 1 0 0 - checkr3 0 - - move.d 0x80000024,r3 - subq 40,r3 - test_cc 0 0 1 0 - checkr3 7ffffffc - - quit diff --git a/tests/tcg/cris/bare/check_subr.s b/tests/tcg/cris/bare/check_subr.s deleted file mode 100644 index 742fbc8..0000000 --- a/tests/tcg/cris/bare/check_subr.s +++ /dev/null @@ -1,102 +0,0 @@ -# mach: crisv0 crisv3 crisv8 crisv10 crisv32 -# output: 1\n1\n1fffe\nfffffffe\ncc463bdb\nffff0001\n1\nfffe\nfedafffe\n78133bdb\nffffff01\n1\nfe\nfeda49fe\n781344db\n85649200\n - - .include "testutils.inc" - start - moveq -1,r3 - moveq -2,r4 - sub.d r4,r3 - test_cc 0 0 0 0 - checkr3 1 - - moveq 2,r3 - moveq 1,r4 - sub.d r4,r3 - test_cc 0 0 0 0 - checkr3 1 - - move.d 0xffff,r3 - move.d -0xffff,r4 - sub.d r4,r3 - test_cc 0 0 0 1 - checkr3 1fffe - - moveq 1,r4 - moveq -1,r3 - sub.d r4,r3 - test_cc 1 0 0 0 - checkr3 fffffffe - - move.d -0x5432f789,r4 - move.d 0x78134452,r3 - sub.d r4,r3 - test_cc 1 0 1 1 - checkr3 cc463bdb - - moveq -1,r3 - moveq -2,r4 - sub.w r4,r3 - test_cc 0 0 0 0 - checkr3 ffff0001 - - moveq 2,r3 - moveq 1,r4 - sub.w r4,r3 - test_cc 0 0 0 0 - checkr3 1 - - move.d 0xffff,r3 - move.d -0xffff,r4 - sub.w r4,r3 - test_cc 1 0 0 0 - checkr3 fffe - - move.d 0xfedaffff,r3 - move.d -0xfedaffff,r4 - sub.w r4,r3 - test_cc 1 0 0 0 - checkr3 fedafffe - - move.d -0x5432f789,r4 - move.d 0x78134452,r3 - sub.w r4,r3 - test_cc 0 0 0 0 - checkr3 78133bdb - - moveq -1,r3 - moveq -2,r4 - sub.b r4,r3 - test_cc 0 0 0 0 - checkr3 ffffff01 - - moveq 2,r3 - moveq 1,r4 - sub.b r4,r3 - test_cc 0 0 0 0 - checkr3 1 - - move.d -0xff,r4 - move.d 0xff,r3 - sub.b r4,r3 - test_cc 1 0 0 0 - checkr3 fe - - move.d -0xfeda49ff,r4 - move.d 0xfeda49ff,r3 - sub.b r4,r3 - test_cc 1 0 0 0 - checkr3 feda49fe - - move.d -0x5432f789,r4 - move.d 0x78134452,r3 - sub.b r4,r3 - test_cc 1 0 0 1 - checkr3 781344db - - move.d 0x85649222,r3 - move.d 0x77445622,r4 - sub.b r4,r3 - test_cc 0 1 0 0 - checkr3 85649200 - - quit diff --git a/tests/tcg/cris/bare/check_xarith.s b/tests/tcg/cris/bare/check_xarith.s deleted file mode 100644 index 80038b2..0000000 --- a/tests/tcg/cris/bare/check_xarith.s +++ /dev/null @@ -1,72 +0,0 @@ - -.include "testutils.inc" - - start - - moveq -1, $r0 - moveq 0, $r1 - addq 1, $r0 - ax - addq 0, $r1 - - move.d $r0, $r3 - checkr3 0 - move.d $r1, $r3 - checkr3 1 - - move.d 0, $r0 - moveq -1, $r1 - subq 1, $r0 - ax - subq 0, $r1 - - move.d $r0, $r3 - checkr3 ffffffff - move.d $r1, $r3 - checkr3 fffffffe - - - moveq -1, $r0 - moveq -1, $r1 - cmpq -1, $r0 - ax - cmpq -1, $r1 - beq 1f - nop - fail -1: - cmpq 0, $r0 - ax - cmpq -1, $r1 - bne 1f - nop - fail -1: - - ;; test for broken X sequence, run it several times. - moveq 8, $r0 -1: - moveq 0, $r3 - move.d $r0, $r1 - andq 1, $r1 - lslq 4, $r1 - moveq 1, $r2 - or.d $r1, $r2 - ba 2f - move $r2, $ccs -2: - addq 0, $r3 - move.d $r0, $r4 - move.d $r1, $r5 - move.d $r2, $r6 - move.d $r3, $r7 - lsrq 4, $r1 - move.d $r1, $r8 - xor $r1, $r3 - checkr3 0 - subq 1, $r0 - bne 1b - nop - - pass - quit diff --git a/tests/tcg/cris/bare/crt.s b/tests/tcg/cris/bare/crt.s deleted file mode 100644 index af027d7..0000000 --- a/tests/tcg/cris/bare/crt.s +++ /dev/null @@ -1,13 +0,0 @@ - .data -_stack_start: - .space 8192, 0 -_stack_end: - .text - .global _start -_start: - move.d _stack_end, $sp - jsr main - nop - moveq 0, $r10 - jump exit - nop diff --git a/tests/tcg/cris/bare/sys.c b/tests/tcg/cris/bare/sys.c deleted file mode 100644 index 1644eec..0000000 --- a/tests/tcg/cris/bare/sys.c +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Helper functions for CRIS system tests - * - * There is no libc and only a limited set of headers. - */ - -#include <stddef.h> - -void exit(int status) -{ - register unsigned int callno asm ("r9") = 1; /* NR_exit */ - - asm volatile ("break 13\n" - : /* no outputs */ - : "r" (callno) - : "memory"); - while (1) { - /* do nothing */ - }; -} - -size_t write(int fd, const void *buf, size_t count) -{ - register unsigned int callno asm ("r9") = 4; /* NR_write */ - register unsigned int r10 asm ("r10") = fd; - register const void *r11 asm ("r11") = buf; - register size_t r12 asm ("r12") = count; - register unsigned int r asm ("r10"); - - asm volatile ("break 13\n" - : "=r" (r) - : "r" (callno), "0" (r10), "r" (r11), "r" (r12) - : "memory"); - - return r; -} - -static inline int mystrlen(char *s) -{ - int i = 0; - while (s[i]) { - i++; - } - return i; -} - - -void pass(void) -{ - char s[] = "passed.\n"; - write(1, s, sizeof(s) - 1); - exit(0); -} - -void _fail(char *reason) -{ - char s[] = "\nfailed: "; - int len = mystrlen(reason); - write(1, s, sizeof(s) - 1); - write(1, reason, len); - write(1, "\n", 1); - exit(1); -} diff --git a/tests/tcg/cris/bare/testutils.inc b/tests/tcg/cris/bare/testutils.inc deleted file mode 100644 index aa1641b..0000000 --- a/tests/tcg/cris/bare/testutils.inc +++ /dev/null @@ -1,117 +0,0 @@ - .syntax no_register_prefix - - .macro start - .text - .global main -main: - .endm - - .macro quit - jump pass - nop - .endm - - .macro pass - jump pass - nop - .endm - - .macro startnostack - start - .endm - - .macro fail - .data -99: - .asciz " checkr3 failed\n" - .text - move.d 99b, $r10 - jsr _fail - nop - .endm - - .macro checkr3 val - cmp.d 0x\val, $r3 - beq 100f - nop - .data -99: - .asciz "checkr3 failed\n" - .text - move.d 99b, $r10 - jsr _fail - nop -100: - .endm - -; Test the condition codes - .macro test_cc N Z V C - .if \N - bpl 9f - nop - .else - bmi 9f - nop - .endif - .if \Z - bne 9f - nop - .else - beq 9f - nop - .endif - .if \V - bvc 9f - nop - .else - bvs 9f - nop - .endif - .if \C - bcc 9f - nop - .else - bcs 9f - nop - .endif - ba 8f - nop -9: - .data -99: - .asciz "test_move_cc failed\n" - .text - move.d 99b, $r10 - jsr _fail - nop -8: - .endm - - - .macro test_move_cc N Z V C - .if \N - bpl 9f - nop - .else - bmi 9f - nop - .endif - .if \Z - bne 9f - nop - .else - beq 9f - nop - .endif - ba 8f - nop -9: - .data -99: - .asciz "test_move_cc failed\n" - .text - move.d 99b, $r10 - jsr _fail - nop -8: - .endm diff --git a/tests/tcg/cris/libc/check_abs.c b/tests/tcg/cris/libc/check_abs.c deleted file mode 100644 index 08b67b6..0000000 --- a/tests/tcg/cris/libc/check_abs.c +++ /dev/null @@ -1,40 +0,0 @@ -#include <stdio.h> -#include <stdlib.h> -#include <stdint.h> -#include "sys.h" -#include "crisutils.h" - -static always_inline int cris_abs(int n) -{ - int r; - asm ("abs\t%1, %0\n" : "=r" (r) : "r" (n)); - return r; -} - -static always_inline void -verify_abs(int val, int res, - const int n, const int z, const int v, const int c) -{ - int r; - - cris_tst_cc_init(); - r = cris_abs(val); - cris_tst_cc(n, z, v, c); - if (r != res) - err(); -} - -int main(void) -{ - verify_abs(-1, 1, 0, 0, 0, 0); - verify_abs(0x80000000, 0x80000000, 1, 0, 0, 0); - verify_abs(0x7fffffff, 0x7fffffff, 0, 0, 0, 0); - verify_abs(42, 42, 0, 0, 0, 0); - verify_abs(1, 1, 0, 0, 0, 0); - verify_abs(0xffff, 0xffff, 0, 0, 0, 0); - verify_abs(0xffff, 0xffff, 0, 0, 0, 0); - verify_abs(-31, 0x1f, 0, 0, 0, 0); - verify_abs(0, 0, 0, 1, 0, 0); - pass(); - return 0; -} diff --git a/tests/tcg/cris/libc/check_addc.c b/tests/tcg/cris/libc/check_addc.c deleted file mode 100644 index fc3fb1f..0000000 --- a/tests/tcg/cris/libc/check_addc.c +++ /dev/null @@ -1,58 +0,0 @@ -#include <stdio.h> -#include <stdlib.h> -#include <stdint.h> -#include "sys.h" -#include "crisutils.h" - -static always_inline int cris_addc(int a, const int b) -{ - asm ("addc\t%1, %0\n" : "+r" (a) : "r" (b)); - return a; -} - -#define verify_addc(a, b, res, n, z, v, c) \ -{ \ - int r; \ - r = cris_addc((a), (b)); \ - cris_tst_cc((n), (z), (v), (c)); \ - if (r != (res)) \ - err(); \ -} - -int main(void) -{ - cris_tst_cc_init(); - asm volatile ("clearf cz"); - verify_addc(0, 0, 0, 0, 0, 0, 0); - - cris_tst_cc_init(); - asm volatile ("setf z"); - verify_addc(0, 0, 0, 0, 1, 0, 0); - - cris_tst_cc_init(); - asm volatile ("setf cz"); - verify_addc(0, 0, 1, 0, 0, 0, 0); - cris_tst_cc_init(); - asm volatile ("clearf c"); - verify_addc(-1, 2, 1, 0, 0, 0, 1); - - cris_tst_cc_init(); - asm volatile ("clearf nzv"); - asm volatile ("setf c"); - verify_addc(-1, 2, 2, 0, 0, 0, 1); - - cris_tst_cc_init(); - asm volatile ("setf c"); - verify_addc(0xffff, 0xffff, 0x1ffff, 0, 0, 0, 0); - - cris_tst_cc_init(); - asm volatile ("clearf nzvc"); - verify_addc(-1, -1, 0xfffffffe, 1, 0, 0, 1); - - cris_tst_cc_init(); - asm volatile ("setf c"); - verify_addc(0x78134452, 0x5432f789, 0xcc463bdc, 1, 0, 1, 0); - - pass(); - return 0; -} diff --git a/tests/tcg/cris/libc/check_addcm.c b/tests/tcg/cris/libc/check_addcm.c deleted file mode 100644 index b355ba1..0000000 --- a/tests/tcg/cris/libc/check_addcm.c +++ /dev/null @@ -1,85 +0,0 @@ -#include <stdio.h> -#include <stdlib.h> -#include <stdint.h> -#include "sys.h" -#include "crisutils.h" - -/* need to avoid acr as source here. */ -static always_inline int cris_addc_m(int a, const int *b) -{ - asm volatile ("addc [%1], %0\n" : "+r" (a) : "r" (b)); - return a; -} - -/* 'b' is a crisv32 constrain to avoid postinc with $acr. */ -static always_inline int cris_addc_pi_m(int a, int **b) -{ - asm volatile ("addc [%1+], %0\n" : "+r" (a), "+b" (*b)); - return a; -} - -#define verify_addc_m(a, b, res, n, z, v, c) \ -{ \ - int r; \ - r = cris_addc_m((a), (b)); \ - cris_tst_cc((n), (z), (v), (c)); \ - if (r != (res)) \ - err(); \ -} - -#define verify_addc_pi_m(a, b, res, n, z, v, c) \ -{ \ - int r; \ - r = cris_addc_pi_m((a), (b)); \ - cris_tst_cc((n), (z), (v), (c)); \ - if (r != (res)) \ - err(); \ -} - -int x[] = { 0, 0, 2, -1, 0xffff, -1, 0x5432f789}; - -int main(void) -{ - int *p = (void *)&x[0]; -#if 1 - cris_tst_cc_init(); - asm volatile ("clearf cz"); - verify_addc_m(0, p, 0, 0, 0, 0, 0); - - cris_tst_cc_init(); - asm volatile ("setf z"); - verify_addc_m(0, p, 0, 0, 1, 0, 0); - - cris_tst_cc_init(); - asm volatile ("setf c"); - verify_addc_m(0, p, 1, 0, 0, 0, 0); - - cris_tst_cc_init(); - asm volatile ("clearf c"); - verify_addc_pi_m(0, &p, 0, 0, 1, 0, 0); - - p = &x[1]; - cris_tst_cc_init(); - asm volatile ("setf c"); - verify_addc_pi_m(0, &p, 1, 0, 0, 0, 0); - - if (p != &x[2]) - err(); - - cris_tst_cc_init(); - asm volatile ("clearf c"); - verify_addc_pi_m(-1, &p, 1, 0, 0, 0, 1); - - if (p != &x[3]) - err(); -#endif - p = &x[3]; - /* TODO: investigate why this one fails. */ - cris_tst_cc_init(); - asm volatile ("setf c"); - verify_addc_m(2, p, 2, 0, 0, 0, 1); - p += 4; - - pass(); - return 0; -} diff --git a/tests/tcg/cris/libc/check_addo.c b/tests/tcg/cris/libc/check_addo.c deleted file mode 100644 index 4235e5f..0000000 --- a/tests/tcg/cris/libc/check_addo.c +++ /dev/null @@ -1,125 +0,0 @@ -#include <stdio.h> -#include <stdlib.h> -#include <stdint.h> -#include "sys.h" -#include "crisutils.h" - -/* this would be better to do in asm, it's an orgy in GCC inline asm now. */ - -#define cris_addo_b(o, v) \ - asm volatile ("addo.b\t[%0], %1, $acr\n" : : "r" (o), "r" (v) : "acr"); -#define cris_addo_w(o, v) \ - asm volatile ("addo.w\t[%0], %1, $acr\n" : : "r" (o), "r" (v) : "acr"); -#define cris_addo_d(o, v) \ - asm volatile ("addo.d\t[%0], %1, $acr\n" : : "r" (o), "r" (v) : "acr"); -#define cris_addo_pi_b(o, v) \ - asm volatile ("addo.b\t[%0+], %1, $acr\n" \ - : "+b" (o): "r" (v) : "acr"); -#define cris_addo_pi_w(o, v) \ - asm volatile ("addo.w\t[%0+], %1, $acr\n" \ - : "+b" (o): "r" (v) : "acr"); -#define cris_addo_pi_d(o, v) \ - asm volatile ("addo.d\t[%0+], %1, $acr\n" \ - : "+b" (o): "r" (v) : "acr"); - -struct { - uint32_t v1; - uint16_t v2; - uint32_t v3; - uint8_t v4; - uint8_t v5; - uint16_t v6; - uint32_t v7; -} y = { - 32769, - -1, - 5, - 3, -4, - 2, - -76789887 -}; - -static int x[3] = {0x55aa77ff, 0xccff2244, 0x88ccee19}; - -int main(void) -{ - int *r; - unsigned char *t, *p; - - /* Note, this test-case will trig an unaligned access, partly - to x[0] and to [x1]. */ - t = (unsigned char *)x; - t -= 32768; - p = (unsigned char *) &y.v1; - mb(); /* don't reorder anything beyond here. */ - cris_tst_cc_init(); - asm volatile ("setf\tzvnc\n"); - cris_addo_pi_d(p, t); - cris_tst_cc(1, 1, 1, 1); - asm volatile ("move.d\t$acr, %0\n" : "=r" (r)); - if (*r != 0x4455aa77) - err(); - - - t += 32770; - mb(); /* don't reorder anything beyond here. */ - cris_tst_cc_init(); - asm volatile ("setf\tzvnc\n"); - cris_addo_pi_w(p, t); - cris_tst_cc(1, 1, 1, 1); - asm volatile ("move.d\t$acr, %0\n" : "=r" (r)); - if (*r != 0x4455aa77) - err(); - - mb(); /* don't reorder anything beyond here. */ - cris_tst_cc_init(); - asm volatile ("setf\tzvnc\n"); - cris_addo_d(p, r); - cris_tst_cc(1, 1, 1, 1); - p += 4; - asm volatile ("move.d\t$acr, %0\n" : "=r" (r)); - if (*r != 0xee19ccff) - err(); - - mb(); /* don't reorder anything beyond here. */ - cris_tst_cc_init(); - asm volatile ("setf\tzvnc\n"); - cris_addo_pi_b(p, t); - cris_tst_cc(0, 0, 0, 0); - asm volatile ("move.d\t$acr, %0\n" : "=r" (r)); - if (*(uint16_t*)r != 0xff22) - err(); - - mb(); /* don't reorder anything beyond here. */ - cris_tst_cc_init(); - asm volatile ("setf\tzvnc\n"); - cris_addo_b(p, r); - cris_tst_cc(1, 1, 1, 1); - p += 1; - asm volatile ("move.d\t$acr, %0\n" : "=r" (r)); - if (*r != 0x4455aa77) - err(); - - mb(); /* don't reorder anything beyond here. */ - cris_tst_cc_init(); - asm volatile ("setf\tzvnc\n"); - cris_addo_w(p, r); - cris_tst_cc(1, 1, 1, 1); - p += 2; - asm volatile ("move.d\t$acr, %0\n" : "=r" (r)); - if (*r != 0xff224455) - err(); - - mb(); /* don't reorder anything beyond here. */ - cris_tst_cc_init(); - asm volatile ("setf\tzvnc\n"); - cris_addo_pi_d(p, t); - cris_tst_cc(0, 0, 0, 0); - asm volatile ("move.d\t$acr, %0\n" : "=r" (r)); - r = (void*)(((char *)r) + 76789885); - if (*r != 0x55aa77ff) - err(); - - pass(); - return 0; -} diff --git a/tests/tcg/cris/libc/check_addoq.c b/tests/tcg/cris/libc/check_addoq.c deleted file mode 100644 index ed509e2..0000000 --- a/tests/tcg/cris/libc/check_addoq.c +++ /dev/null @@ -1,44 +0,0 @@ -#include <stdio.h> -#include <stdlib.h> -#include <stdint.h> -#include "sys.h" -#include "crisutils.h" - -/* this would be better to do in asm, it's an orgy in GCC inline asm now. */ - -/* ACR will be clobbered. */ -#define cris_addoq(o, v) \ - asm volatile ("addoq\t%1, %0, $acr\n" : : "r" (v), "i" (o) : "acr"); - - -int main(void) -{ - int x[3] = {0x55aa77ff, 0xccff2244, 0x88ccee19}; - int *p, *t = x + 1; - - cris_tst_cc_init(); - asm volatile ("setf\tzvnc\n"); - cris_addoq(0, t); - cris_tst_cc(1, 1, 1, 1); - asm volatile ("move.d\t$acr, %0\n" : "=r" (p)); - if (*p != 0xccff2244) - err(); - - cris_tst_cc_init(); - asm volatile ("setf\tzvnc\n"); - cris_addoq(4, t); - cris_tst_cc(0, 0, 0, 0); - asm volatile ("move.d\t$acr, %0\n" : "=r" (p)); - if (*p != 0x88ccee19) - err(); - - cris_tst_cc_init(); - asm volatile ("clearf\tzvnc\n"); - cris_addoq(-8, t + 1); - cris_tst_cc(0, 0, 0, 0); - asm volatile ("move.d\t$acr, %0\n" : "=r" (p)); - if (*p != 0x55aa77ff) - err(); - pass(); - return 0; -} diff --git a/tests/tcg/cris/libc/check_bound.c b/tests/tcg/cris/libc/check_bound.c deleted file mode 100644 index d956ab9..0000000 --- a/tests/tcg/cris/libc/check_bound.c +++ /dev/null @@ -1,142 +0,0 @@ -#include <stdio.h> -#include <stdlib.h> -#include <stdint.h> -#include "sys.h" -#include "crisutils.h" - -static always_inline int cris_bound_b(int v, int b) -{ - int r = v; - asm ("bound.b\t%1, %0\n" : "+r" (r) : "ri" (b)); - return r; -} - -static always_inline int cris_bound_w(int v, int b) -{ - int r = v; - asm ("bound.w\t%1, %0\n" : "+r" (r) : "ri" (b)); - return r; -} - -static always_inline int cris_bound_d(int v, int b) -{ - int r = v; - asm ("bound.d\t%1, %0\n" : "+r" (r) : "ri" (b)); - return r; -} - -int main(void) -{ - int r; - - cris_tst_cc_init(); - r = cris_bound_d(-1, 2); - cris_tst_cc(0, 0, 0, 0); - if (r != 2) - err(); - - cris_tst_cc_init(); - r = cris_bound_d(2, 0xffffffff); - cris_tst_cc(0, 0, 0, 0); - if (r != 2) - err(); - - cris_tst_cc_init(); - r = cris_bound_d(0xffff, 0xffff); - cris_tst_cc(0, 0, 0, 0); - if (r != 0xffff) - err(); - - cris_tst_cc_init(); - r = cris_bound_d(-1, 0xffffffff); - cris_tst_cc(1, 0, 0, 0); - if (r != 0xffffffff) - err(); - - cris_tst_cc_init(); - r = cris_bound_d(0x78134452, 0x5432f789); - cris_tst_cc(0, 0, 0, 0); - if (r != 0x5432f789) - err(); - - cris_tst_cc_init(); - r = cris_bound_w(-1, 2); - cris_tst_cc(0, 0, 0, 0); - if (r != 2) - err(); - - cris_tst_cc_init(); - r = cris_bound_w(-1, 0xffff); - cris_tst_cc(0, 0, 0, 0); - if (r != 0xffff) - err(); - - cris_tst_cc_init(); - r = cris_bound_w(2, 0xffff); - cris_tst_cc(0, 0, 0, 0); - if (r != 2) - err(); - - cris_tst_cc_init(); - r = cris_bound_w(0xfedaffff, 0xffff); - cris_tst_cc(0, 0, 0, 0); - if (r != 0xffff) - err(); - - cris_tst_cc_init(); - r = cris_bound_w(0x78134452, 0xf789); - cris_tst_cc(0, 0, 0, 0); - if (r != 0xf789) - err(); - - cris_tst_cc_init(); - r = cris_bound_b(-1, 2); - cris_tst_cc(0, 0, 0, 0); - if (r != 2) - err(); - - cris_tst_cc_init(); - r = cris_bound_b(2, 0xff); - cris_tst_cc(0, 0, 0, 0); - if (r != 2) - err(); - - cris_tst_cc_init(); - r = cris_bound_b(-1, 0xff); - cris_tst_cc(0, 0, 0, 0); - if (r != 0xff) - err(); - - cris_tst_cc_init(); - r = cris_bound_b(0xff, 0xff); - cris_tst_cc(0, 0, 0, 0); - if (r != 0xff) - err(); - - cris_tst_cc_init(); - r = cris_bound_b(0xfeda49ff, 0xff); - cris_tst_cc(0, 0, 0, 0); - if (r != 0xff) - err(); - - cris_tst_cc_init(); - r = cris_bound_b(0x78134452, 0x89); - cris_tst_cc(0, 0, 0, 0); - if (r != 0x89) - err(); - - cris_tst_cc_init(); - r = cris_bound_w(0x78134452, 0); - cris_tst_cc(0, 1, 0, 0); - if (r != 0) - err(); - - cris_tst_cc_init(); - r = cris_bound_b(0xffff, -1); - cris_tst_cc(0, 0, 0, 0); - if (r != 0xff) - err(); - - pass(); - return 0; -} diff --git a/tests/tcg/cris/libc/check_ftag.c b/tests/tcg/cris/libc/check_ftag.c deleted file mode 100644 index aaa5c97..0000000 --- a/tests/tcg/cris/libc/check_ftag.c +++ /dev/null @@ -1,37 +0,0 @@ -#include <stdio.h> -#include <stdlib.h> -#include <stdint.h> -#include "sys.h" -#include "crisutils.h" - -static always_inline void cris_ftag_i(unsigned int x) -{ - register unsigned int v asm("$r10") = x; - asm ("ftagi\t[%0]\n" : : "r" (v) ); -} -static always_inline void cris_ftag_d(unsigned int x) -{ - register unsigned int v asm("$r10") = x; - asm ("ftagd\t[%0]\n" : : "r" (v) ); -} -static always_inline void cris_fidx_i(unsigned int x) -{ - register unsigned int v asm("$r10") = x; - asm ("fidxi\t[%0]\n" : : "r" (v) ); -} -static always_inline void cris_fidx_d(unsigned int x) -{ - register unsigned int v asm("$r10") = x; - asm ("fidxd\t[%0]\n" : : "r" (v) ); -} - - -int main(void) -{ - cris_ftag_i(0); - cris_ftag_d(0); - cris_fidx_i(0); - cris_fidx_d(0); - pass(); - return 0; -} diff --git a/tests/tcg/cris/libc/check_gcctorture_pr28634-1.c b/tests/tcg/cris/libc/check_gcctorture_pr28634-1.c deleted file mode 100644 index 45ecd15..0000000 --- a/tests/tcg/cris/libc/check_gcctorture_pr28634-1.c +++ /dev/null @@ -1,15 +0,0 @@ -/* PR rtl-optimization/28634. On targets with delayed branches, - dbr_schedule could do the next iteration's addition in the - branch delay slot, then subtract the value again if the branch - wasn't taken. This can lead to rounding errors. */ -int x = -1; -int y = 1; -int -main (void) -{ - while (y > 0) - y += x; - if (y != x + 1) - abort (); - exit (0); -} diff --git a/tests/tcg/cris/libc/check_gcctorture_pr28634.c b/tests/tcg/cris/libc/check_gcctorture_pr28634.c deleted file mode 100644 index a0c5254..0000000 --- a/tests/tcg/cris/libc/check_gcctorture_pr28634.c +++ /dev/null @@ -1,15 +0,0 @@ -/* PR rtl-optimization/28634. On targets with delayed branches, - dbr_schedule could do the next iteration's addition in the - branch delay slot, then subtract the value again if the branch - wasn't taken. This can lead to rounding errors. */ -double x = -0x1.0p53; -double y = 1; -int -main (void) -{ - while (y > 0) - y += x; - if (y != x + 1) - abort (); - exit (0); -} diff --git a/tests/tcg/cris/libc/check_glibc_kernelversion.c b/tests/tcg/cris/libc/check_glibc_kernelversion.c deleted file mode 100644 index 7aada89..0000000 --- a/tests/tcg/cris/libc/check_glibc_kernelversion.c +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Check the lz insn. - */ - -#include <stdio.h> -#include <stdlib.h> -#include <stdint.h> -#include "sys.h" - -#define __LINUX_KERNEL_VERSION 131584 - -#define DL_SYSDEP_OSCHECK(FATAL) \ - do { \ - /* Test whether the kernel is new enough. This test is only \ - performed if the library is not compiled to run on all \ - kernels. */ \ - if (__LINUX_KERNEL_VERSION > 0) \ - { \ - char bufmem[64]; \ - char *buf = bufmem; \ - unsigned int version; \ - int parts; \ - char *cp; \ - struct utsname uts; \ - \ - /* Try the uname syscall */ \ - if (__uname (&uts)) \ - { \ - /* This was not successful. Now try reading the /proc \ - filesystem. */ \ - ssize_t reslen; \ - int fd = __open ("/proc/sys/kernel/osrelease", O_RDONLY); \ - if (fd == -1 \ - || (reslen = __read (fd, bufmem, sizeof (bufmem))) <= 0) \ - /* This also didn't work. We give up since we cannot \ - make sure the library can actually work. */ \ - FATAL ("FATAL: cannot determine library version\n"); \ - __close (fd); \ - buf[MIN (reslen, (ssize_t) sizeof (bufmem) - 1)] = '\0'; \ - } \ - else \ - buf = uts.release; \ - \ - /* Now convert it into a number. The string consists of at most \ - three parts. */ \ - version = 0; \ - parts = 0; \ - cp = buf; \ - while ((*cp >= '0') && (*cp <= '9')) \ - { \ - unsigned int here = *cp++ - '0'; \ - \ - while ((*cp >= '0') && (*cp <= '9')) \ - { \ - here *= 10; \ - here += *cp++ - '0'; \ - } \ - \ - ++parts; \ - version <<= 8; \ - version |= here; \ - \ - if (*cp++ != '.') \ - /* Another part following? */ \ - break; \ - } \ - \ - if (parts < 3) \ - version <<= 8 * (3 - parts); \ - \ - /* Now we can test with the required version. */ \ - if (version < __LINUX_KERNEL_VERSION) \ - /* Not sufficient. */ \ - FATAL ("FATAL: kernel too old\n"); \ - \ - _dl_osversion = version; \ - } \ - } while (0) - -int main(void) -{ - char bufmem[64] = "2.6.22"; - char *buf = bufmem; - unsigned int version; - int parts; - char *cp; - - version = 0; - parts = 0; - cp = buf; - while ((*cp >= '0') && (*cp <= '9')) - { - unsigned int here = *cp++ - '0'; - - while ((*cp >= '0') && (*cp <= '9')) - { - here *= 10; - here += *cp++ - '0'; - } - - ++parts; - version <<= 8; - version |= here; - - if (*cp++ != '.') - /* Another part following? */ - break; - } - - if (parts < 3) - version <<= 8 * (3 - parts); - if (version < __LINUX_KERNEL_VERSION) - err(); - pass(); - exit(0); -} diff --git a/tests/tcg/cris/libc/check_hello.c b/tests/tcg/cris/libc/check_hello.c deleted file mode 100644 index fb403ba..0000000 --- a/tests/tcg/cris/libc/check_hello.c +++ /dev/null @@ -1,7 +0,0 @@ -#include <stdio.h> -#include <stdlib.h> -int main () -{ - printf ("pass\n"); - exit (0); -} diff --git a/tests/tcg/cris/libc/check_int64.c b/tests/tcg/cris/libc/check_int64.c deleted file mode 100644 index 69caec1..0000000 --- a/tests/tcg/cris/libc/check_int64.c +++ /dev/null @@ -1,47 +0,0 @@ -#include <stdio.h> -#include <stdlib.h> -#include <stdint.h> -#include "sys.h" -#include "crisutils.h" - - -static always_inline int64_t add64(const int64_t a, const int64_t b) -{ - return a + b; -} - -static always_inline int64_t sub64(const int64_t a, const int64_t b) -{ - return a - b; -} - -int main(void) -{ - int64_t a = 1; - int64_t b = 2; - - /* FIXME: add some tests. */ - a = add64(a, b); - if (a != 3) - err(); - - a = sub64(a, b); - if (a != 1) - err(); - - a = add64(a, -4); - if (a != -3) - err(); - - a = add64(a, 3); - if (a != 0) - err(); - - a = 0; - a = sub64(a, 1); - if (a != -1) - err(); - - pass(); - return 0; -} diff --git a/tests/tcg/cris/libc/check_lz.c b/tests/tcg/cris/libc/check_lz.c deleted file mode 100644 index bf051a6..0000000 --- a/tests/tcg/cris/libc/check_lz.c +++ /dev/null @@ -1,49 +0,0 @@ -#include <stdio.h> -#include <stdlib.h> -#include <stdint.h> -#include "sys.h" - -static always_inline int cris_lz(int x) -{ - int r; - asm ("lz\t%1, %0\n" : "=r" (r) : "r" (x)); - return r; -} - -void check_lz(void) -{ - int i; - - if (cris_lz(0) != 32) - err(); - if (cris_lz(1) != 31) - err(); - if (cris_lz(2) != 30) - err(); - if (cris_lz(4) != 29) - err(); - if (cris_lz(8) != 28) - err(); - - /* try all positions with a single bit. */ - for (i = 1; i < 32; i++) { - if (cris_lz(1 << (i-1)) != (32 - i)) - err(); - } - - /* try all positions with all bits. */ - for (i = 1; i < 32; i++) { - /* split up this computation to clarify it. */ - uint32_t val; - val = (unsigned int)-1 >> (32 - i); - if (cris_lz(val) != (32 - i)) - err(); - } -} - -int main(void) -{ - check_lz(); - pass(); - exit(0); -} diff --git a/tests/tcg/cris/libc/check_mapbrk.c b/tests/tcg/cris/libc/check_mapbrk.c deleted file mode 100644 index 1aff762..0000000 --- a/tests/tcg/cris/libc/check_mapbrk.c +++ /dev/null @@ -1,39 +0,0 @@ -#include <stdio.h> -#include <stdlib.h> - -/* Basic sanity check that syscalls to implement malloc (brk, mmap2, - munmap) are trivially functional. */ - -int main () -{ - void *p1, *p2, *p3, *p4, *p5, *p6; - - if ((p1 = malloc (8100)) == NULL - || (p2 = malloc (16300)) == NULL - || (p3 = malloc (4000)) == NULL - || (p4 = malloc (500)) == NULL - || (p5 = malloc (1023*1024)) == NULL - || (p6 = malloc (8191*1024)) == NULL) - { - printf ("fail\n"); - exit (1); - } - - free (p1); - free (p2); - free (p3); - free (p4); - free (p5); - free (p6); - - p1 = malloc (64000); - if (p1 == NULL) - { - printf ("fail\n"); - exit (1); - } - free (p1); - - printf ("pass\n"); - exit (0); -} diff --git a/tests/tcg/cris/libc/check_mmap1.c b/tests/tcg/cris/libc/check_mmap1.c deleted file mode 100644 index b803f0c..0000000 --- a/tests/tcg/cris/libc/check_mmap1.c +++ /dev/null @@ -1,48 +0,0 @@ -/* -#notarget: cris*-*-elf -*/ - -#define _GNU_SOURCE -#include <string.h> -#include <stdlib.h> -#include <stdio.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <fcntl.h> -#include <unistd.h> -#include <sys/mman.h> - -int main (int argc, char *argv[]) -{ - int fd = open (argv[0], O_RDONLY); - struct stat sb; - int size; - void *a; - const char *str = "a string you'll only find in the program"; - - if (fd == -1) - { - perror ("open"); - abort (); - } - - if (fstat (fd, &sb) < 0) - { - perror ("fstat"); - abort (); - } - - size = sb.st_size; - - /* We want to test mmapping a size that isn't exactly a page. */ - if ((size & 8191) == 0) - size--; - - a = mmap (NULL, size, PROT_READ, MAP_PRIVATE, fd, 0); - - if (memmem (a, size, str, strlen (str) + 1) == NULL) - abort (); - - printf ("pass\n"); - exit (0); -} diff --git a/tests/tcg/cris/libc/check_mmap2.c b/tests/tcg/cris/libc/check_mmap2.c deleted file mode 100644 index 35139a0..0000000 --- a/tests/tcg/cris/libc/check_mmap2.c +++ /dev/null @@ -1,48 +0,0 @@ -/* -#notarget: cris*-*-elf -*/ - -#define _GNU_SOURCE -#include <string.h> -#include <stdlib.h> -#include <stdio.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <fcntl.h> -#include <unistd.h> -#include <sys/mman.h> - -int main (int argc, char *argv[]) -{ - int fd = open (argv[0], O_RDONLY); - struct stat sb; - int size; - void *a; - const char *str = "a string you'll only find in the program"; - - if (fd == -1) - { - perror ("open"); - abort (); - } - - if (fstat (fd, &sb) < 0) - { - perror ("fstat"); - abort (); - } - - size = sb.st_size; - - /* We want to test mmapping a size that isn't exactly a page. */ - if ((size & 8191) == 0) - size--; - - a = mmap (NULL, size, PROT_READ, MAP_SHARED, fd, 0); - - if (memmem (a, size, str, strlen (str) + 1) == NULL) - abort (); - - printf ("pass\n"); - exit (0); -} diff --git a/tests/tcg/cris/libc/check_mmap3.c b/tests/tcg/cris/libc/check_mmap3.c deleted file mode 100644 index cb890ef..0000000 --- a/tests/tcg/cris/libc/check_mmap3.c +++ /dev/null @@ -1,33 +0,0 @@ -/* -#notarget: cris*-*-elf -*/ - -#define _GNU_SOURCE -#include <string.h> -#include <stdlib.h> -#include <stdio.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <unistd.h> -#include <sys/mman.h> - -int main (int argc, char *argv[]) -{ - volatile unsigned char *a; - - /* Check that we can map a non-multiple of a page and still get a full page. */ - a = mmap (NULL, 0x4c, PROT_READ | PROT_WRITE | PROT_EXEC, - MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); - if (a == NULL || a == (unsigned char *) -1) - abort (); - - a[0] = 0xbe; - a[8191] = 0xef; - memset ((char *) a + 1, 0, 8190); - - if (a[0] != 0xbe || a[8191] != 0xef) - abort (); - - printf ("pass\n"); - exit (0); -} diff --git a/tests/tcg/cris/libc/check_moveq.c b/tests/tcg/cris/libc/check_moveq.c deleted file mode 100644 index 80f2dff..0000000 --- a/tests/tcg/cris/libc/check_moveq.c +++ /dev/null @@ -1,51 +0,0 @@ -#include <stdio.h> -#include <stdlib.h> -#include <stdint.h> -#include "sys.h" -#include "crisutils.h" - -#define cris_moveq(dst, src) \ - asm volatile ("moveq %1, %0\n" : "=r" (dst) : "i" (src)); - - - -int main(void) -{ - int t; - - cris_tst_cc_init(); - asm volatile ("setf\tzvnc\n"); - cris_moveq(t, 10); - cris_tst_cc(1, 1, 1, 1); - if (t != 10) - err(); - - /* make sure moveq doesn't clobber the zflag. */ - cris_tst_cc_init(); - asm volatile ("setf vnc\n"); - asm volatile ("clearf z\n"); - cris_moveq(t, 0); - cris_tst_cc(1, 0, 1, 1); - if (t != 0) - err(); - - /* make sure moveq doesn't clobber the nflag. - Also check large immediates */ - cris_tst_cc_init(); - asm volatile ("setf zvc\n"); - asm volatile ("clearf n\n"); - cris_moveq(t, -31); - cris_tst_cc(0, 1, 1, 1); - if (t != -31) - err(); - - cris_tst_cc_init(); - asm volatile ("setf nzvc\n"); - cris_moveq(t, 31); - cris_tst_cc(1, 1, 1, 1); - if (t != 31) - err(); - - pass(); - return 0; -} diff --git a/tests/tcg/cris/libc/check_openpf1.c b/tests/tcg/cris/libc/check_openpf1.c deleted file mode 100644 index 251d26e..0000000 --- a/tests/tcg/cris/libc/check_openpf1.c +++ /dev/null @@ -1,38 +0,0 @@ -/* Check that --sysroot is applied to open(2). -#sim: --sysroot=@exedir@ - - We assume, with EXE being the name of the executable: - - The simulator executes with cwd the same directory where the executable - is located (so argv[0] contains a plain filename without directory - components). - - There's no /EXE on the host file system. */ - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <errno.h> -int main (int argc, char *argv[]) -{ - char *fnam = argv[0]; - FILE *f; - if (argv[0][0] != '/') - { - fnam = malloc (strlen (argv[0]) + 2); - if (fnam == NULL) - abort (); - strcpy (fnam, "/"); - strcat (fnam, argv[0]); - } - - f = fopen (fnam, "rb"); - if (f == NULL) - abort (); - fclose(f); - - /* Cover another execution path. */ - if (fopen ("/nonexistent", "rb") != NULL - || errno != ENOENT) - abort (); - printf ("pass\n"); - return 0; -} diff --git a/tests/tcg/cris/libc/check_openpf2.c b/tests/tcg/cris/libc/check_openpf2.c deleted file mode 100644 index 5d56189..0000000 --- a/tests/tcg/cris/libc/check_openpf2.c +++ /dev/null @@ -1,16 +0,0 @@ -/* Check that the simulator has chdir:ed to the --sysroot argument -#sim: --sysroot=@srcdir@ - (or that --sysroot is applied to relative file paths). */ - -#include <stdio.h> -#include <stdlib.h> -#include <errno.h> -int main (int argc, char *argv[]) -{ - FILE *f = fopen ("check_openpf2.c", "rb"); - if (f == NULL) - abort (); - fclose(f); - printf ("pass\n"); - return 0; -} diff --git a/tests/tcg/cris/libc/check_openpf3.c b/tests/tcg/cris/libc/check_openpf3.c deleted file mode 100644 index 557adee..0000000 --- a/tests/tcg/cris/libc/check_openpf3.c +++ /dev/null @@ -1,49 +0,0 @@ -/* Basic file operations (rename, unlink); once without sysroot. We - also test that the simulator has chdir:ed to PREFIX, when defined. */ - -#include <stdio.h> -#include <stdlib.h> -#include <errno.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <unistd.h> - -#ifndef PREFIX -#define PREFIX -#endif - -void err (const char *s) -{ - perror (s); - abort (); -} - -int main (int argc, char *argv[]) -{ - FILE *f; - struct stat buf; - - unlink (PREFIX "testfoo2.tmp"); - - f = fopen ("testfoo1.tmp", "w"); - if (f == NULL) - err ("open"); - fclose (f); - - if (rename (PREFIX "testfoo1.tmp", PREFIX "testfoo2.tmp") != 0) - err ("rename"); - - if (stat (PREFIX "testfoo2.tmp", &buf) != 0 - || !S_ISREG (buf.st_mode)) - err ("stat 1"); - - if (stat ("testfoo2.tmp", &buf) != 0 - || !S_ISREG (buf.st_mode)) - err ("stat 2"); - - if (unlink (PREFIX "testfoo2.tmp") != 0) - err ("unlink"); - - printf ("pass\n"); - return 0; -} diff --git a/tests/tcg/cris/libc/check_openpf5.c b/tests/tcg/cris/libc/check_openpf5.c deleted file mode 100644 index 1f86ea2..0000000 --- a/tests/tcg/cris/libc/check_openpf5.c +++ /dev/null @@ -1,56 +0,0 @@ -/* Check that TRT happens when error on too many opened files. -#notarget: cris*-*-elf -#sim: --sysroot=@exedir@ -*/ -#include <stddef.h> -#include <stdlib.h> -#include <stdio.h> -#include <unistd.h> -#include <errno.h> -#include <limits.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <fcntl.h> -#include <string.h> - -int main (int argc, char *argv[]) -{ - int i; - int filemax; - -#ifdef OPEN_MAX - filemax = OPEN_MAX; -#else - filemax = sysconf (_SC_OPEN_MAX); -#endif - - char *fn = malloc (strlen (argv[0]) + 2); - if (fn == NULL) - abort (); - strcpy (fn, "/"); - strcat (fn, argv[0]); - - for (i = 0; i < filemax + 1; i++) - { - if (open (fn, O_RDONLY) < 0) - { - /* Shouldn't happen too early. */ - if (i < filemax - 3 - 1) - { - fprintf (stderr, "i: %d\n", i); - abort (); - } - if (errno != EMFILE) - { - perror ("open"); - abort (); - } - goto ok; - } - } - abort (); - -ok: - printf ("pass\n"); - exit (0); -} diff --git a/tests/tcg/cris/libc/check_settls1.c b/tests/tcg/cris/libc/check_settls1.c deleted file mode 100644 index 3abc3a9..0000000 --- a/tests/tcg/cris/libc/check_settls1.c +++ /dev/null @@ -1,45 +0,0 @@ -#include <stdio.h> -#include <stdlib.h> -#include <errno.h> -#include <unistd.h> - -#include <sys/syscall.h> - -#ifndef SYS_set_thread_area -#define SYS_set_thread_area 243 -#endif - -int main (void) -{ - unsigned long tp, old_tp; - int ret; - - asm volatile ("move $pid,%0" : "=r" (old_tp)); - old_tp &= ~0xff; - - ret = syscall (SYS_set_thread_area, 0xf0); - if (ret != -1 || errno != EINVAL) { - syscall (SYS_set_thread_area, old_tp); - perror ("Invalid thread area accepted:"); - abort(); - } - - ret = syscall (SYS_set_thread_area, 0xeddeed00); - if (ret != 0) { - perror ("Valid thread area not accepted: "); - abort (); - } - - asm volatile ("move $pid,%0" : "=r" (tp)); - tp &= ~0xff; - syscall (SYS_set_thread_area, old_tp); - - if (tp != 0xeddeed00) { - * (volatile int *) 0 = 0; - perror ("tls2"); - abort (); - } - - printf ("pass\n"); - return EXIT_SUCCESS; -} diff --git a/tests/tcg/cris/libc/check_sigalrm.c b/tests/tcg/cris/libc/check_sigalrm.c deleted file mode 100644 index 39fa8d9..0000000 --- a/tests/tcg/cris/libc/check_sigalrm.c +++ /dev/null @@ -1,26 +0,0 @@ -#include <stdio.h> -#include <stdlib.h> -#include <signal.h> -#include <unistd.h> - -#define MAGIC (0xdeadbeef) - -int s = 0; -void sighandler(int sig) -{ - s = MAGIC; -} - -int main(int argc, char **argv) -{ - int p; - - p = getpid(); - signal(SIGALRM, sighandler); - kill(p, SIGALRM); - if (s != MAGIC) - return EXIT_FAILURE; - - printf ("passed\n"); - return EXIT_SUCCESS; -} diff --git a/tests/tcg/cris/libc/check_stat1.c b/tests/tcg/cris/libc/check_stat1.c deleted file mode 100644 index 2e2cae5..0000000 --- a/tests/tcg/cris/libc/check_stat1.c +++ /dev/null @@ -1,16 +0,0 @@ -#include <sys/types.h> -#include <sys/stat.h> -#include <unistd.h> -#include <stdio.h> -#include <stdlib.h> - -int main (void) -{ - struct stat buf; - - if (stat (".", &buf) != 0 - || !S_ISDIR (buf.st_mode)) - abort (); - printf ("pass\n"); - exit (0); -} diff --git a/tests/tcg/cris/libc/check_stat2.c b/tests/tcg/cris/libc/check_stat2.c deleted file mode 100644 index e36172e..0000000 --- a/tests/tcg/cris/libc/check_stat2.c +++ /dev/null @@ -1,20 +0,0 @@ -/* -#notarget: cris*-*-elf -*/ - -#include <sys/types.h> -#include <sys/stat.h> -#include <unistd.h> -#include <stdio.h> -#include <stdlib.h> - -int main (void) -{ - struct stat buf; - - if (lstat (".", &buf) != 0 - || !S_ISDIR (buf.st_mode)) - abort (); - printf ("pass\n"); - exit (0); -} diff --git a/tests/tcg/cris/libc/check_stat3.c b/tests/tcg/cris/libc/check_stat3.c deleted file mode 100644 index 36a9d5d..0000000 --- a/tests/tcg/cris/libc/check_stat3.c +++ /dev/null @@ -1,25 +0,0 @@ -/* Simulator options: -#sim: --sysroot=@exedir@ -*/ -#include <sys/types.h> -#include <sys/stat.h> -#include <unistd.h> -#include <stdio.h> -#include <string.h> -#include <stdlib.h> - -int main (int argc, char *argv[]) -{ - char path[1024] = "/"; - struct stat buf; - - strncat(path, argv[0], sizeof(path) - 2); - if (stat (".", &buf) != 0 - || !S_ISDIR (buf.st_mode)) - abort (); - if (stat (path, &buf) != 0 - || !S_ISREG (buf.st_mode)) - abort (); - printf ("pass\n"); - exit (0); -} diff --git a/tests/tcg/cris/libc/check_stat4.c b/tests/tcg/cris/libc/check_stat4.c deleted file mode 100644 index 04f21fe..0000000 --- a/tests/tcg/cris/libc/check_stat4.c +++ /dev/null @@ -1,27 +0,0 @@ -/* Simulator options: -#notarget: cris*-*-elf -#sim: --sysroot=@exedir@ -*/ - -#include <sys/types.h> -#include <sys/stat.h> -#include <unistd.h> -#include <stdio.h> -#include <string.h> -#include <stdlib.h> - -int main (int argc, char *argv[]) -{ - char path[1024] = "/"; - struct stat buf; - - strncat(path, argv[0], sizeof(path) - 2); - if (lstat (".", &buf) != 0 - || !S_ISDIR (buf.st_mode)) - abort (); - if (lstat (path, &buf) != 0 - || !S_ISREG (buf.st_mode)) - abort (); - printf ("pass\n"); - exit (0); -} diff --git a/tests/tcg/cris/libc/check_swap.c b/tests/tcg/cris/libc/check_swap.c deleted file mode 100644 index 9a68c1e..0000000 --- a/tests/tcg/cris/libc/check_swap.c +++ /dev/null @@ -1,76 +0,0 @@ -#include <stdio.h> -#include <stdlib.h> -#include <stdint.h> -#include "sys.h" -#include "crisutils.h" - -#define N 8 -#define W 4 -#define B 2 -#define R 1 - -static always_inline int cris_swap(const int mode, int x) -{ - switch (mode) - { - case N: asm ("swapn\t%0\n" : "+r" (x) : "0" (x)); break; - case W: asm ("swapw\t%0\n" : "+r" (x) : "0" (x)); break; - case B: asm ("swapb\t%0\n" : "+r" (x) : "0" (x)); break; - case R: asm ("swapr\t%0\n" : "+r" (x) : "0" (x)); break; - case B|R: asm ("swapbr\t%0\n" : "+r" (x) : "0" (x)); break; - case W|R: asm ("swapwr\t%0\n" : "+r" (x) : "0" (x)); break; - case W|B: asm ("swapwb\t%0\n" : "+r" (x) : "0" (x)); break; - case W|B|R: asm ("swapwbr\t%0\n" : "+r" (x) : "0" (x)); break; - case N|R: asm ("swapnr\t%0\n" : "+r" (x) : "0" (x)); break; - case N|B: asm ("swapnb\t%0\n" : "+r" (x) : "0" (x)); break; - case N|B|R: asm ("swapnbr\t%0\n" : "+r" (x) : "0" (x)); break; - case N|W: asm ("swapnw\t%0\n" : "+r" (x) : "0" (x)); break; - default: - err(); - break; - } - return x; -} - -/* Made this a macro to be able to pick up the location of the errors. */ -#define verify_swap(mode, val, expected, n, z) \ -do { \ - int r; \ - cris_tst_cc_init(); \ - r = cris_swap(mode, val); \ - cris_tst_mov_cc(n, z); \ - if (r != expected) \ - err(); \ -} while(0) - -void check_swap(void) -{ - /* Some of these numbers are borrowed from GDB's cris sim - testsuite. */ - if (cris_swap(N, 0) != 0xffffffff) - err(); - if (cris_swap(W, 0x12345678) != 0x56781234) - err(); - if (cris_swap(B, 0x12345678) != 0x34127856) - err(); - - verify_swap(R, 0x78134452, 0x1ec8224a, 0, 0); - verify_swap(B, 0x78134452, 0x13785244, 0, 0); - verify_swap(B|R, 0x78134452, 0xc81e4a22, 1, 0); - verify_swap(W, 0x78134452, 0x44527813, 0, 0); - verify_swap(W|R, 0x78134452, 0x224a1ec8, 0, 0); - verify_swap(W|B|R, 0x78134452, 0x4a22c81e, 0, 0); - verify_swap(N, 0x78134452, 0x87ecbbad, 1, 0); - verify_swap(N|R, 0x78134452, 0xe137ddb5, 1, 0); - verify_swap(N|B, 0x78134452, 0xec87adbb, 1, 0); - verify_swap(N|B|R, 0x78134452, 0x37e1b5dd, 0, 0); - verify_swap(N|W, 0x78134452, 0xbbad87ec, 1, 0); - verify_swap(N|B|R, 0xffffffff, 0, 0, 1); -} - -int main(void) -{ - check_swap(); - pass(); - return 0; -} diff --git a/tests/tcg/cris/libc/check_time2.c b/tests/tcg/cris/libc/check_time2.c deleted file mode 100644 index 20b69b4..0000000 --- a/tests/tcg/cris/libc/check_time2.c +++ /dev/null @@ -1,18 +0,0 @@ -/* CB_SYS_time doesn't implement the Linux time syscall; the return - value isn't written to the argument. */ - -#include <time.h> -#include <stdio.h> -#include <stdlib.h> - -int -main (void) -{ - time_t x = (time_t) -1; - time_t t = time (&x); - - if (t == (time_t) -1 || t != x) - abort (); - printf ("pass\n"); - exit (0); -} diff --git a/tests/tcg/cris/libc/crisutils.h b/tests/tcg/cris/libc/crisutils.h deleted file mode 100644 index bbbe6c5..0000000 --- a/tests/tcg/cris/libc/crisutils.h +++ /dev/null @@ -1,76 +0,0 @@ -#ifndef CRISUTILS_H -#define CRISUTILS_H 1 - -static char *tst_cc_loc = NULL; - -#define cris_tst_cc_init() \ -do { tst_cc_loc = "test_cc failed at " CURRENT_LOCATION; } while(0) - -/* We need a real symbol to signal error. */ -void _err(void) { - if (!tst_cc_loc) - tst_cc_loc = "tst_cc_failed\n"; - _fail(tst_cc_loc); -} - -static always_inline void cris_tst_cc_n1(void) -{ - asm volatile ("bpl _err\n" - "nop\n"); -} -static always_inline void cris_tst_cc_n0(void) -{ - asm volatile ("bmi _err\n" - "nop\n"); -} - -static always_inline void cris_tst_cc_z1(void) -{ - asm volatile ("bne _err\n" - "nop\n"); -} -static always_inline void cris_tst_cc_z0(void) -{ - asm volatile ("beq _err\n" - "nop\n"); -} -static always_inline void cris_tst_cc_v1(void) -{ - asm volatile ("bvc _err\n" - "nop\n"); -} -static always_inline void cris_tst_cc_v0(void) -{ - asm volatile ("bvs _err\n" - "nop\n"); -} - -static always_inline void cris_tst_cc_c1(void) -{ - asm volatile ("bcc _err\n" - "nop\n"); -} -static always_inline void cris_tst_cc_c0(void) -{ - asm volatile ("bcs _err\n" - "nop\n"); -} - -static always_inline void cris_tst_mov_cc(int n, int z) -{ - if (n) cris_tst_cc_n1(); else cris_tst_cc_n0(); - if (z) cris_tst_cc_z1(); else cris_tst_cc_z0(); - asm volatile ("" : : "g" (_err)); -} - -static always_inline void cris_tst_cc(const int n, const int z, - const int v, const int c) -{ - if (n) cris_tst_cc_n1(); else cris_tst_cc_n0(); - if (z) cris_tst_cc_z1(); else cris_tst_cc_z0(); - if (v) cris_tst_cc_v1(); else cris_tst_cc_v0(); - if (c) cris_tst_cc_c1(); else cris_tst_cc_c0(); - asm volatile ("" : : "g" (_err)); -} - -#endif diff --git a/tests/tcg/cris/libc/sys.h b/tests/tcg/cris/libc/sys.h deleted file mode 100644 index 3dd47bb..0000000 --- a/tests/tcg/cris/libc/sys.h +++ /dev/null @@ -1,18 +0,0 @@ -#include <unistd.h> - -#define STRINGIFY(x) #x -#define TOSTRING(x) STRINGIFY(x) - -#define always_inline inline __attribute__((always_inline)) - -#define CURRENT_LOCATION __FILE__ ":" TOSTRING(__LINE__) - -#define err() \ -{ \ - _fail("at " CURRENT_LOCATION " "); \ -} - -#define mb() asm volatile ("" : : : "memory") - -void pass(void); -void _fail(char *reason); diff --git a/tests/tcg/i386/Makefile.target b/tests/tcg/i386/Makefile.target index bbe2c44..f1df404 100644 --- a/tests/tcg/i386/Makefile.target +++ b/tests/tcg/i386/Makefile.target @@ -22,7 +22,7 @@ run-test-i386-sse-exceptions: QEMU_OPTS += -cpu max test-i386-pcmpistri: CFLAGS += -msse4.2 run-test-i386-pcmpistri: QEMU_OPTS += -cpu max -test-i386-bmi2: CFLAGS=-O2 +test-i386-bmi2: CFLAGS=-O2 -fwrapv run-test-i386-bmi2: QEMU_OPTS += -cpu max test-i386-adcox: CFLAGS=-O2 diff --git a/tests/tcg/i386/test-avx.c b/tests/tcg/i386/test-avx.c index 230e6d8..80fe363 100644 --- a/tests/tcg/i386/test-avx.c +++ b/tests/tcg/i386/test-avx.c @@ -244,7 +244,7 @@ v4di indexd = {0x00000002ffffffcdull, 0xfffffff500000010ull, 0x0000003afffffff0ull, 0x000000000000000eull}; v4di gather_mem[0x20]; -_Static_assert(sizeof(gather_mem) == 1024); +_Static_assert(sizeof(gather_mem) == 1024, "gather_mem not expected size"); void init_f16reg(v4di *r) { diff --git a/tests/tcg/i386/test-i386-adcox.c b/tests/tcg/i386/test-i386-adcox.c index 16169ef..a717064 100644 --- a/tests/tcg/i386/test-i386-adcox.c +++ b/tests/tcg/i386/test-i386-adcox.c @@ -29,7 +29,7 @@ void test_adox_adcx(uint32_t in_c, uint32_t in_o, REG adcx_operand, REG adox_ope "adcx %3, %1;" "pushf; pop %0" : "+r" (flags), "+r" (out_adcx), "+r" (out_adox) - : "r" ((REG)-1), "0" (flags), "1" (out_adcx), "2" (out_adox)); + : "r" ((REG) - 1), "0" (flags), "1" (out_adcx), "2" (out_adox)); assert(out_adcx == in_c + adcx_operand - 1); assert(out_adox == in_o + adox_operand - 1); @@ -53,8 +53,8 @@ void test_adcx_adox(uint32_t in_c, uint32_t in_o, REG adcx_operand, REG adox_ope "adcx %3, %1;" "adox %3, %2;" "pushf; pop %0" - : "+r" (flags), "+r" (out_adcx), "+r" (out_adox) - : "r" ((REG)-1), "0" (flags), "1" (out_adcx), "2" (out_adox)); + : "+r"(flags), "+r"(out_adcx), "+r"(out_adox) + : "r" ((REG)-1)); assert(out_adcx == in_c + adcx_operand - 1); assert(out_adox == in_o + adox_operand - 1); diff --git a/tests/tcg/loongarch64/system/kernel.ld b/tests/tcg/loongarch64/system/kernel.ld index f1a7c01..56d8588 100644 --- a/tests/tcg/loongarch64/system/kernel.ld +++ b/tests/tcg/loongarch64/system/kernel.ld @@ -3,7 +3,7 @@ ENTRY(_start) SECTIONS { /* Linux kernel legacy start address. */ - . = 0x9000000000200000; + . = 0x200000; _text = .; .text : { *(.text) diff --git a/tests/tcg/loongarch64/system/regdef.h b/tests/tcg/loongarch64/system/regdef.h index faa09b2..b586b4e 100644 --- a/tests/tcg/loongarch64/system/regdef.h +++ b/tests/tcg/loongarch64/system/regdef.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: GPL-2.0 */ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright (c) 2021 Loongson Technology Corporation Limited */ diff --git a/tests/tcg/multiarch/Makefile.target b/tests/tcg/multiarch/Makefile.target index 5e3391e..45c9cfe 100644 --- a/tests/tcg/multiarch/Makefile.target +++ b/tests/tcg/multiarch/Makefile.target @@ -42,6 +42,17 @@ munmap-pthread: LDFLAGS+=-pthread vma-pthread: CFLAGS+=-pthread vma-pthread: LDFLAGS+=-pthread +sigreturn-sigmask: CFLAGS+=-pthread +sigreturn-sigmask: LDFLAGS+=-pthread + +# GCC versions 12/13/14/15 at least incorrectly complain about +# "'SHA1Transform' reading 64 bytes from a region of size 0"; see the gcc bug +# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=106709 +# Since this is just a standard piece of library code we've borrowed for a +# TCG test case, suppress the warning rather than trying to modify the +# code to work around the compiler. +sha1: CFLAGS+=-Wno-stringop-overread -Wno-unknown-warning-option + # The vma-pthread seems very sensitive on gitlab and we currently # don't know if its exposing a real bug or the test is flaky. ifneq ($(GITLAB_CI),) @@ -127,6 +138,13 @@ run-gdbstub-follow-fork-mode-parent: follow-fork-mode --bin $< --test $(MULTIARCH_SRC)/gdbstub/follow-fork-mode-parent.py, \ following parents on fork) +run-gdbstub-late-attach: late-attach + $(call run-test, $@, env LATE_ATTACH_PY=1 $(GDB_SCRIPT) \ + --gdb $(GDB) \ + --qemu $(QEMU) --qargs "$(QEMU_OPTS)" --no-suspend \ + --bin $< --test $(MULTIARCH_SRC)/gdbstub/late-attach.py, \ + attaching to a running process) + else run-gdbstub-%: $(call skip-test, "gdbstub test $*", "need working gdb with $(patsubst -%,,$(TARGET_NAME)) support") @@ -136,7 +154,7 @@ EXTRA_RUNS += run-gdbstub-sha1 run-gdbstub-qxfer-auxv-read \ run-gdbstub-registers run-gdbstub-prot-none \ run-gdbstub-catch-syscalls run-gdbstub-follow-fork-mode-child \ run-gdbstub-follow-fork-mode-parent \ - run-gdbstub-qxfer-siginfo-read + run-gdbstub-qxfer-siginfo-read run-gdbstub-late-attach # ARM Compatible Semi Hosting Tests # @@ -170,5 +188,16 @@ run-plugin-semiconsole-with-%: TESTS += semihosting semiconsole endif +# Test plugin memory access instrumentation +run-plugin-test-plugin-mem-access-with-libmem.so: \ + PLUGIN_ARGS=$(COMMA)print-accesses=true +run-plugin-test-plugin-mem-access-with-libmem.so: \ + CHECK_PLUGIN_OUTPUT_COMMAND= \ + $(SRC_PATH)/tests/tcg/multiarch/check-plugin-output.sh \ + $(QEMU) $< + +test-plugin-mem-access: CFLAGS+=-pthread -O0 +test-plugin-mem-access: LDFLAGS+=-pthread -O0 + # Update TESTS TESTS += $(MULTIARCH_TESTS) diff --git a/tests/tcg/multiarch/check-plugin-output.sh b/tests/tcg/multiarch/check-plugin-output.sh new file mode 100755 index 0000000..80607f0 --- /dev/null +++ b/tests/tcg/multiarch/check-plugin-output.sh @@ -0,0 +1,36 @@ +#!/usr/bin/env bash + +# This script runs a given executable using qemu, and compare its standard +# output with an expected plugin output. +# Each line of output is searched (as a regexp) in the expected plugin output. + +set -euo pipefail + +die() +{ + echo "$@" 1>&2 + exit 1 +} + +check() +{ + file=$1 + pattern=$2 + grep "$pattern" "$file" > /dev/null || die "\"$pattern\" not found in $file" +} + +[ $# -eq 3 ] || die "usage: qemu_bin exe plugin_out_file" + +qemu_bin=$1; shift +exe=$1;shift +plugin_out=$1; shift + +expected() +{ + $qemu_bin $exe || + die "running $exe failed" +} + +expected | while read line; do + check "$plugin_out" "$line" +done diff --git a/tests/tcg/multiarch/gdbstub/interrupt.py b/tests/tcg/multiarch/gdbstub/interrupt.py index 90a45b5..2d5654d 100644 --- a/tests/tcg/multiarch/gdbstub/interrupt.py +++ b/tests/tcg/multiarch/gdbstub/interrupt.py @@ -8,7 +8,7 @@ from __future__ import print_function # import gdb -from test_gdbstub import main, report +from test_gdbstub import gdb_exit, main, report def check_interrupt(thread): @@ -49,7 +49,7 @@ def run_test(): """ if len(gdb.selected_inferior().threads()) == 1: print("SKIP: set to run on a single thread") - exit(0) + gdb_exit(0) gdb.execute("set scheduler-locking on") for thread in gdb.selected_inferior().threads(): diff --git a/tests/tcg/multiarch/gdbstub/late-attach.py b/tests/tcg/multiarch/gdbstub/late-attach.py new file mode 100644 index 0000000..1d40efb --- /dev/null +++ b/tests/tcg/multiarch/gdbstub/late-attach.py @@ -0,0 +1,28 @@ +"""Test attaching GDB to a running process. + +SPDX-License-Identifier: GPL-2.0-or-later +""" +from test_gdbstub import main, report + + +def run_test(): + """Run through the tests one by one""" + try: + phase = gdb.parse_and_eval("phase").string() + except gdb.error: + # Assume the guest did not reach main(). + phase = "start" + + if phase == "start": + gdb.execute("break sigwait") + gdb.execute("continue") + phase = gdb.parse_and_eval("phase").string() + report(phase == "sigwait", "{} == \"sigwait\"".format(phase)) + + gdb.execute("signal SIGUSR1") + + exitcode = int(gdb.parse_and_eval("$_exitcode")) + report(exitcode == 0, "{} == 0".format(exitcode)) + + +main(run_test) diff --git a/tests/tcg/multiarch/gdbstub/prot-none.py b/tests/tcg/multiarch/gdbstub/prot-none.py index 7e26458..51082a3 100644 --- a/tests/tcg/multiarch/gdbstub/prot-none.py +++ b/tests/tcg/multiarch/gdbstub/prot-none.py @@ -5,7 +5,7 @@ This runs as a sourced script (via -x, via run-test.py). SPDX-License-Identifier: GPL-2.0-or-later """ import ctypes -from test_gdbstub import main, report +from test_gdbstub import gdb_exit, main, report def probe_proc_self_mem(): @@ -22,7 +22,7 @@ def run_test(): """Run through the tests one by one""" if not probe_proc_self_mem(): print("SKIP: /proc/self/mem is not usable") - exit(0) + gdb_exit(0) gdb.Breakpoint("break_here") gdb.execute("continue") val = gdb.parse_and_eval("*(char[2] *)q").string() diff --git a/tests/tcg/multiarch/gdbstub/test-proc-mappings.py b/tests/tcg/multiarch/gdbstub/test-proc-mappings.py index 564613f..6eb6ebf 100644 --- a/tests/tcg/multiarch/gdbstub/test-proc-mappings.py +++ b/tests/tcg/multiarch/gdbstub/test-proc-mappings.py @@ -3,22 +3,17 @@ This runs as a sourced script (via -x, via run-test.py).""" from __future__ import print_function import gdb -from test_gdbstub import main, report +from test_gdbstub import gdb_exit, main, report def run_test(): """Run through the tests one by one""" - try: - mappings = gdb.execute("info proc mappings", False, True) - except gdb.error as exc: - exc_str = str(exc) - if "Not supported on this target." in exc_str: - # Detect failures due to an outstanding issue with how GDB handles - # the x86_64 QEMU's target.xml, which does not contain the - # definition of orig_rax. Skip the test in this case. - print("SKIP: {}".format(exc_str)) - return - raise + if gdb.selected_inferior().architecture().name() == "m68k": + # m68k GDB supports only GDB_OSABI_SVR4, but GDB_OSABI_LINUX is + # required for the info proc support (see set_gdbarch_info_proc()). + print("SKIP: m68k GDB does not support GDB_OSABI_LINUX") + gdb_exit(0) + mappings = gdb.execute("info proc mappings", False, True) report(isinstance(mappings, str), "Fetched the mappings from the inferior") # Broken with host page size > guest page size # report("/sha1" in mappings, "Found the test binary name in the mappings") diff --git a/tests/tcg/multiarch/late-attach.c b/tests/tcg/multiarch/late-attach.c new file mode 100644 index 0000000..20a3640 --- /dev/null +++ b/tests/tcg/multiarch/late-attach.c @@ -0,0 +1,41 @@ +/* + * Test attaching GDB to a running process. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#include <assert.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> + +static const char *phase = "start"; + +int main(void) +{ + sigset_t set; + int sig; + + assert(sigfillset(&set) == 0); + assert(sigprocmask(SIG_BLOCK, &set, NULL) == 0); + + /* Let GDB know it can send SIGUSR1. */ + phase = "sigwait"; + if (getenv("LATE_ATTACH_PY")) { + assert(sigwait(&set, &sig) == 0); + if (sig != SIGUSR1) { + fprintf(stderr, "Unexpected signal %d\n", sig); + return EXIT_FAILURE; + } + } + + /* Check that the guest does not see host_interrupt_signal. */ + assert(sigpending(&set) == 0); + for (sig = 1; sig < NSIG; sig++) { + if (sigismember(&set, sig)) { + fprintf(stderr, "Unexpected signal %d\n", sig); + return EXIT_FAILURE; + } + } + + return EXIT_SUCCESS; +} diff --git a/tests/tcg/multiarch/linux/linux-sigrtminmax.c b/tests/tcg/multiarch/linux/linux-sigrtminmax.c new file mode 100644 index 0000000..a7059aa --- /dev/null +++ b/tests/tcg/multiarch/linux/linux-sigrtminmax.c @@ -0,0 +1,74 @@ +/* + * Test the lowest and the highest real-time signals. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#include <assert.h> +#include <signal.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +/* For hexagon and microblaze. */ +#ifndef __SIGRTMIN +#define __SIGRTMIN 32 +#endif + +extern char **environ; + +static bool seen_sigrtmin, seen_sigrtmax; + +static void handle_signal(int sig) +{ + if (sig == SIGRTMIN) { + seen_sigrtmin = true; + } else if (sig == SIGRTMAX) { + seen_sigrtmax = true; + } else { + _exit(1); + } +} + +int main(int argc, char **argv) +{ + char *qemu = getenv("QEMU"); + struct sigaction act; + + assert(qemu); + + if (!getenv("QEMU_RTSIG_MAP")) { + char **new_argv = malloc((argc + 2) + sizeof(char *)); + int tsig1, hsig1, count1, tsig2, hsig2, count2; + char rt_sigmap[64]; + + /* Re-exec with a mapping that includes SIGRTMIN and SIGRTMAX. */ + new_argv[0] = qemu; + memcpy(&new_argv[1], argv, (argc + 1) * sizeof(char *)); + tsig1 = __SIGRTMIN; + /* The host must have a few signals starting from this one. */ + hsig1 = 36; + count1 = SIGRTMIN - __SIGRTMIN + 1; + tsig2 = SIGRTMAX; + hsig2 = hsig1 + count1; + count2 = 1; + snprintf(rt_sigmap, sizeof(rt_sigmap), "%d %d %d,%d %d %d", + tsig1, hsig1, count1, tsig2, hsig2, count2); + setenv("QEMU_RTSIG_MAP", rt_sigmap, 0); + assert(execve(new_argv[0], new_argv, environ) == 0); + return EXIT_FAILURE; + } + + memset(&act, 0, sizeof(act)); + act.sa_handler = handle_signal; + assert(sigaction(SIGRTMIN, &act, NULL) == 0); + assert(sigaction(SIGRTMAX, &act, NULL) == 0); + + assert(kill(getpid(), SIGRTMIN) == 0); + assert(seen_sigrtmin); + assert(kill(getpid(), SIGRTMAX) == 0); + assert(seen_sigrtmax); + + return EXIT_SUCCESS; +} diff --git a/tests/tcg/multiarch/test-vma.c b/tests/tcg/multiarch/linux/test-vma.c index 2893d60..2893d60 100644 --- a/tests/tcg/multiarch/test-vma.c +++ b/tests/tcg/multiarch/linux/test-vma.c diff --git a/tests/tcg/multiarch/sigreturn-sigmask.c b/tests/tcg/multiarch/sigreturn-sigmask.c new file mode 100644 index 0000000..e6cc904 --- /dev/null +++ b/tests/tcg/multiarch/sigreturn-sigmask.c @@ -0,0 +1,51 @@ +/* + * Test that sigreturn() does not corrupt the signal mask. + * Block SIGUSR2 and handle SIGUSR1. + * Then sigwait() SIGUSR2, which relies on it remaining blocked. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#include <assert.h> +#include <pthread.h> +#include <signal.h> +#include <stdlib.h> +#include <unistd.h> + +int seen_sig = -1; + +static void signal_func(int sig) +{ + seen_sig = sig; +} + +static void *thread_func(void *arg) +{ + kill(getpid(), SIGUSR2); + return NULL; +} + +int main(void) +{ + struct sigaction act = { + .sa_handler = signal_func, + }; + pthread_t thread; + sigset_t set; + int sig; + + assert(sigaction(SIGUSR1, &act, NULL) == 0); + + assert(sigemptyset(&set) == 0); + assert(sigaddset(&set, SIGUSR2) == 0); + assert(sigprocmask(SIG_BLOCK, &set, NULL) == 0); + + kill(getpid(), SIGUSR1); + assert(seen_sig == SIGUSR1); + + assert(pthread_create(&thread, NULL, thread_func, NULL) == 0); + assert(sigwait(&set, &sig) == 0); + assert(sig == SIGUSR2); + assert(pthread_join(thread, NULL) == 0); + + return EXIT_SUCCESS; +} diff --git a/tests/tcg/multiarch/system/Makefile.softmmu-target b/tests/tcg/multiarch/system/Makefile.softmmu-target index 32dc0f9..07be001 100644 --- a/tests/tcg/multiarch/system/Makefile.softmmu-target +++ b/tests/tcg/multiarch/system/Makefile.softmmu-target @@ -65,3 +65,9 @@ endif MULTIARCH_RUNS += run-gdbstub-memory run-gdbstub-interrupt \ run-gdbstub-untimely-packet run-gdbstub-registers + +# Test plugin memory access instrumentation +run-plugin-memory-with-libmem.so: \ + PLUGIN_ARGS=$(COMMA)region-summary=true +run-plugin-memory-with-libmem.so: \ + CHECK_PLUGIN_OUTPUT_COMMAND=$(MULTIARCH_SYSTEM_SRC)/validate-memory-counts.py $@.out diff --git a/tests/tcg/multiarch/system/memory.c b/tests/tcg/multiarch/system/memory.c index 6eb2eb1..7508f6b 100644 --- a/tests/tcg/multiarch/system/memory.c +++ b/tests/tcg/multiarch/system/memory.c @@ -20,20 +20,28 @@ # error "Target does not specify CHECK_UNALIGNED" #endif +uint32_t test_read_count; +uint32_t test_write_count; + #define MEM_PAGE_SIZE 4096 /* nominal 4k "pages" */ #define TEST_SIZE (MEM_PAGE_SIZE * 4) /* 4 pages */ #define ARRAY_SIZE(x) ((sizeof(x) / sizeof((x)[0]))) -__attribute__((aligned(MEM_PAGE_SIZE))) +__attribute__((aligned(TEST_SIZE))) static uint8_t test_data[TEST_SIZE]; typedef void (*init_ufn) (int offset); typedef bool (*read_ufn) (int offset); typedef bool (*read_sfn) (int offset, bool nf); -static void pdot(int count) +static void pdot(int count, bool write) { + if (write) { + test_write_count++; + } else { + test_read_count++; + } if (count % 128 == 0) { ml_printf("."); } @@ -63,12 +71,14 @@ static void init_test_data_u8(int unused_offset) int i; (void)(unused_offset); - ml_printf("Filling test area with u8:"); + ml_printf("Filling test area with u8 (%p):", ptr); + for (i = 0; i < TEST_SIZE; i++) { *ptr++ = BYTE_NEXT(count); - pdot(i); + pdot(i, true); } - ml_printf("done\n"); + + ml_printf("done %d @ %p\n", i, ptr); } /* @@ -91,10 +101,11 @@ static void init_test_data_s8(bool neg_first) neg_first ? "neg first" : "pos first"); for (i = 0; i < TEST_SIZE / 2; i++) { *ptr++ = get_byte(i, neg_first); + pdot(i, true); *ptr++ = get_byte(i, !neg_first); - pdot(i); + pdot(i, true); } - ml_printf("done\n"); + ml_printf("done %d @ %p\n", i * 2, ptr); } /* @@ -105,9 +116,19 @@ static void reset_start_data(int offset) { uint32_t *ptr = (uint32_t *) &test_data[0]; int i; + + if (!offset) { + return; + } + + ml_printf("Flushing %d bytes from %p: ", offset, ptr); + for (i = 0; i < offset; i++) { *ptr++ = 0; + pdot(i, true); } + + ml_printf("done %d @ %p\n", i, ptr); } static void init_test_data_u16(int offset) @@ -117,17 +138,17 @@ static void init_test_data_u16(int offset) const int max = (TEST_SIZE - offset) / sizeof(word); int i; - ml_printf("Filling test area with u16 (offset %d, %p):", offset, ptr); - reset_start_data(offset); + ml_printf("Filling test area with u16 (offset %d, %p):", offset, ptr); + for (i = 0; i < max; i++) { uint16_t low = BYTE_NEXT(count), high = BYTE_NEXT(count); word = BYTE_SHIFT(high, 1) | BYTE_SHIFT(low, 0); *ptr++ = word; - pdot(i); + pdot(i, true); } - ml_printf("done @ %p\n", ptr); + ml_printf("done %d @ %p\n", i, ptr); } static void init_test_data_u32(int offset) @@ -137,21 +158,22 @@ static void init_test_data_u32(int offset) const int max = (TEST_SIZE - offset) / sizeof(word); int i; - ml_printf("Filling test area with u32 (offset %d, %p):", offset, ptr); - reset_start_data(offset); + ml_printf("Filling test area with u32 (offset %d, %p):", offset, ptr); + for (i = 0; i < max; i++) { uint32_t b4 = BYTE_NEXT(count), b3 = BYTE_NEXT(count); uint32_t b2 = BYTE_NEXT(count), b1 = BYTE_NEXT(count); word = BYTE_SHIFT(b1, 3) | BYTE_SHIFT(b2, 2) | BYTE_SHIFT(b3, 1) | BYTE_SHIFT(b4, 0); *ptr++ = word; - pdot(i); + pdot(i, true); } - ml_printf("done @ %p\n", ptr); + ml_printf("done %d @ %p\n", i, ptr); } +#if __SIZEOF_POINTER__ >= 8 static void init_test_data_u64(int offset) { uint8_t count = 0; @@ -159,10 +181,10 @@ static void init_test_data_u64(int offset) const int max = (TEST_SIZE - offset) / sizeof(word); int i; - ml_printf("Filling test area with u64 (offset %d, %p):", offset, ptr); - reset_start_data(offset); + ml_printf("Filling test area with u64 (offset %d, %p):", offset, ptr); + for (i = 0; i < max; i++) { uint64_t b8 = BYTE_NEXT(count), b7 = BYTE_NEXT(count); uint64_t b6 = BYTE_NEXT(count), b5 = BYTE_NEXT(count); @@ -172,10 +194,11 @@ static void init_test_data_u64(int offset) BYTE_SHIFT(b4, 4) | BYTE_SHIFT(b5, 3) | BYTE_SHIFT(b6, 2) | BYTE_SHIFT(b7, 1) | BYTE_SHIFT(b8, 0); *ptr++ = word; - pdot(i); + pdot(i, true); } - ml_printf("done @ %p\n", ptr); + ml_printf("done %d @ %p\n", i, ptr); } +#endif static bool read_test_data_u16(int offset) { @@ -194,11 +217,11 @@ static bool read_test_data_u16(int offset) ml_printf("Error %d < %d\n", high, low); return false; } else { - pdot(i); + pdot(i, false); } } - ml_printf("done @ %p\n", ptr); + ml_printf("done %d @ %p\n", i, ptr); return true; } @@ -236,13 +259,14 @@ static bool read_test_data_u32(int offset) ml_printf("Error %d, %d, %d, %d", b1, b2, b3, b4); return false; } else { - pdot(i); + pdot(i, false); } } - ml_printf("done @ %p\n", ptr); + ml_printf("done %d @ %p\n", i, ptr); return true; } +#if __SIZEOF_POINTER__ >= 8 static bool read_test_data_u64(int offset) { uint64_t word, *ptr = (uint64_t *)&test_data[offset]; @@ -290,17 +314,22 @@ static bool read_test_data_u64(int offset) b1, b2, b3, b4, b5, b6, b7, b8); return false; } else { - pdot(i); + pdot(i, false); } } - ml_printf("done @ %p\n", ptr); + ml_printf("done %d @ %p\n", i, ptr); return true; } +#endif /* Read the test data and verify at various offsets */ -read_ufn read_ufns[] = { read_test_data_u16, - read_test_data_u32, - read_test_data_u64 }; +read_ufn read_ufns[] = { + read_test_data_u16, + read_test_data_u32, +#if __SIZEOF_POINTER__ >= 8 + read_test_data_u64 +#endif +}; bool do_unsigned_reads(int start_off) { @@ -357,15 +386,17 @@ static bool read_test_data_s8(int offset, bool neg_first) second = *ptr++; if (neg_first && first < 0 && second > 0) { - pdot(i); + pdot(i, false); + pdot(i, false); } else if (!neg_first && first > 0 && second < 0) { - pdot(i); + pdot(i, false); + pdot(i, false); } else { ml_printf("Error %d %c %d\n", first, neg_first ? '<' : '>', second); return false; } } - ml_printf("done @ %p\n", ptr); + ml_printf("done %d @ %p\n", i * 2, ptr); return true; } @@ -390,15 +421,15 @@ static bool read_test_data_s16(int offset, bool neg_first) int32_t data = *ptr++; if (neg_first && data < 0) { - pdot(i); + pdot(i, false); } else if (!neg_first && data > 0) { - pdot(i); + pdot(i, false); } else { ml_printf("Error %d %c 0\n", data, neg_first ? '<' : '>'); return false; } } - ml_printf("done @ %p\n", ptr); + ml_printf("done %d @ %p\n", i, ptr); return true; } @@ -423,15 +454,15 @@ static bool read_test_data_s32(int offset, bool neg_first) int64_t data = *ptr++; if (neg_first && data < 0) { - pdot(i); + pdot(i, false); } else if (!neg_first && data > 0) { - pdot(i); + pdot(i, false); } else { ml_printf("Error %d %c 0\n", data, neg_first ? '<' : '>'); return false; } } - ml_printf("done @ %p\n", ptr); + ml_printf("done %d @ %p\n", i, ptr); return true; } @@ -465,16 +496,23 @@ bool do_signed_reads(bool neg_first) return ok; } -init_ufn init_ufns[] = { init_test_data_u8, - init_test_data_u16, - init_test_data_u32, - init_test_data_u64 }; +init_ufn init_ufns[] = { + init_test_data_u8, + init_test_data_u16, + init_test_data_u32, +#if __SIZEOF_POINTER__ >= 8 + init_test_data_u64 +#endif +}; int main(void) { int i; bool ok = true; + ml_printf("Test data start: 0x%lx\n", (unsigned long)&test_data[0]); + ml_printf("Test data end: 0x%lx\n", (unsigned long)&test_data[TEST_SIZE]); + /* Run through the unsigned tests first */ for (i = 0; i < ARRAY_SIZE(init_ufns) && ok; i++) { ok = do_unsigned_test(init_ufns[i]); @@ -490,6 +528,8 @@ int main(void) ok = do_signed_reads(true); } + ml_printf("Test data read: %lu\n", (unsigned long)test_read_count); + ml_printf("Test data write: %lu\n", (unsigned long)test_write_count); ml_printf("Test complete: %s\n", ok ? "PASSED" : "FAILED"); return ok ? 0 : -1; } diff --git a/tests/tcg/multiarch/system/validate-memory-counts.py b/tests/tcg/multiarch/system/validate-memory-counts.py new file mode 100755 index 0000000..5b8bbf3 --- /dev/null +++ b/tests/tcg/multiarch/system/validate-memory-counts.py @@ -0,0 +1,130 @@ +#!/usr/bin/env python3 +# +# validate-memory-counts.py: check we instrumented memory properly +# +# This program takes two inputs: +# - the mem plugin output +# - the memory binary output +# +# Copyright (C) 2024 Linaro Ltd +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import sys +from argparse import ArgumentParser + +def extract_counts(path): + """ + Load the output from path and extract the lines containing: + + Test data start: 0x40214000 + Test data end: 0x40218001 + Test data read: 2522280 + Test data write: 262111 + + From the stream of data. Extract the values for use in the + validation function. + """ + start_address = None + end_address = None + read_count = 0 + write_count = 0 + with open(path, 'r') as f: + for line in f: + if line.startswith("Test data start:"): + start_address = int(line.split(':')[1].strip(), 16) + elif line.startswith("Test data end:"): + end_address = int(line.split(':')[1].strip(), 16) + elif line.startswith("Test data read:"): + read_count = int(line.split(':')[1].strip()) + elif line.startswith("Test data write:"): + write_count = int(line.split(':')[1].strip()) + return start_address, end_address, read_count, write_count + + +def parse_plugin_output(path, start, end): + """ + Load the plugin output from path in the form of: + + Region Base, Reads, Writes, Seen all + 0x0000000040004000, 31093, 0, false + 0x0000000040214000, 2522280, 278579, true + 0x0000000040000000, 137398, 0, false + 0x0000000040210000, 54727397, 33721956, false + + And extract the ranges that match test data start and end and + return the results. + """ + total_reads = 0 + total_writes = 0 + seen_all = False + + with open(path, 'r') as f: + next(f) # Skip the header + for line in f: + + if line.startswith("Region Base"): + continue + + parts = line.strip().split(', ') + if len(parts) != 4: + continue + + region_base = int(parts[0], 16) + reads = int(parts[1]) + writes = int(parts[2]) + + if start <= region_base < end: # Checking if within range + total_reads += reads + total_writes += writes + seen_all = parts[3] == "true" + + return total_reads, total_writes, seen_all + +def main() -> None: + """ + Process the arguments, injest the program and plugin out and + verify they match up and report if they do not. + """ + parser = ArgumentParser(description="Validate memory instrumentation") + parser.add_argument('test_output', + help="The output from the test itself") + parser.add_argument('plugin_output', + help="The output from memory plugin") + parser.add_argument('--bss-cleared', + action='store_true', + help='Assume bss was cleared (and adjusts counts).') + + args = parser.parse_args() + + # Extract counts from memory binary + start, end, exp_reads, exp_writes = extract_counts(args.test_output) + + # Some targets clear BSS before running but the test doesn't know + # that so we adjust it by the size of the test region. + if args.bss_cleared: + exp_writes += 16384 + + if start is None or end is None: + print("Failed to test_data boundaries from output.") + sys.exit(1) + + # Parse plugin output + preads, pwrites, seen_all = parse_plugin_output(args.plugin_output, + start, end) + + if not seen_all: + print("Fail: didn't instrument all accesses to test_data.") + sys.exit(1) + + # Compare and report + if preads == exp_reads and pwrites == exp_writes: + sys.exit(0) + else: + print("Fail: The memory reads and writes count does not match.") + print(f"Expected Reads: {exp_reads}, Actual Reads: {preads}") + print(f"Expected Writes: {exp_writes}, Actual Writes: {pwrites}") + sys.exit(1) + +if __name__ == "__main__": + main() diff --git a/tests/tcg/multiarch/test-plugin-mem-access.c b/tests/tcg/multiarch/test-plugin-mem-access.c new file mode 100644 index 0000000..057b9aa --- /dev/null +++ b/tests/tcg/multiarch/test-plugin-mem-access.c @@ -0,0 +1,177 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * + * Check if we detect all memory accesses expected using plugin API. + * Used in conjunction with ./check-plugin-mem-access.sh check script. + * Output of this program is the list of patterns expected in plugin output. + * + * 8,16,32 load/store are tested for all arch. + * 64,128 load/store are tested for aarch64/x64. + * atomic operations (8,16,32,64) are tested for x64 only. + */ + +#include <pthread.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> + +#if defined(__x86_64__) +#include <emmintrin.h> +#elif defined(__aarch64__) +#include <arm_neon.h> +#endif /* __x86_64__ */ + +static void *data; + +/* ,store_u8,.*,8,store,0xf1 */ +#define PRINT_EXPECTED(function, type, value, action) \ +do { \ + printf(",%s,.*,%d,%s,%s\n", \ + #function, (int) sizeof(type) * 8, action, value); \ +} \ +while (0) + +#define DEFINE_STORE(name, type, value) \ + \ +static void print_expected_store_##name(void) \ +{ \ + PRINT_EXPECTED(store_##name, type, #value, "store"); \ +} \ + \ +static void store_##name(void) \ +{ \ + *((type *)data) = value; \ + print_expected_store_##name(); \ +} + +#define DEFINE_ATOMIC_OP(name, type, value) \ + \ +static void print_expected_atomic_op_##name(void) \ +{ \ + PRINT_EXPECTED(atomic_op_##name, type, "0x0*42", "load"); \ + PRINT_EXPECTED(atomic_op_##name, type, #value, "store"); \ +} \ + \ +static void atomic_op_##name(void) \ +{ \ + *((type *)data) = 0x42; \ + __sync_val_compare_and_swap((type *)data, 0x42, value); \ + print_expected_atomic_op_##name(); \ +} + +#define DEFINE_LOAD(name, type, value) \ + \ +static void print_expected_load_##name(void) \ +{ \ + PRINT_EXPECTED(load_##name, type, #value, "load"); \ +} \ + \ +static void load_##name(void) \ +{ \ + \ + /* volatile forces load to be generated. */ \ + volatile type src = *((type *) data); \ + volatile type dest = src; \ + (void)src, (void)dest; \ + print_expected_load_##name(); \ +} + +DEFINE_STORE(u8, uint8_t, 0xf1) +DEFINE_LOAD(u8, uint8_t, 0xf1) +DEFINE_STORE(u16, uint16_t, 0xf123) +DEFINE_LOAD(u16, uint16_t, 0xf123) +DEFINE_STORE(u32, uint32_t, 0xff112233) +DEFINE_LOAD(u32, uint32_t, 0xff112233) + +#if defined(__x86_64__) || defined(__aarch64__) +DEFINE_STORE(u64, uint64_t, 0xf123456789abcdef) +DEFINE_LOAD(u64, uint64_t, 0xf123456789abcdef) + +static void print_expected_store_u128(void) +{ + PRINT_EXPECTED(store_u128, __int128, + "0xf122334455667788f123456789abcdef", "store"); +} + +static void store_u128(void) +{ +#ifdef __x86_64__ + _mm_store_si128(data, _mm_set_epi32(0xf1223344, 0x55667788, + 0xf1234567, 0x89abcdef)); +#else + const uint32_t init[4] = {0x89abcdef, 0xf1234567, 0x55667788, 0xf1223344}; + uint32x4_t vec = vld1q_u32(init); + vst1q_u32(data, vec); +#endif /* __x86_64__ */ + print_expected_store_u128(); +} + +static void print_expected_load_u128(void) +{ + PRINT_EXPECTED(load_u128, __int128, + "0xf122334455667788f123456789abcdef", "load"); +} + +static void load_u128(void) +{ +#ifdef __x86_64__ + __m128i var = _mm_load_si128(data); +#else + uint32x4_t var = vld1q_u32(data); +#endif + (void) var; + print_expected_load_u128(); +} +#endif /* __x86_64__ || __aarch64__ */ + +#if defined(__x86_64__) +DEFINE_ATOMIC_OP(u8, uint8_t, 0xf1) +DEFINE_ATOMIC_OP(u16, uint16_t, 0xf123) +DEFINE_ATOMIC_OP(u32, uint32_t, 0xff112233) +DEFINE_ATOMIC_OP(u64, uint64_t, 0xf123456789abcdef) +#endif /* __x86_64__ */ + +static void *f(void *p) +{ + return NULL; +} + +int main(void) +{ + /* + * We force creation of a second thread to enable cpu flag CF_PARALLEL. + * This will generate atomic operations when needed. + */ + pthread_t thread; + pthread_create(&thread, NULL, &f, NULL); + pthread_join(thread, NULL); + + /* allocate storage up to 128 bits */ + data = malloc(16); + + store_u8(); + load_u8(); + + store_u16(); + load_u16(); + + store_u32(); + load_u32(); + +#if defined(__x86_64__) || defined(__aarch64__) + store_u64(); + load_u64(); + + store_u128(); + load_u128(); +#endif /* __x86_64__ || __aarch64__ */ + +#if defined(__x86_64__) + atomic_op_u8(); + atomic_op_u16(); + atomic_op_u32(); + atomic_op_u64(); +#endif /* __x86_64__ */ + + free(data); +} diff --git a/tests/tcg/plugins/insn.c b/tests/tcg/plugins/insn.c index baf2d07..0c723cb 100644 --- a/tests/tcg/plugins/insn.c +++ b/tests/tcg/plugins/insn.c @@ -150,10 +150,8 @@ static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) qemu_plugin_register_vcpu_insn_exec_inline_per_vcpu( insn, QEMU_PLUGIN_INLINE_ADD_U64, insn_count, 1); } else { - uint64_t vaddr = qemu_plugin_insn_vaddr(insn); qemu_plugin_register_vcpu_insn_exec_cb( - insn, vcpu_insn_exec_before, QEMU_PLUGIN_CB_NO_REGS, - GUINT_TO_POINTER(vaddr)); + insn, vcpu_insn_exec_before, QEMU_PLUGIN_CB_NO_REGS, NULL); } if (do_size) { diff --git a/tests/tcg/plugins/mem.c b/tests/tcg/plugins/mem.c index b650ddd..ca4e888 100644 --- a/tests/tcg/plugins/mem.c +++ b/tests/tcg/plugins/mem.c @@ -12,6 +12,15 @@ #include <stdio.h> #include <glib.h> +/* + * plugins should not include anything from QEMU aside from the + * API header. However as this is a test plugin to exercise the + * internals of QEMU and we want to avoid needless code duplication we + * do so here. bswap.h is pretty self-contained although it needs a + * few things provided by compiler.h. + */ +#include <compiler.h> +#include <bswap.h> #include <qemu-plugin.h> QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION; @@ -21,13 +30,52 @@ typedef struct { uint64_t io_count; } CPUCount; +typedef struct { + uint64_t vaddr; + const char *sym; +} InsnInfo; + +/* + * For the "memory" system test we need to track accesses to + * individual regions. We mirror the data written to the region and + * then check when it is read that it matches up. + * + * We do this as regions rather than pages to save on complications + * with page crossing and the fact the test only cares about the + * test_data region. + */ +static uint64_t region_size = 4096 * 4; +static uint64_t region_mask; + +typedef struct { + uint64_t region_address; + uint64_t reads; + uint64_t writes; + uint8_t *data; + /* Did we see every write and read with correct values? */ + bool seen_all; +} RegionInfo; + static struct qemu_plugin_scoreboard *counts; static qemu_plugin_u64 mem_count; static qemu_plugin_u64 io_count; -static bool do_inline, do_callback; +static bool do_inline, do_callback, do_print_accesses, do_region_summary; static bool do_haddr; static enum qemu_plugin_mem_rw rw = QEMU_PLUGIN_MEM_RW; + +static GMutex lock; +static GHashTable *regions; + +static gint addr_order(gconstpointer a, gconstpointer b, gpointer d) +{ + RegionInfo *na = (RegionInfo *) a; + RegionInfo *nb = (RegionInfo *) b; + + return na->region_address > nb->region_address ? 1 : -1; +} + + static void plugin_exit(qemu_plugin_id_t id, void *p) { g_autoptr(GString) out = g_string_new(""); @@ -41,9 +89,145 @@ static void plugin_exit(qemu_plugin_id_t id, void *p) qemu_plugin_u64_sum(io_count)); } qemu_plugin_outs(out->str); + + + if (do_region_summary) { + GList *counts = g_hash_table_get_values(regions); + + counts = g_list_sort_with_data(counts, addr_order, NULL); + + g_string_printf(out, "Region Base, Reads, Writes, Seen all\n"); + + if (counts && g_list_next(counts)) { + for (/* counts */; counts; counts = counts->next) { + RegionInfo *ri = (RegionInfo *) counts->data; + + g_string_append_printf(out, + "0x%016"PRIx64", " + "%"PRId64", %"PRId64", %s\n", + ri->region_address, + ri->reads, + ri->writes, + ri->seen_all ? "true" : "false"); + } + } + qemu_plugin_outs(out->str); + } + qemu_plugin_scoreboard_free(counts); } +/* + * Update the region tracking info for the access. We split up accesses + * that span regions even though the plugin infrastructure will deliver + * it as a single access. + */ +static void update_region_info(uint64_t region, uint64_t offset, + qemu_plugin_meminfo_t meminfo, + qemu_plugin_mem_value value, + unsigned size) +{ + bool be = qemu_plugin_mem_is_big_endian(meminfo); + bool is_store = qemu_plugin_mem_is_store(meminfo); + RegionInfo *ri; + bool unseen_data = false; + + g_assert(offset + size <= region_size); + + g_mutex_lock(&lock); + ri = (RegionInfo *) g_hash_table_lookup(regions, ®ion); + + if (!ri) { + ri = g_new0(RegionInfo, 1); + ri->region_address = region; + ri->data = g_malloc0(region_size); + ri->seen_all = true; + g_hash_table_insert(regions, &ri->region_address, ri); + } + + if (is_store) { + ri->writes++; + } else { + ri->reads++; + } + + switch (value.type) { + case QEMU_PLUGIN_MEM_VALUE_U8: + if (is_store) { + ri->data[offset] = value.data.u8; + } else if (ri->data[offset] != value.data.u8) { + unseen_data = true; + } + break; + case QEMU_PLUGIN_MEM_VALUE_U16: + { + uint16_t *p = (uint16_t *) &ri->data[offset]; + if (is_store) { + if (be) { + stw_be_p(p, value.data.u16); + } else { + stw_le_p(p, value.data.u16); + } + } else { + uint16_t val = be ? lduw_be_p(p) : lduw_le_p(p); + unseen_data = val != value.data.u16; + } + break; + } + case QEMU_PLUGIN_MEM_VALUE_U32: + { + uint32_t *p = (uint32_t *) &ri->data[offset]; + if (is_store) { + if (be) { + stl_be_p(p, value.data.u32); + } else { + stl_le_p(p, value.data.u32); + } + } else { + uint32_t val = be ? ldl_be_p(p) : ldl_le_p(p); + unseen_data = val != value.data.u32; + } + break; + } + case QEMU_PLUGIN_MEM_VALUE_U64: + { + uint64_t *p = (uint64_t *) &ri->data[offset]; + if (is_store) { + if (be) { + stq_be_p(p, value.data.u64); + } else { + stq_le_p(p, value.data.u64); + } + } else { + uint64_t val = be ? ldq_be_p(p) : ldq_le_p(p); + unseen_data = val != value.data.u64; + } + break; + } + case QEMU_PLUGIN_MEM_VALUE_U128: + /* non in test so skip */ + break; + default: + g_assert_not_reached(); + } + + /* + * This is expected for regions initialised by QEMU (.text etc) but we + * expect to see all data read and written to the test_data region + * of the memory test. + */ + if (unseen_data && ri->seen_all) { + g_autoptr(GString) error = g_string_new("Warning: "); + g_string_append_printf(error, "0x%016"PRIx64":%"PRId64 + " read an un-instrumented value\n", + region, offset); + qemu_plugin_outs(error->str); + ri->seen_all = false; + } + + g_mutex_unlock(&lock); +} + static void vcpu_mem(unsigned int cpu_index, qemu_plugin_meminfo_t meminfo, uint64_t vaddr, void *udata) { @@ -58,6 +242,53 @@ static void vcpu_mem(unsigned int cpu_index, qemu_plugin_meminfo_t meminfo, } else { qemu_plugin_u64_add(mem_count, cpu_index, 1); } + + if (do_region_summary) { + uint64_t region = vaddr & ~region_mask; + uint64_t offset = vaddr & region_mask; + qemu_plugin_mem_value value = qemu_plugin_mem_get_value(meminfo); + unsigned size = 1 << qemu_plugin_mem_size_shift(meminfo); + + update_region_info(region, offset, meminfo, value, size); + } +} + +static void print_access(unsigned int cpu_index, qemu_plugin_meminfo_t meminfo, + uint64_t vaddr, void *udata) +{ + InsnInfo *insn_info = udata; + unsigned size = 8 << qemu_plugin_mem_size_shift(meminfo); + const char *type = qemu_plugin_mem_is_store(meminfo) ? "store" : "load"; + qemu_plugin_mem_value value = qemu_plugin_mem_get_value(meminfo); + uint64_t hwaddr = + qemu_plugin_hwaddr_phys_addr(qemu_plugin_get_hwaddr(meminfo, vaddr)); + g_autoptr(GString) out = g_string_new(""); + g_string_printf(out, + "0x%"PRIx64",%s,0x%"PRIx64",0x%"PRIx64",%d,%s,", + insn_info->vaddr, insn_info->sym, + vaddr, hwaddr, size, type); + switch (value.type) { + case QEMU_PLUGIN_MEM_VALUE_U8: + g_string_append_printf(out, "0x%02"PRIx8, value.data.u8); + break; + case QEMU_PLUGIN_MEM_VALUE_U16: + g_string_append_printf(out, "0x%04"PRIx16, value.data.u16); + break; + case QEMU_PLUGIN_MEM_VALUE_U32: + g_string_append_printf(out, "0x%08"PRIx32, value.data.u32); + break; + case QEMU_PLUGIN_MEM_VALUE_U64: + g_string_append_printf(out, "0x%016"PRIx64, value.data.u64); + break; + case QEMU_PLUGIN_MEM_VALUE_U128: + g_string_append_printf(out, "0x%016"PRIx64"%016"PRIx64, + value.data.u128.high, value.data.u128.low); + break; + default: + g_assert_not_reached(); + } + g_string_append_printf(out, "\n"); + qemu_plugin_outs(out->str); } static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) @@ -74,11 +305,21 @@ static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) QEMU_PLUGIN_INLINE_ADD_U64, mem_count, 1); } - if (do_callback) { + if (do_callback || do_region_summary) { qemu_plugin_register_vcpu_mem_cb(insn, vcpu_mem, QEMU_PLUGIN_CB_NO_REGS, rw, NULL); } + if (do_print_accesses) { + /* we leak this pointer, to avoid locking to keep track of it */ + InsnInfo *insn_info = g_malloc(sizeof(InsnInfo)); + const char *sym = qemu_plugin_insn_symbol(insn); + insn_info->sym = sym ? sym : ""; + insn_info->vaddr = qemu_plugin_insn_vaddr(insn); + qemu_plugin_register_vcpu_mem_cb(insn, print_access, + QEMU_PLUGIN_CB_NO_REGS, + rw, (void *) insn_info); + } } } @@ -117,6 +358,18 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, fprintf(stderr, "boolean argument parsing failed: %s\n", opt); return -1; } + } else if (g_strcmp0(tokens[0], "print-accesses") == 0) { + if (!qemu_plugin_bool_parse(tokens[0], tokens[1], + &do_print_accesses)) { + fprintf(stderr, "boolean argument parsing failed: %s\n", opt); + return -1; + } + } else if (g_strcmp0(tokens[0], "region-summary") == 0) { + if (!qemu_plugin_bool_parse(tokens[0], tokens[1], + &do_region_summary)) { + fprintf(stderr, "boolean argument parsing failed: %s\n", opt); + return -1; + } } else { fprintf(stderr, "option parsing failed: %s\n", opt); return -1; @@ -129,6 +382,19 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, return -1; } + if (do_print_accesses) { + g_autoptr(GString) out = g_string_new(""); + g_string_printf(out, + "insn_vaddr,insn_symbol,mem_vaddr,mem_hwaddr," + "access_size,access_type,mem_value\n"); + qemu_plugin_outs(out->str); + } + + if (do_region_summary) { + region_mask = (region_size - 1); + regions = g_hash_table_new(g_int64_hash, g_int64_equal); + } + counts = qemu_plugin_scoreboard_new(sizeof(CPUCount)); mem_count = qemu_plugin_scoreboard_u64_in_struct( counts, CPUCount, mem_count); diff --git a/tests/tcg/plugins/meson.build b/tests/tcg/plugins/meson.build index f847849..61a007d 100644 --- a/tests/tcg/plugins/meson.build +++ b/tests/tcg/plugins/meson.build @@ -1,13 +1,12 @@ t = [] if get_option('plugins') - foreach i : ['bb', 'empty', 'inline', 'insn', 'mem', 'syscall'] + foreach i : ['bb', 'empty', 'inline', 'insn', 'mem', 'reset', 'syscall', 'patch'] if host_os == 'windows' t += shared_module(i, files(i + '.c') + '../../../contrib/plugins/win32_linker.c', include_directories: '../../../include/qemu', link_depends: [win32_qemu_plugin_api_lib], - link_args: ['-Lplugins', '-lqemu_plugin_api'], + link_args: win32_qemu_plugin_api_link_flags, dependencies: glib) - else t += shared_module(i, files(i + '.c'), include_directories: '../../../include/qemu', @@ -18,5 +17,7 @@ endif if t.length() > 0 alias_target('test-plugins', t) else - run_target('test-plugins', command: find_program('true')) + run_target('test-plugins', command: [python, '-c', '']) endif + +plugin_modules += t diff --git a/tests/tcg/plugins/patch.c b/tests/tcg/plugins/patch.c new file mode 100644 index 0000000..111c5c1 --- /dev/null +++ b/tests/tcg/plugins/patch.c @@ -0,0 +1,251 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * + * This plugin patches instructions matching a pattern to a different + * instruction as they execute + * + */ + +#include "glib.h" +#include "glibconfig.h" + +#include <qemu-plugin.h> +#include <string.h> +#include <stdio.h> + +QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION; + +static bool use_hwaddr; +static GByteArray *target_data; +static GByteArray *patch_data; + +/** + * Parse a string of hexadecimal digits into a GByteArray. The string must be + * even length + */ +static GByteArray *str_to_bytes(const char *str) +{ + size_t len = strlen(str); + + if (len == 0 || len % 2 != 0) { + return NULL; + } + + GByteArray *bytes = g_byte_array_new(); + char byte[3] = {0}; + guint8 value = 0; + + for (size_t i = 0; i < len; i += 2) { + byte[0] = str[i]; + byte[1] = str[i + 1]; + value = (guint8)g_ascii_strtoull(byte, NULL, 16); + g_byte_array_append(bytes, &value, 1); + } + + return bytes; +} + +static void patch_hwaddr(unsigned int vcpu_index, void *userdata) +{ + uintptr_t addr = (uintptr_t) userdata; + g_autoptr(GString) str = g_string_new(NULL); + g_string_printf(str, "patching: @0x%" + PRIxPTR "\n", + addr); + qemu_plugin_outs(str->str); + + enum qemu_plugin_hwaddr_operation_result result = + qemu_plugin_write_memory_hwaddr(addr, patch_data); + + + if (result != QEMU_PLUGIN_HWADDR_OPERATION_OK) { + g_autoptr(GString) errmsg = g_string_new(NULL); + g_string_printf(errmsg, "Failed to write memory: %d\n", result); + qemu_plugin_outs(errmsg->str); + return; + } + + GByteArray *read_data = g_byte_array_new(); + + result = qemu_plugin_read_memory_hwaddr(addr, read_data, + patch_data->len); + + qemu_plugin_outs("Reading memory...\n"); + + if (result != QEMU_PLUGIN_HWADDR_OPERATION_OK) { + g_autoptr(GString) errmsg = g_string_new(NULL); + g_string_printf(errmsg, "Failed to read memory: %d\n", result); + qemu_plugin_outs(errmsg->str); + return; + } + + if (memcmp(patch_data->data, read_data->data, patch_data->len) != 0) { + qemu_plugin_outs("Failed to read back written data\n"); + } + + qemu_plugin_outs("Success!\n"); + + return; +} + +static void patch_vaddr(unsigned int vcpu_index, void *userdata) +{ + uintptr_t addr = (uintptr_t) userdata; + uint64_t hwaddr = 0; + if (!qemu_plugin_translate_vaddr(addr, &hwaddr)) { + qemu_plugin_outs("Failed to translate vaddr\n"); + return; + } + g_autoptr(GString) str = g_string_new(NULL); + g_string_printf(str, "patching: @0x%" + PRIxPTR " hw: @0x%" PRIx64 "\n", + addr, hwaddr); + qemu_plugin_outs(str->str); + + qemu_plugin_outs("Writing memory (vaddr)...\n"); + + if (!qemu_plugin_write_memory_vaddr(addr, patch_data)) { + qemu_plugin_outs("Failed to write memory\n"); + return; + } + + qemu_plugin_outs("Reading memory (vaddr)...\n"); + + g_autoptr(GByteArray) read_data = g_byte_array_new(); + + if (!qemu_plugin_read_memory_vaddr(addr, read_data, patch_data->len)) { + qemu_plugin_outs("Failed to read memory\n"); + return; + } + + if (memcmp(patch_data->data, read_data->data, patch_data->len) != 0) { + qemu_plugin_outs("Failed to read back written data\n"); + } + + qemu_plugin_outs("Success!\n"); + + return; +} + +/* + * Callback on translation of a translation block. + */ +static void vcpu_tb_trans_cb(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) +{ + g_autoptr(GByteArray) insn_data = g_byte_array_new(); + uintptr_t addr = 0; + + for (size_t i = 0; i < qemu_plugin_tb_n_insns(tb); i++) { + struct qemu_plugin_insn *insn = qemu_plugin_tb_get_insn(tb, i); + uint64_t vaddr = qemu_plugin_insn_vaddr(insn); + + if (use_hwaddr) { + uint64_t hwaddr = 0; + if (!qemu_plugin_translate_vaddr(vaddr, &hwaddr)) { + qemu_plugin_outs("Failed to translate vaddr\n"); + continue; + } + /* + * As we cannot emulate 64 bit systems on 32 bit hosts we + * should never see the top bits set, hence we can safely + * cast to uintptr_t. + */ + g_assert(hwaddr <= UINTPTR_MAX); + addr = (uintptr_t) hwaddr; + } else { + g_assert(vaddr <= UINTPTR_MAX); + addr = (uintptr_t) vaddr; + } + + g_byte_array_set_size(insn_data, qemu_plugin_insn_size(insn)); + qemu_plugin_insn_data(insn, insn_data->data, insn_data->len); + + if (insn_data->len >= target_data->len && + !memcmp(insn_data->data, target_data->data, + MIN(target_data->len, insn_data->len))) { + if (use_hwaddr) { + qemu_plugin_register_vcpu_tb_exec_cb(tb, patch_hwaddr, + QEMU_PLUGIN_CB_NO_REGS, + (void *) addr); + } else { + qemu_plugin_register_vcpu_tb_exec_cb(tb, patch_vaddr, + QEMU_PLUGIN_CB_NO_REGS, + (void *) addr); + } + } + } +} + +static void usage(void) +{ + fprintf(stderr, "Usage: <lib>,target=<bytes>,patch=<new_bytes>" + "[,use_hwaddr=true|false]"); +} + +/* + * Called when the plugin is installed + */ +QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, + const qemu_info_t *info, int argc, + char **argv) +{ + + use_hwaddr = true; + target_data = NULL; + patch_data = NULL; + + if (argc > 4) { + usage(); + return -1; + } + + for (size_t i = 0; i < argc; i++) { + char *opt = argv[i]; + g_auto(GStrv) tokens = g_strsplit(opt, "=", 2); + if (g_strcmp0(tokens[0], "use_hwaddr") == 0) { + if (!qemu_plugin_bool_parse(tokens[0], tokens[1], &use_hwaddr)) { + fprintf(stderr, + "Failed to parse boolean argument use_hwaddr\n"); + return -1; + } + } else if (g_strcmp0(tokens[0], "target") == 0) { + target_data = str_to_bytes(tokens[1]); + if (!target_data) { + fprintf(stderr, + "Failed to parse target bytes.\n"); + return -1; + } + } else if (g_strcmp0(tokens[0], "patch") == 0) { + patch_data = str_to_bytes(tokens[1]); + if (!patch_data) { + fprintf(stderr, "Failed to parse patch bytes.\n"); + return -1; + } + } else { + fprintf(stderr, "Unknown argument: %s\n", tokens[0]); + usage(); + return -1; + } + } + + if (!target_data) { + fprintf(stderr, "target argument is required\n"); + usage(); + return -1; + } + + if (!patch_data) { + fprintf(stderr, "patch argument is required\n"); + usage(); + return -1; + } + + if (target_data->len != patch_data->len) { + fprintf(stderr, "Target and patch data must be the same length\n"); + return -1; + } + + qemu_plugin_register_vcpu_tb_trans_cb(id, vcpu_tb_trans_cb); + + return 0; +} diff --git a/tests/tcg/plugins/reset.c b/tests/tcg/plugins/reset.c new file mode 100644 index 0000000..1be8be2 --- /dev/null +++ b/tests/tcg/plugins/reset.c @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2025 Linaro Ltd + * + * Test the reset/uninstall cycle of a plugin. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#include <glib.h> + +#include <qemu-plugin.h> + +QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION; +static qemu_plugin_id_t plugin_id; +static bool was_reset; +static bool was_uninstalled; + +static void after_uninstall(qemu_plugin_id_t id) +{ + g_assert(was_reset && !was_uninstalled); + qemu_plugin_outs("uninstall done\n"); + was_uninstalled = true; +} + +static void tb_exec_after_reset(unsigned int vcpu_index, void *userdata) +{ + g_assert(was_reset && !was_uninstalled); + qemu_plugin_uninstall(plugin_id, after_uninstall); +} + +static void tb_trans_after_reset(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) +{ + g_assert(was_reset && !was_uninstalled); + qemu_plugin_register_vcpu_tb_exec_cb(tb, tb_exec_after_reset, + QEMU_PLUGIN_CB_NO_REGS, NULL); +} + +static void after_reset(qemu_plugin_id_t id) +{ + g_assert(!was_reset && !was_uninstalled); + qemu_plugin_outs("reset done\n"); + was_reset = true; + qemu_plugin_register_vcpu_tb_trans_cb(id, tb_trans_after_reset); +} + +static void tb_exec_before_reset(unsigned int vcpu_index, void *userdata) +{ + g_assert(!was_reset && !was_uninstalled); + qemu_plugin_reset(plugin_id, after_reset); +} + +static void tb_trans_before_reset(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) +{ + g_assert(!was_reset && !was_uninstalled); + qemu_plugin_register_vcpu_tb_exec_cb(tb, tb_exec_before_reset, + QEMU_PLUGIN_CB_NO_REGS, NULL); +} + +QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, + const qemu_info_t *info, + int argc, char **argv) +{ + plugin_id = id; + qemu_plugin_register_vcpu_tb_trans_cb(id, tb_trans_before_reset); + return 0; +} + +/* Since we uninstall the plugin, we can't use qemu_plugin_register_atexit_cb, + * so we use destructor attribute instead. */ +static void __attribute__((destructor)) on_plugin_exit(void) +{ + g_assert(was_reset && was_uninstalled); + qemu_plugin_outs("plugin exit\n"); +} diff --git a/tests/tcg/plugins/syscall.c b/tests/tcg/plugins/syscall.c index 72e1a5b..42801f5 100644 --- a/tests/tcg/plugins/syscall.c +++ b/tests/tcg/plugins/syscall.c @@ -22,23 +22,109 @@ typedef struct { int64_t errors; } SyscallStats; +struct SyscallInfo { + const char *name; + int64_t write_sysno; +}; + +static const struct SyscallInfo arch_syscall_info[] = { + { "aarch64", 64 }, + { "aarch64_be", 64 }, + { "alpha", 4 }, + { "arm", 4 }, + { "armeb", 4 }, + { "avr", -1 }, + { "hexagon", 64 }, + { "hppa", -1 }, + { "i386", 4 }, + { "loongarch64", -1 }, + { "m68k", 4 }, + { "microblaze", 4 }, + { "microblazeel", 4 }, + { "mips", 1 }, + { "mips64", 1 }, + { "mips64el", 1 }, + { "mipsel", 1 }, + { "mipsn32", 1 }, + { "mipsn32el", 1 }, + { "or1k", -1 }, + { "ppc", 4 }, + { "ppc64", 4 }, + { "ppc64le", 4 }, + { "riscv32", 64 }, + { "riscv64", 64 }, + { "rx", -1 }, + { "s390x", -1 }, + { "sh4", -1 }, + { "sh4eb", -1 }, + { "sparc", 4 }, + { "sparc32plus", 4 }, + { "sparc64", 4 }, + { "tricore", -1 }, + { "x86_64", 1 }, + { "xtensa", 13 }, + { "xtensaeb", 13 }, + { NULL, -1 }, +}; + static GMutex lock; static GHashTable *statistics; +static GByteArray *memory_buffer; +static bool do_log_writes; +static int64_t write_sysno = -1; static SyscallStats *get_or_create_entry(int64_t num) { SyscallStats *entry = - (SyscallStats *) g_hash_table_lookup(statistics, GINT_TO_POINTER(num)); + (SyscallStats *) g_hash_table_lookup(statistics, &num); if (!entry) { entry = g_new0(SyscallStats, 1); entry->num = num; - g_hash_table_insert(statistics, GINT_TO_POINTER(num), (gpointer) entry); + g_hash_table_insert(statistics, &entry->num, entry); } return entry; } +/* + * Hex-dump a GByteArray to the QEMU plugin output in the format: + * 61 63 63 65 6c 09 09 20 20 20 66 70 75 09 09 09 | accel.....fpu... + * 20 6d 6f 64 75 6c 65 2d 63 6f 6d 6d 6f 6e 2e 63 | .module-common.c + */ +static void hexdump(const GByteArray *data) +{ + g_autoptr(GString) out = g_string_new(""); + + for (guint index = 0; index < data->len; index += 16) { + for (guint col = 0; col < 16; col++) { + if (index + col < data->len) { + g_string_append_printf(out, "%02x ", data->data[index + col]); + } else { + g_string_append(out, " "); + } + } + + g_string_append(out, " | "); + + for (guint col = 0; col < 16; col++) { + if (index + col >= data->len) { + break; + } + + if (g_ascii_isgraph(data->data[index + col])) { + g_string_append_printf(out, "%c", data->data[index + col]); + } else { + g_string_append(out, "."); + } + } + + g_string_append(out, "\n"); + } + + qemu_plugin_outs(out->str); +} + static void vcpu_syscall(qemu_plugin_id_t id, unsigned int vcpu_index, int64_t num, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5, @@ -54,6 +140,14 @@ static void vcpu_syscall(qemu_plugin_id_t id, unsigned int vcpu_index, g_autofree gchar *out = g_strdup_printf("syscall #%" PRIi64 "\n", num); qemu_plugin_outs(out); } + + if (do_log_writes && num == write_sysno) { + if (qemu_plugin_read_memory_vaddr(a2, memory_buffer, a3)) { + hexdump(memory_buffer); + } else { + fprintf(stderr, "Error reading memory from vaddr %"PRIu64"\n", a2); + } + } } static void vcpu_syscall_ret(qemu_plugin_id_t id, unsigned int vcpu_idx, @@ -86,7 +180,7 @@ static void print_entry(gpointer val, gpointer user_data) qemu_plugin_outs(out); } -static gint comp_func(gconstpointer ea, gconstpointer eb) +static gint comp_func(gconstpointer ea, gconstpointer eb, gpointer d) { SyscallStats *ent_a = (SyscallStats *) ea; SyscallStats *ent_b = (SyscallStats *) eb; @@ -103,7 +197,7 @@ static void plugin_exit(qemu_plugin_id_t id, void *p) g_mutex_lock(&lock); GList *entries = g_hash_table_get_values(statistics); - entries = g_list_sort(entries, comp_func); + entries = g_list_sort_with_data(entries, comp_func, NULL); qemu_plugin_outs("syscall no. calls errors\n"); g_list_foreach(entries, print_entry, NULL); @@ -127,6 +221,10 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, if (!qemu_plugin_bool_parse(tokens[0], tokens[1], &do_print)) { fprintf(stderr, "boolean argument parsing failed: %s\n", opt); } + } else if (g_strcmp0(tokens[0], "log_writes") == 0) { + if (!qemu_plugin_bool_parse(tokens[0], tokens[1], &do_log_writes)) { + fprintf(stderr, "boolean argument parsing failed: %s\n", opt); + } } else { fprintf(stderr, "unsupported argument: %s\n", argv[i]); return -1; @@ -134,7 +232,25 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, } if (!do_print) { - statistics = g_hash_table_new_full(NULL, g_direct_equal, NULL, g_free); + statistics = g_hash_table_new_full(g_int64_hash, g_int64_equal, NULL, g_free); + } + + if (do_log_writes) { + for (const struct SyscallInfo *syscall_info = arch_syscall_info; + syscall_info->name != NULL; syscall_info++) { + + if (g_strcmp0(syscall_info->name, info->target_name) == 0) { + write_sysno = syscall_info->write_sysno; + break; + } + } + + if (write_sysno == -1) { + fprintf(stderr, "write syscall number not found\n"); + return -1; + } + + memory_buffer = g_byte_array_new(); } qemu_plugin_register_vcpu_syscall_cb(id, vcpu_syscall); diff --git a/tests/tcg/ppc64/Makefile.target b/tests/tcg/ppc64/Makefile.target index 509a20b..0d058b2 100644 --- a/tests/tcg/ppc64/Makefile.target +++ b/tests/tcg/ppc64/Makefile.target @@ -6,7 +6,7 @@ VPATH += $(SRC_PATH)/tests/tcg/ppc64 config-cc.mak: Makefile $(quiet-@)( \ - $(call cc-option,-mpower8-vector, CROSS_CC_HAS_POWER8_VECTOR); \ + $(call cc-option,-mcpu=power8, CROSS_CC_HAS_CPU_POWER8); \ $(call cc-option,-mpower10, CROSS_CC_HAS_POWER10)) 3> config-cc.mak -include config-cc.mak @@ -23,15 +23,15 @@ run-threadcount: threadcount run-plugin-threadcount-with-%: $(call skip-test, $<, "BROKEN (flaky with clang) ") -ifneq ($(CROSS_CC_HAS_POWER8_VECTOR),) +ifneq ($(CROSS_CC_HAS_CPU_POWER8),) PPC64_TESTS=bcdsub non_signalling_xscv endif -$(PPC64_TESTS): CFLAGS += -mpower8-vector +$(PPC64_TESTS): CFLAGS += -mcpu=power8 -ifneq ($(CROSS_CC_HAS_POWER8_VECTOR),) +ifneq ($(CROSS_CC_HAS_CPU_POWER8),) PPC64_TESTS += vsx_f2i_nan endif -vsx_f2i_nan: CFLAGS += -mpower8-vector -I$(SRC_PATH)/include +vsx_f2i_nan: CFLAGS += -mcpu=power8 -I$(SRC_PATH)/include PPC64_TESTS += mtfsf PPC64_TESTS += mffsce @@ -55,4 +55,9 @@ PPC64_TESTS += signal_save_restore_xer PPC64_TESTS += xxspltw PPC64_TESTS += test-aes +# ppc64 ABI uses function descriptors, and thus, QEMU can't find symbol for a +# given instruction. Thus, we don't check output of mem-access plugin. +run-plugin-test-plugin-mem-access-with-libmem.so: \ + CHECK_PLUGIN_OUTPUT_COMMAND= + TESTS += $(PPC64_TESTS) diff --git a/tests/tcg/s390x/Makefile.softmmu-target b/tests/tcg/s390x/Makefile.softmmu-target index f60f94b..8cd4667 100644 --- a/tests/tcg/s390x/Makefile.softmmu-target +++ b/tests/tcg/s390x/Makefile.softmmu-target @@ -1,8 +1,9 @@ S390X_SRC=$(SRC_PATH)/tests/tcg/s390x VPATH+=$(S390X_SRC) -QEMU_OPTS+=-action panic=exit-failure -nographic $(EXTFLAGS) -kernel +# EXTFLAGS can be passed by the user, e.g. to override the --accel +QEMU_OPTS+=-action panic=exit-failure -nographic -serial chardev:output $(EXTFLAGS) -kernel LINK_SCRIPT=$(S390X_SRC)/softmmu.ld -CFLAGS+=-ggdb -O0 +CFLAGS+=-ggdb -O0 -I$(SRC_PATH)/include/hw/s390x/ipl/ LDFLAGS=-nostdlib -static %.o: %.S @@ -41,8 +42,15 @@ $(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) -$(MULTIARCH_TESTS): CFLAGS += $(MINILIB_INC) +$(MULTIARCH_TESTS): CFLAGS += $(MINILIB_INC) \ + -I$(SRC_PATH)/roms/SLOF/lib/libc/include/ memory: CFLAGS += -DCHECK_UNALIGNED=0 + +# s390x clears the BSS section so we need to account for that +run-plugin-memory-with-libmem.so: \ + CHECK_PLUGIN_OUTPUT_COMMAND=$(MULTIARCH_SYSTEM_SRC)/validate-memory-counts.py \ + --bss-cleared $@.out diff --git a/tests/tcg/s390x/Makefile.target b/tests/tcg/s390x/Makefile.target index a8f86c9..da5fe71 100644 --- a/tests/tcg/s390x/Makefile.target +++ b/tests/tcg/s390x/Makefile.target @@ -48,6 +48,7 @@ TESTS+=lae TESTS+=cvd TESTS+=cvb TESTS+=ts +TESTS+=ex-smc cdsg: CFLAGS+=-pthread cdsg: LDFLAGS+=-pthread @@ -73,8 +74,11 @@ $(Z13_TESTS): CFLAGS+=-march=z13 -O2 TESTS+=$(Z13_TESTS) ifneq ($(CROSS_CC_HAS_Z14),) -Z14_TESTS=vfminmax +Z14_TESTS=fma vfminmax +fma: float.h +fma: LDFLAGS+=-lm vfminmax: LDFLAGS+=-lm +vfminmax: float.h $(Z14_TESTS): CFLAGS+=-march=z14 -O2 TESTS+=$(Z14_TESTS) endif diff --git a/tests/tcg/s390x/console.c b/tests/tcg/s390x/console.c index d43ce3f..6c26f04 100644 --- a/tests/tcg/s390x/console.c +++ b/tests/tcg/s390x/console.c @@ -4,7 +4,10 @@ * * SPDX-License-Identifier: GPL-2.0-or-later */ + #include "../../../pc-bios/s390-ccw/sclp.c" +#include "../../../roms/SLOF/lib/libc/string/memset.c" +#include "../../../roms/SLOF/lib/libc/string/memcpy.c" void __sys_outc(char c) { diff --git a/tests/tcg/s390x/ex-smc.c b/tests/tcg/s390x/ex-smc.c new file mode 100644 index 0000000..f403640 --- /dev/null +++ b/tests/tcg/s390x/ex-smc.c @@ -0,0 +1,57 @@ +/* + * Test modifying an EXECUTE target. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#include <assert.h> +#include <stdlib.h> + +/* Make sure we exercise the same EXECUTE instruction. */ +extern void execute(unsigned char *insn, unsigned char mask, + unsigned long *r1_r5); +asm(".globl execute\n" + "execute:\n" + "lg %r1,0(%r4)\n" + "lg %r5,8(%r4)\n" + "ex %r3,0(%r2)\n" + "stg %r5,8(%r4)\n" + "stg %r1,0(%r4)\n" + "br %r14\n"); + +/* Define an RWX EXECUTE target. */ +extern unsigned char lgfi[]; +asm(".pushsection .rwx,\"awx\",@progbits\n" + ".globl lgfi\n" + "lgfi: lgfi %r0,0\n" + ".popsection\n"); + +int main(void) +{ + unsigned long r1_r5[2]; + + /* Create an initial TB. */ + r1_r5[0] = -1; + r1_r5[1] = -1; + execute(lgfi, 1 << 4, r1_r5); + assert(r1_r5[0] == 0); + assert(r1_r5[1] == -1); + + /* Test changing the mask. */ + execute(lgfi, 5 << 4, r1_r5); + assert(r1_r5[0] == 0); + assert(r1_r5[1] == 0); + + /* Test changing the target. */ + lgfi[5] = 42; + execute(lgfi, 5 << 4, r1_r5); + assert(r1_r5[0] == 0); + assert(r1_r5[1] == 42); + + /* Test changing both the mask and the target. */ + lgfi[5] = 24; + execute(lgfi, 1 << 4, r1_r5); + assert(r1_r5[0] == 24); + assert(r1_r5[1] == 42); + + return EXIT_SUCCESS; +} diff --git a/tests/tcg/s390x/float.h b/tests/tcg/s390x/float.h new file mode 100644 index 0000000..9d1682b --- /dev/null +++ b/tests/tcg/s390x/float.h @@ -0,0 +1,104 @@ +/* + * Helpers for floating-point tests. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#ifndef FLOAT_H +#define FLOAT_H + +/* + * Floating-point value classes. + */ +#define N_FORMATS 3 +#define CLASS_MINUS_INF 0 +#define CLASS_MINUS_FN 1 +#define CLASS_MINUS_ZERO 2 +#define CLASS_PLUS_ZERO 3 +#define CLASS_PLUS_FN 4 +#define CLASS_PLUS_INF 5 +#define CLASS_QNAN 6 +#define CLASS_SNAN 7 +#define N_SIGNED_CLASSES 8 +static const size_t float_sizes[N_FORMATS] = { + /* M4 == 2: short */ 4, + /* M4 == 3: long */ 8, + /* M4 == 4: extended */ 16, +}; +static const size_t e_bits[N_FORMATS] = { + /* M4 == 2: short */ 8, + /* M4 == 3: long */ 11, + /* M4 == 4: extended */ 15, +}; +struct float_class { + size_t n; + unsigned char v[2][16]; +}; +static const struct float_class signed_floats[N_FORMATS][N_SIGNED_CLASSES] = { + /* M4 == 2: short */ + { + /* -inf */ {1, {{0xff, 0x80, 0x00, 0x00}}}, + /* -Fn */ {2, {{0xc2, 0x28, 0x00, 0x00}, + {0xc2, 0x29, 0x00, 0x00}}}, + /* -0 */ {1, {{0x80, 0x00, 0x00, 0x00}}}, + /* +0 */ {1, {{0x00, 0x00, 0x00, 0x00}}}, + /* +Fn */ {2, {{0x42, 0x28, 0x00, 0x00}, + {0x42, 0x2a, 0x00, 0x00}}}, + /* +inf */ {1, {{0x7f, 0x80, 0x00, 0x00}}}, + /* QNaN */ {2, {{0x7f, 0xff, 0xff, 0xff}, + {0x7f, 0xff, 0xff, 0xfe}}}, + /* SNaN */ {2, {{0x7f, 0xbf, 0xff, 0xff}, + {0x7f, 0xbf, 0xff, 0xfd}}}, + }, + + /* M4 == 3: long */ + { + /* -inf */ {1, {{0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* -Fn */ {2, {{0xc0, 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0xc0, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* -0 */ {1, {{0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* +0 */ {1, {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* +Fn */ {2, {{0x40, 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x40, 0x47, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* +inf */ {1, {{0x7f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* QNaN */ {2, {{0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + {0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe}}}, + /* SNaN */ {2, {{0x7f, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + {0x7f, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd}}}, + }, + + /* M4 == 4: extended */ + { + /* -inf */ {1, {{0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* -Fn */ {2, {{0xc0, 0x04, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0xc0, 0x04, 0x51, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* -0 */ {1, {{0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* +0 */ {1, {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* +Fn */ {2, {{0x40, 0x04, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x40, 0x04, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* +inf */ {1, {{0x7f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}}, + /* QNaN */ {2, {{0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + {0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe}}}, + /* SNaN */ {2, {{0x7f, 0xff, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + {0x7f, 0xff, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd}}}, + }, +}; +static const unsigned char default_nans[N_FORMATS][16] = { + /* M4 == 2: short */ {0x7f, 0xc0, 0x00, 0x00}, + /* M4 == 3: long */ {0x7f, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + /* M4 == 4: extended */ {0x7f, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, +}; + +static void dump_v(FILE *f, const void *v, size_t n) +{ + for (int i = 0; i < n; i++) { + fprintf(f, "%02x", ((const unsigned char *)v)[i]); + } +} + +static void snan_to_qnan(char *v, int fmt) +{ + size_t bit = 1 + e_bits[fmt]; + v[bit / 8] |= 1 << (7 - (bit % 8)); +} + +#endif diff --git a/tests/tcg/s390x/fma.c b/tests/tcg/s390x/fma.c new file mode 100644 index 0000000..6872f59 --- /dev/null +++ b/tests/tcg/s390x/fma.c @@ -0,0 +1,233 @@ +/* + * Test floating-point multiply-and-add instructions. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#include <fenv.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "float.h" + +union val { + float e; + double d; + long double x; + char buf[16]; +}; + +/* + * PoP tables as close to the original as possible. + */ +static const char *table1[N_SIGNED_CLASSES][N_SIGNED_CLASSES] = { + /* -inf -Fn -0 +0 +Fn +inf QNaN SNaN */ + {/* -inf */ "P(+inf)", "P(+inf)", "Xi: T(dNaN)", "Xi: T(dNaN)", "P(-inf)", "P(-inf)", "P(b)", "Xi: T(b*)"}, + {/* -Fn */ "P(+inf)", "P(a*b)", "P(+0)", "P(-0)", "P(a*b)", "P(-inf)", "P(b)", "Xi: T(b*)"}, + {/* -0 */ "Xi: T(dNaN)", "P(+0)", "P(+0)", "P(-0)", "P(-0)", "Xi: T(dNaN)", "P(b)", "Xi: T(b*)"}, + {/* +0 */ "Xi: T(dNaN)", "P(-0)", "P(-0)", "P(+0)", "P(+0)", "Xi: T(dNaN)", "P(b)", "Xi: T(b*)"}, + {/* +Fn */ "P(-inf)", "P(a*b)", "P(-0)", "P(+0)", "P(a*b)", "P(+inf)", "P(b)", "Xi: T(b*)"}, + {/* +inf */ "P(-inf)", "P(-inf)", "Xi: T(dNaN)", "Xi: T(dNaN)", "P(+inf)", "P(+inf)", "P(b)", "Xi: T(b*)"}, + {/* QNaN */ "P(a)", "P(a)", "P(a)", "P(a)", "P(a)", "P(a)", "P(a)", "Xi: T(b*)"}, + {/* SNaN */ "Xi: T(a*)", "Xi: T(a*)", "Xi: T(a*)", "Xi: T(a*)", "Xi: T(a*)", "Xi: T(a*)", "Xi: T(a*)", "Xi: T(a*)"}, +}; + +static const char *table2[N_SIGNED_CLASSES][N_SIGNED_CLASSES] = { + /* -inf -Fn -0 +0 +Fn +inf QNaN SNaN */ + {/* -inf */ "T(-inf)", "T(-inf)", "T(-inf)", "T(-inf)", "T(-inf)", "Xi: T(dNaN)", "T(c)", "Xi: T(c*)"}, + {/* -Fn */ "T(-inf)", "R(p+c)", "R(p)", "R(p)", "R(p+c)", "T(+inf)", "T(c)", "Xi: T(c*)"}, + {/* -0 */ "T(-inf)", "R(c)", "T(-0)", "Rezd", "R(c)", "T(+inf)", "T(c)", "Xi: T(c*)"}, + {/* +0 */ "T(-inf)", "R(c)", "Rezd", "T(+0)", "R(c)", "T(+inf)", "T(c)", "Xi: T(c*)"}, + {/* +Fn */ "T(-inf)", "R(p+c)", "R(p)", "R(p)", "R(p+c)", "T(+inf)", "T(c)", "Xi: T(c*)"}, + {/* +inf */ "Xi: T(dNaN)", "T(+inf)", "T(+inf)", "T(+inf)", "T(+inf)", "T(+inf)", "T(c)", "Xi: T(c*)"}, + {/* QNaN */ "T(p)", "T(p)", "T(p)", "T(p)", "T(p)", "T(p)", "T(p)", "Xi: T(c*)"}, + /* SNaN: can't happen */ +}; + +static void interpret_tables(union val *r, bool *xi, int fmt, + int cls_a, const union val *a, + int cls_b, const union val *b, + int cls_c, const union val *c) +{ + const char *spec1 = table1[cls_a][cls_b]; + const char *spec2; + union val p; + int cls_p; + + *xi = false; + + if (strcmp(spec1, "P(-inf)") == 0) { + cls_p = CLASS_MINUS_INF; + } else if (strcmp(spec1, "P(+inf)") == 0) { + cls_p = CLASS_PLUS_INF; + } else if (strcmp(spec1, "P(-0)") == 0) { + cls_p = CLASS_MINUS_ZERO; + } else if (strcmp(spec1, "P(+0)") == 0) { + cls_p = CLASS_PLUS_ZERO; + } else if (strcmp(spec1, "P(a)") == 0) { + cls_p = cls_a; + memcpy(&p, a, sizeof(p)); + } else if (strcmp(spec1, "P(b)") == 0) { + cls_p = cls_b; + memcpy(&p, b, sizeof(p)); + } else if (strcmp(spec1, "P(a*b)") == 0) { + /* + * In the general case splitting fma into multiplication and addition + * doesn't work, but this is the case with our test inputs. + */ + cls_p = cls_a == cls_b ? CLASS_PLUS_FN : CLASS_MINUS_FN; + switch (fmt) { + case 0: + p.e = a->e * b->e; + break; + case 1: + p.d = a->d * b->d; + break; + case 2: + p.x = a->x * b->x; + break; + default: + fprintf(stderr, "Unsupported fmt: %d\n", fmt); + exit(1); + } + } else if (strcmp(spec1, "Xi: T(dNaN)") == 0) { + memcpy(r, default_nans[fmt], sizeof(*r)); + *xi = true; + return; + } else if (strcmp(spec1, "Xi: T(a*)") == 0) { + memcpy(r, a, sizeof(*r)); + snan_to_qnan(r->buf, fmt); + *xi = true; + return; + } else if (strcmp(spec1, "Xi: T(b*)") == 0) { + memcpy(r, b, sizeof(*r)); + snan_to_qnan(r->buf, fmt); + *xi = true; + return; + } else { + fprintf(stderr, "Unsupported spec1: %s\n", spec1); + exit(1); + } + + spec2 = table2[cls_p][cls_c]; + if (strcmp(spec2, "T(-inf)") == 0) { + memcpy(r, signed_floats[fmt][CLASS_MINUS_INF].v[0], sizeof(*r)); + } else if (strcmp(spec2, "T(+inf)") == 0) { + memcpy(r, signed_floats[fmt][CLASS_PLUS_INF].v[0], sizeof(*r)); + } else if (strcmp(spec2, "T(-0)") == 0) { + memcpy(r, signed_floats[fmt][CLASS_MINUS_ZERO].v[0], sizeof(*r)); + } else if (strcmp(spec2, "T(+0)") == 0 || strcmp(spec2, "Rezd") == 0) { + memcpy(r, signed_floats[fmt][CLASS_PLUS_ZERO].v[0], sizeof(*r)); + } else if (strcmp(spec2, "R(c)") == 0 || strcmp(spec2, "T(c)") == 0) { + memcpy(r, c, sizeof(*r)); + } else if (strcmp(spec2, "R(p)") == 0 || strcmp(spec2, "T(p)") == 0) { + memcpy(r, &p, sizeof(*r)); + } else if (strcmp(spec2, "R(p+c)") == 0 || strcmp(spec2, "T(p+c)") == 0) { + switch (fmt) { + case 0: + r->e = p.e + c->e; + break; + case 1: + r->d = p.d + c->d; + break; + case 2: + r->x = p.x + c->x; + break; + default: + fprintf(stderr, "Unsupported fmt: %d\n", fmt); + exit(1); + } + } else if (strcmp(spec2, "Xi: T(dNaN)") == 0) { + memcpy(r, default_nans[fmt], sizeof(*r)); + *xi = true; + } else if (strcmp(spec2, "Xi: T(c*)") == 0) { + memcpy(r, c, sizeof(*r)); + snan_to_qnan(r->buf, fmt); + *xi = true; + } else { + fprintf(stderr, "Unsupported spec2: %s\n", spec2); + exit(1); + } +} + +struct iter { + int fmt; + int cls[3]; + int val[3]; +}; + +static bool iter_next(struct iter *it) +{ + int i; + + for (i = 2; i >= 0; i--) { + if (++it->val[i] != signed_floats[it->fmt][it->cls[i]].n) { + return true; + } + it->val[i] = 0; + + if (++it->cls[i] != N_SIGNED_CLASSES) { + return true; + } + it->cls[i] = 0; + } + + return ++it->fmt != N_FORMATS; +} + +int main(void) +{ + int ret = EXIT_SUCCESS; + struct iter it = {}; + + do { + size_t n = float_sizes[it.fmt]; + union val a, b, c, exp, res; + bool xi_exp, xi; + + memcpy(&a, signed_floats[it.fmt][it.cls[0]].v[it.val[0]], sizeof(a)); + memcpy(&b, signed_floats[it.fmt][it.cls[1]].v[it.val[1]], sizeof(b)); + memcpy(&c, signed_floats[it.fmt][it.cls[2]].v[it.val[2]], sizeof(c)); + + interpret_tables(&exp, &xi_exp, it.fmt, + it.cls[1], &b, it.cls[2], &c, it.cls[0], &a); + + memcpy(&res, &a, sizeof(res)); + feclearexcept(FE_ALL_EXCEPT); + switch (it.fmt) { + case 0: + asm("maebr %[a],%[b],%[c]" + : [a] "+f" (res.e) : [b] "f" (b.e), [c] "f" (c.e)); + break; + case 1: + asm("madbr %[a],%[b],%[c]" + : [a] "+f" (res.d) : [b] "f" (b.d), [c] "f" (c.d)); + break; + case 2: + asm("wfmaxb %[a],%[c],%[b],%[a]" + : [a] "+v" (res.x) : [b] "v" (b.x), [c] "v" (c.x)); + break; + default: + fprintf(stderr, "Unsupported fmt: %d\n", it.fmt); + exit(1); + } + xi = fetestexcept(FE_ALL_EXCEPT) == FE_INVALID; + + if (memcmp(&res, &exp, n) != 0 || xi != xi_exp) { + fprintf(stderr, "[ FAILED ] "); + dump_v(stderr, &b, n); + fprintf(stderr, " * "); + dump_v(stderr, &c, n); + fprintf(stderr, " + "); + dump_v(stderr, &a, n); + fprintf(stderr, ": actual="); + dump_v(stderr, &res, n); + fprintf(stderr, "/%d, expected=", (int)xi); + dump_v(stderr, &exp, n); + fprintf(stderr, "/%d\n", (int)xi_exp); + ret = EXIT_FAILURE; + } + } while (iter_next(&it)); + + return ret; +} diff --git a/tests/tcg/s390x/mvc-smc.c b/tests/tcg/s390x/mvc-smc.c new file mode 100644 index 0000000..d68f60c --- /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; +} diff --git a/tests/tcg/s390x/vfminmax.c b/tests/tcg/s390x/vfminmax.c index 22629df..e66285f 100644 --- a/tests/tcg/s390x/vfminmax.c +++ b/tests/tcg/s390x/vfminmax.c @@ -4,6 +4,8 @@ #include <stdio.h> #include <string.h> +#include "float.h" + /* * vfmin/vfmax instruction execution. */ @@ -21,99 +23,22 @@ static void vfminmax(unsigned int op, unsigned int m4, unsigned int m5, unsigned int m6, void *v1, const void *v2, const void *v3) { - insn[3] = (m6 << 4) | m5; - insn[4] = (m4 << 4) | 0x0e; - insn[5] = op; + insn[3] = (m6 << 4) | m5; + insn[4] = (m4 << 4) | 0x0e; + insn[5] = op; asm("vl %%v25,%[v2]\n" "vl %%v26,%[v3]\n" "ex 0,%[insn]\n" "vst %%v24,%[v1]\n" : [v1] "=m" (*(char (*)[16])v1) - : [v2] "m" (*(char (*)[16])v2) - , [v3] "m" (*(char (*)[16])v3) - , [insn] "m"(insn) + : [v2] "m" (*(const char (*)[16])v2) + , [v3] "m" (*(const char (*)[16])v3) + , [insn] "m" (insn) : "v24", "v25", "v26"); } /* - * Floating-point value classes. - */ -#define N_FORMATS 3 -#define N_SIGNED_CLASSES 8 -static const size_t float_sizes[N_FORMATS] = { - /* M4 == 2: short */ 4, - /* M4 == 3: long */ 8, - /* M4 == 4: extended */ 16, -}; -static const size_t e_bits[N_FORMATS] = { - /* M4 == 2: short */ 8, - /* M4 == 3: long */ 11, - /* M4 == 4: extended */ 15, -}; -static const unsigned char signed_floats[N_FORMATS][N_SIGNED_CLASSES][2][16] = { - /* M4 == 2: short */ - { - /* -inf */ {{0xff, 0x80, 0x00, 0x00}, - {0xff, 0x80, 0x00, 0x00}}, - /* -Fn */ {{0xc2, 0x28, 0x00, 0x00}, - {0xc2, 0x29, 0x00, 0x00}}, - /* -0 */ {{0x80, 0x00, 0x00, 0x00}, - {0x80, 0x00, 0x00, 0x00}}, - /* +0 */ {{0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00}}, - /* +Fn */ {{0x42, 0x28, 0x00, 0x00}, - {0x42, 0x2a, 0x00, 0x00}}, - /* +inf */ {{0x7f, 0x80, 0x00, 0x00}, - {0x7f, 0x80, 0x00, 0x00}}, - /* QNaN */ {{0x7f, 0xff, 0xff, 0xff}, - {0x7f, 0xff, 0xff, 0xfe}}, - /* SNaN */ {{0x7f, 0xbf, 0xff, 0xff}, - {0x7f, 0xbf, 0xff, 0xfd}}, - }, - - /* M4 == 3: long */ - { - /* -inf */ {{0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - /* -Fn */ {{0xc0, 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0xc0, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - /* -0 */ {{0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - /* +0 */ {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - /* +Fn */ {{0x40, 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x40, 0x47, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - /* +inf */ {{0x7f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x7f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - /* QNaN */ {{0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, - {0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe}}, - /* SNaN */ {{0x7f, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, - {0x7f, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd}}, - }, - - /* M4 == 4: extended */ - { - /* -inf */ {{0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - /* -Fn */ {{0xc0, 0x04, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0xc0, 0x04, 0x51, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - /* -0 */ {{0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - /* +0 */ {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - /* +Fn */ {{0x40, 0x04, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x40, 0x04, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - /* +inf */ {{0x7f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - {0x7f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, - /* QNaN */ {{0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, - {0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe}}, - /* SNaN */ {{0x7f, 0xff, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, - {0x7f, 0xff, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd}}, - }, -}; - -/* * PoP tables as close to the original as possible. */ struct signed_test { @@ -285,13 +210,6 @@ struct signed_test { }, }; -static void dump_v(FILE *f, const void *v, size_t n) -{ - for (int i = 0; i < n; i++) { - fprintf(f, "%02x", ((const unsigned char *)v)[i]); - } -} - static int signed_test(struct signed_test *test, int m4, int m5, const void *v1_exp, bool xi_exp, const void *v2, const void *v3) @@ -320,10 +238,28 @@ static int signed_test(struct signed_test *test, int m4, int m5, return 0; } -static void snan_to_qnan(char *v, int m4) +struct iter { + int cls[2]; + int val[2]; +}; + +static bool iter_next(struct iter *it, int fmt) { - size_t bit = 1 + e_bits[m4 - 2]; - v[bit / 8] |= 1 << (7 - (bit % 8)); + int i; + + for (i = 1; i >= 0; i--) { + if (++it->val[i] != signed_floats[fmt][it->cls[i]].n) { + return true; + } + it->val[i] = 0; + + if (++it->cls[i] != N_SIGNED_CLASSES) { + return true; + } + it->cls[i] = 0; + } + + return false; } int main(void) @@ -333,72 +269,71 @@ int main(void) for (i = 0; i < sizeof(signed_tests) / sizeof(signed_tests[0]); i++) { struct signed_test *test = &signed_tests[i]; - int m4; + int fmt; - for (m4 = 2; m4 <= 4; m4++) { - const unsigned char (*floats)[2][16] = signed_floats[m4 - 2]; - size_t float_size = float_sizes[m4 - 2]; + for (fmt = 0; fmt < N_FORMATS; fmt++) { + size_t float_size = float_sizes[fmt]; + int m4 = fmt + 2; int m5; for (m5 = 0; m5 <= 8; m5 += 8) { char v1_exp[16], v2[16], v3[16]; bool xi_exp = false; + struct iter it = {}; int pos = 0; - int i2; - for (i2 = 0; i2 < N_SIGNED_CLASSES * 2; i2++) { - int i3; + do { + const char *spec = test->table[it.cls[0]][it.cls[1]]; - for (i3 = 0; i3 < N_SIGNED_CLASSES * 2; i3++) { - const char *spec = test->table[i2 / 2][i3 / 2]; + memcpy(&v2[pos], + signed_floats[fmt][it.cls[0]].v[it.val[0]], + float_size); + memcpy(&v3[pos], + signed_floats[fmt][it.cls[1]].v[it.val[1]], + float_size); + if (strcmp(spec, "T(a)") == 0 || + strcmp(spec, "Xi: T(a)") == 0) { + memcpy(&v1_exp[pos], &v2[pos], float_size); + } else if (strcmp(spec, "T(b)") == 0 || + strcmp(spec, "Xi: T(b)") == 0) { + memcpy(&v1_exp[pos], &v3[pos], float_size); + } else if (strcmp(spec, "Xi: T(a*)") == 0) { + memcpy(&v1_exp[pos], &v2[pos], float_size); + snan_to_qnan(&v1_exp[pos], fmt); + } else if (strcmp(spec, "Xi: T(b*)") == 0) { + memcpy(&v1_exp[pos], &v3[pos], float_size); + snan_to_qnan(&v1_exp[pos], fmt); + } else if (strcmp(spec, "T(M(a,b))") == 0) { + /* + * Comparing floats is risky, since the compiler might + * generate the same instruction that we are testing. + * Compare ints instead. This works, because we get + * here only for +-Fn, and the corresponding test + * values have identical exponents. + */ + int v2_int = *(int *)&v2[pos]; + int v3_int = *(int *)&v3[pos]; - memcpy(&v2[pos], floats[i2 / 2][i2 % 2], float_size); - memcpy(&v3[pos], floats[i3 / 2][i3 % 2], float_size); - if (strcmp(spec, "T(a)") == 0 || - strcmp(spec, "Xi: T(a)") == 0) { + if ((v2_int < v3_int) == + ((test->op == VFMIN) != (v2_int < 0))) { memcpy(&v1_exp[pos], &v2[pos], float_size); - } else if (strcmp(spec, "T(b)") == 0 || - strcmp(spec, "Xi: T(b)") == 0) { - memcpy(&v1_exp[pos], &v3[pos], float_size); - } else if (strcmp(spec, "Xi: T(a*)") == 0) { - memcpy(&v1_exp[pos], &v2[pos], float_size); - snan_to_qnan(&v1_exp[pos], m4); - } else if (strcmp(spec, "Xi: T(b*)") == 0) { - memcpy(&v1_exp[pos], &v3[pos], float_size); - snan_to_qnan(&v1_exp[pos], m4); - } else if (strcmp(spec, "T(M(a,b))") == 0) { - /* - * Comparing floats is risky, since the compiler - * might generate the same instruction that we are - * testing. Compare ints instead. This works, - * because we get here only for +-Fn, and the - * corresponding test values have identical - * exponents. - */ - int v2_int = *(int *)&v2[pos]; - int v3_int = *(int *)&v3[pos]; - - if ((v2_int < v3_int) == - ((test->op == VFMIN) != (v2_int < 0))) { - memcpy(&v1_exp[pos], &v2[pos], float_size); - } else { - memcpy(&v1_exp[pos], &v3[pos], float_size); - } } else { - fprintf(stderr, "Unexpected spec: %s\n", spec); - return 1; + memcpy(&v1_exp[pos], &v3[pos], float_size); } - xi_exp |= spec[0] == 'X'; - pos += float_size; + } else { + fprintf(stderr, "Unexpected spec: %s\n", spec); + return 1; + } + xi_exp |= spec[0] == 'X'; + pos += float_size; - if ((m5 & 8) || pos == 16) { - ret |= signed_test(test, m4, m5, - v1_exp, xi_exp, v2, v3); - pos = 0; - xi_exp = false; - } + if ((m5 & 8) || pos == 16) { + ret |= signed_test(test, m4, m5, + v1_exp, xi_exp, v2, v3); + pos = 0; + xi_exp = false; } - } + } while (iter_next(&it, fmt)); if (pos != 0) { ret |= signed_test(test, m4, m5, v1_exp, xi_exp, v2, v3); diff --git a/tests/tcg/x86_64/Makefile.softmmu-target b/tests/tcg/x86_64/Makefile.softmmu-target index ef6bcb4..3e30ca9 100644 --- a/tests/tcg/x86_64/Makefile.softmmu-target +++ b/tests/tcg/x86_64/Makefile.softmmu-target @@ -1,13 +1,11 @@ # -# x86 system tests -# -# This currently builds only for i386. The common C code is built -# with standard compiler flags however so we can support both by -# adding additional boot files for x86_64. +# x86_64 system tests # -I386_SYSTEM_SRC=$(SRC_PATH)/tests/tcg/i386/system X64_SYSTEM_SRC=$(SRC_PATH)/tests/tcg/x86_64/system +X64_SYSTEM_TESTS=$(patsubst $(X64_SYSTEM_SRC)/%.c, %, $(wildcard $(X64_SYSTEM_SRC)/*.c)) + +VPATH+=$(X64_SYSTEM_SRC) # These objects provide the basic boot code and helper functions for all tests CRT_OBJS=boot.o @@ -18,7 +16,7 @@ LDFLAGS=-Wl,-T$(LINK_SCRIPT) -Wl,-melf_x86_64 CFLAGS+=-nostdlib -ggdb -O0 $(MINILIB_INC) LDFLAGS+=-static -nostdlib $(CRT_OBJS) $(MINILIB_OBJS) -lgcc -TESTS+=$(MULTIARCH_TESTS) +TESTS+=$(MULTIARCH_TESTS) $(X64_SYSTEM_TESTS) EXTRA_RUNS+=$(MULTIARCH_RUNS) # building head blobs @@ -35,3 +33,12 @@ memory: CFLAGS+=-DCHECK_UNALIGNED=1 # Running QEMU_OPTS+=-device isa-debugcon,chardev=output -device isa-debug-exit,iobase=0xf4,iosize=0x4 -kernel + +ifeq ($(CONFIG_PLUGIN),y) +run-plugin-patch-target-with-libpatch.so: \ + PLUGIN_ARGS=$(COMMA)target=ffc0$(COMMA)patch=9090$(COMMA)use_hwaddr=true +run-plugin-patch-target-with-libpatch.so: \ + CHECK_PLUGIN_OUTPUT_COMMAND=$(X64_SYSTEM_SRC)/validate-patch.py $@.out +run-plugin-patch-target-with-libpatch.so: patch-target libpatch.so +EXTRA_RUNS+=run-plugin-patch-target-with-libpatch.so +endif diff --git a/tests/tcg/x86_64/Makefile.target b/tests/tcg/x86_64/Makefile.target index 783ab5b..be20fc6 100644 --- a/tests/tcg/x86_64/Makefile.target +++ b/tests/tcg/x86_64/Makefile.target @@ -17,6 +17,8 @@ X86_64_TESTS += cmpxchg X86_64_TESTS += adox X86_64_TESTS += test-1648 X86_64_TESTS += test-2175 +X86_64_TESTS += cross-modifying-code +X86_64_TESTS += fma TESTS=$(MULTIARCH_TESTS) $(X86_64_TESTS) test-x86_64 else TESTS=$(MULTIARCH_TESTS) @@ -27,6 +29,9 @@ adox: CFLAGS=-O2 run-test-i386-ssse3: QEMU_OPTS += -cpu max run-plugin-test-i386-ssse3-%: QEMU_OPTS += -cpu max +cross-modifying-code: CFLAGS+=-pthread +cross-modifying-code: LDFLAGS+=-pthread + test-x86_64: LDFLAGS+=-lm -lc test-x86_64: test-i386.c test-i386.h test-i386-shift.h test-i386-muldiv.h $(CC) $(CFLAGS) $< -o $@ $(LDFLAGS) diff --git a/tests/tcg/x86_64/cross-modifying-code.c b/tests/tcg/x86_64/cross-modifying-code.c new file mode 100644 index 0000000..2704df6 --- /dev/null +++ b/tests/tcg/x86_64/cross-modifying-code.c @@ -0,0 +1,80 @@ +/* + * Test patching code, running in one thread, from another thread. + * + * Intel SDM calls this "cross-modifying code" and recommends a special + * sequence, which requires both threads to cooperate. + * + * Linux kernel uses a different sequence that does not require cooperation and + * involves patching the first byte with int3. + * + * Finally, there is user-mode software out there that simply uses atomics, and + * that seems to be good enough in practice. Test that QEMU has no problems + * with this as well. + */ + +#include <assert.h> +#include <pthread.h> +#include <stdbool.h> +#include <stdlib.h> + +void add1_or_nop(long *x); +asm(".pushsection .rwx,\"awx\",@progbits\n" + ".globl add1_or_nop\n" + /* addq $0x1,(%rdi) */ + "add1_or_nop: .byte 0x48, 0x83, 0x07, 0x01\n" + "ret\n" + ".popsection\n"); + +#define THREAD_WAIT 0 +#define THREAD_PATCH 1 +#define THREAD_STOP 2 + +static void *thread_func(void *arg) +{ + int val = 0x0026748d; /* nop */ + + while (true) { + switch (__atomic_load_n((int *)arg, __ATOMIC_SEQ_CST)) { + case THREAD_WAIT: + break; + case THREAD_PATCH: + val = __atomic_exchange_n((int *)&add1_or_nop, val, + __ATOMIC_SEQ_CST); + break; + case THREAD_STOP: + return NULL; + default: + assert(false); + __builtin_unreachable(); + } + } +} + +#define INITIAL 42 +#define COUNT 1000000 + +int main(void) +{ + int command = THREAD_WAIT; + pthread_t thread; + long x = 0; + int err; + int i; + + err = pthread_create(&thread, NULL, &thread_func, &command); + assert(err == 0); + + __atomic_store_n(&command, THREAD_PATCH, __ATOMIC_SEQ_CST); + for (i = 0; i < COUNT; i++) { + add1_or_nop(&x); + } + __atomic_store_n(&command, THREAD_STOP, __ATOMIC_SEQ_CST); + + err = pthread_join(thread, NULL); + assert(err == 0); + + assert(x >= INITIAL); + assert(x <= INITIAL + COUNT); + + return EXIT_SUCCESS; +} diff --git a/tests/tcg/x86_64/fma.c b/tests/tcg/x86_64/fma.c new file mode 100644 index 0000000..3421961 --- /dev/null +++ b/tests/tcg/x86_64/fma.c @@ -0,0 +1,116 @@ +/* + * Test some fused multiply add corner cases. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#include <stdio.h> +#include <stdint.h> +#include <stdbool.h> +#include <inttypes.h> + +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) + +/* + * Perform one "n * m + a" operation using the vfmadd insn and return + * the result; on return *mxcsr_p is set to the bottom 6 bits of MXCSR + * (the Flag bits). If ftz is true then we set MXCSR.FTZ while doing + * the operation. + * We print the operation and its results to stdout. + */ +static uint64_t do_fmadd(uint64_t n, uint64_t m, uint64_t a, + bool ftz, uint32_t *mxcsr_p) +{ + uint64_t r; + uint32_t mxcsr = 0; + uint32_t ftz_bit = ftz ? (1 << 15) : 0; + uint32_t saved_mxcsr = 0; + + asm volatile("stmxcsr %[saved_mxcsr]\n" + "stmxcsr %[mxcsr]\n" + "andl $0xffff7fc0, %[mxcsr]\n" + "orl %[ftz_bit], %[mxcsr]\n" + "ldmxcsr %[mxcsr]\n" + "movq %[a], %%xmm0\n" + "movq %[m], %%xmm1\n" + "movq %[n], %%xmm2\n" + /* xmm0 = xmm0 + xmm2 * xmm1 */ + "vfmadd231sd %%xmm1, %%xmm2, %%xmm0\n" + "movq %%xmm0, %[r]\n" + "stmxcsr %[mxcsr]\n" + "ldmxcsr %[saved_mxcsr]\n" + : [r] "=r" (r), [mxcsr] "=m" (mxcsr), + [saved_mxcsr] "=m" (saved_mxcsr) + : [n] "r" (n), [m] "r" (m), [a] "r" (a), + [ftz_bit] "r" (ftz_bit) + : "xmm0", "xmm1", "xmm2"); + *mxcsr_p = mxcsr & 0x3f; + printf("vfmadd132sd 0x%" PRIx64 " 0x%" PRIx64 " 0x%" PRIx64 + " = 0x%" PRIx64 " MXCSR flags 0x%" PRIx32 "\n", + n, m, a, r, *mxcsr_p); + return r; +} + +typedef struct testdata { + /* Input n, m, a */ + uint64_t n; + uint64_t m; + uint64_t a; + bool ftz; + /* Expected result */ + uint64_t expected_r; + /* Expected low 6 bits of MXCSR (the Flag bits) */ + uint32_t expected_mxcsr; +} testdata; + +static testdata tests[] = { + { 0, 0x7ff0000000000000, 0x7ff000000000aaaa, false, /* 0 * Inf + SNaN */ + 0x7ff800000000aaaa, 1 }, /* Should be QNaN and does raise Invalid */ + { 0, 0x7ff0000000000000, 0x7ff800000000aaaa, false, /* 0 * Inf + QNaN */ + 0x7ff800000000aaaa, 0 }, /* Should be QNaN and does *not* raise Invalid */ + /* + * These inputs give a result which is tiny before rounding but which + * becomes non-tiny after rounding. x86 is a "detect tininess after + * rounding" architecture, so it should give a non-denormal result and + * not set the Underflow flag (only the Precision flag for an inexact + * result). + */ + { 0x3fdfffffffffffff, 0x001fffffffffffff, 0x801fffffffffffff, false, + 0x8010000000000000, 0x20 }, + /* + * Flushing of denormal outputs to zero should also happen after + * rounding, so setting FTZ should not affect the result or the flags. + */ + { 0x3fdfffffffffffff, 0x001fffffffffffff, 0x801fffffffffffff, true, + 0x8010000000000000, 0x20 }, /* Enabling FTZ shouldn't change flags */ + /* + * normal * 0 + a denormal. With FTZ disabled this gives an exact + * result (equal to the input denormal) that has consumed the denormal. + */ + { 0x3cc8000000000000, 0x0000000000000000, 0x8008000000000000, false, + 0x8008000000000000, 0x2 }, /* Denormal */ + /* + * With FTZ enabled, this consumes the denormal, returns zero (because + * flushed) and indicates also Underflow and Precision. + */ + { 0x3cc8000000000000, 0x0000000000000000, 0x8008000000000000, true, + 0x8000000000000000, 0x32 }, /* Precision, Underflow, Denormal */ +}; + +int main(void) +{ + bool passed = true; + for (int i = 0; i < ARRAY_SIZE(tests); i++) { + uint32_t mxcsr; + uint64_t r = do_fmadd(tests[i].n, tests[i].m, tests[i].a, + tests[i].ftz, &mxcsr); + if (r != tests[i].expected_r) { + printf("expected result 0x%" PRIx64 "\n", tests[i].expected_r); + passed = false; + } + if (mxcsr != tests[i].expected_mxcsr) { + printf("expected MXCSR flags 0x%x\n", tests[i].expected_mxcsr); + passed = false; + } + } + return passed ? 0 : 1; +} diff --git a/tests/tcg/x86_64/system/patch-target.c b/tests/tcg/x86_64/system/patch-target.c new file mode 100644 index 0000000..8c2b6f4 --- /dev/null +++ b/tests/tcg/x86_64/system/patch-target.c @@ -0,0 +1,22 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * + * This test target increments a value 100 times. The patcher converts the + * inc instruction to a nop, so it only increments the value once. + * + */ +#include <minilib.h> + +int main(void) +{ + ml_printf("Running test...\n"); + unsigned int x = 0; + for (int i = 0; i < 100; i++) { + asm volatile ( + "inc %[x]" + : [x] "+a" (x) + ); + } + ml_printf("Value: %d\n", x); + return 0; +} diff --git a/tests/tcg/x86_64/system/validate-patch.py b/tests/tcg/x86_64/system/validate-patch.py new file mode 100755 index 0000000..700950e --- /dev/null +++ b/tests/tcg/x86_64/system/validate-patch.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python3 +# +# validate-patch.py: check the patch applies +# +# This program takes two inputs: +# - the plugin output +# - the binary output +# +# Copyright (C) 2024 +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import sys +from argparse import ArgumentParser + +def main() -> None: + """ + Process the arguments, injest the program and plugin out and + verify they match up and report if they do not. + """ + parser = ArgumentParser(description="Validate patch") + parser.add_argument('test_output', + help="The output from the test itself") + parser.add_argument('plugin_output', + help="The output from plugin") + args = parser.parse_args() + + with open(args.test_output, 'r') as f: + test_data = f.read() + with open(args.plugin_output, 'r') as f: + plugin_data = f.read() + if "Value: 1" in test_data: + sys.exit(0) + else: + sys.exit(1) + +if __name__ == "__main__": + main() + diff --git a/tests/uefi-test-tools/Makefile b/tests/uefi-test-tools/Makefile index f4eaebd..8ee6fb3 100644 --- a/tests/uefi-test-tools/Makefile +++ b/tests/uefi-test-tools/Makefile @@ -12,7 +12,7 @@ edk2_dir := ../../roms/edk2 images_dir := ../data/uefi-boot-images -emulation_targets := arm aarch64 i386 x86_64 riscv64 +emulation_targets := arm aarch64 i386 x86_64 riscv64 loongarch64 uefi_binaries := bios-tables-test intermediate_suffixes := .efi .fat .iso.raw @@ -56,7 +56,8 @@ Build/%.iso.raw: Build/%.fat # stripped from, the argument. map_arm_to_uefi = $(subst arm,ARM,$(1)) map_aarch64_to_uefi = $(subst aarch64,AA64,$(call map_arm_to_uefi,$(1))) -map_riscv64_to_uefi = $(subst riscv64,RISCV64,$(call map_aarch64_to_uefi,$(1))) +map_loongarch64_to_uefi = $(subst loongarch64,LOONGARCH64,$(call map_aarch64_to_uefi,$(1))) +map_riscv64_to_uefi = $(subst riscv64,RISCV64,$(call map_loongarch64_to_uefi,$(1))) map_i386_to_uefi = $(subst i386,IA32,$(call map_riscv64_to_uefi,$(1))) map_x86_64_to_uefi = $(subst x86_64,X64,$(call map_i386_to_uefi,$(1))) map_to_uefi = $(subst .,,$(call map_x86_64_to_uefi,$(1))) diff --git a/tests/uefi-test-tools/UefiTestToolsPkg/UefiTestToolsPkg.dsc b/tests/uefi-test-tools/UefiTestToolsPkg/UefiTestToolsPkg.dsc index 0902fd3..facf8df 100644 --- a/tests/uefi-test-tools/UefiTestToolsPkg/UefiTestToolsPkg.dsc +++ b/tests/uefi-test-tools/UefiTestToolsPkg/UefiTestToolsPkg.dsc @@ -19,7 +19,7 @@ PLATFORM_VERSION = 0.1 PLATFORM_NAME = UefiTestTools SKUID_IDENTIFIER = DEFAULT - SUPPORTED_ARCHITECTURES = ARM|AARCH64|IA32|X64|RISCV64 + SUPPORTED_ARCHITECTURES = ARM|AARCH64|IA32|X64|RISCV64|LOONGARCH64 BUILD_TARGETS = DEBUG [BuildOptions.IA32] @@ -65,6 +65,10 @@ [LibraryClasses.RISCV64] BaseMemoryLib|MdePkg/Library/BaseMemoryLib/BaseMemoryLib.inf +[LibraryClasses.LOONGARCH64] + BaseMemoryLib|MdePkg/Library/BaseMemoryLib/BaseMemoryLib.inf + StackCheckLib|MdePkg/Library/StackCheckLibNull/StackCheckLibNull.inf + [PcdsFixedAtBuild] gEfiMdePkgTokenSpaceGuid.PcdDebugPrintErrorLevel|0x8040004F gEfiMdePkgTokenSpaceGuid.PcdDebugPropertyMask|0x2F diff --git a/tests/uefi-test-tools/uefi-test-build.config b/tests/uefi-test-tools/uefi-test-build.config index a4c61fc..8bf4826 100644 --- a/tests/uefi-test-tools/uefi-test-build.config +++ b/tests/uefi-test-tools/uefi-test-build.config @@ -22,6 +22,16 @@ arch = AARCH64 cpy1 = AARCH64/BiosTablesTest.efi bios-tables-test.aarch64.efi #################################################################################### +# loongarch64 + +[build.loongarch64] +conf = UefiTestToolsPkg/UefiTestToolsPkg.dsc +plat = UefiTestTools +dest = ./Build +arch = LOONGARCH64 +cpy1 = LOONGARCH64/BiosTablesTest.efi bios-tables-test.loongarch64.efi + +#################################################################################### # riscv64 [build.riscv64] diff --git a/tests/unit/check-block-qdict.c b/tests/unit/check-block-qdict.c index 751c58e..0036d85 100644 --- a/tests/unit/check-block-qdict.c +++ b/tests/unit/check-block-qdict.c @@ -9,8 +9,8 @@ #include "qemu/osdep.h" #include "block/qdict.h" -#include "qapi/qmp/qlist.h" -#include "qapi/qmp/qnum.h" +#include "qobject/qlist.h" +#include "qobject/qnum.h" #include "qapi/error.h" static void qdict_defaults_test(void) diff --git a/tests/unit/check-qdict.c b/tests/unit/check-qdict.c index b5efa85..a1312be 100644 --- a/tests/unit/check-qdict.c +++ b/tests/unit/check-qdict.c @@ -11,9 +11,9 @@ */ #include "qemu/osdep.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qnum.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qdict.h" +#include "qobject/qnum.h" +#include "qobject/qstring.h" /* * Public Interface test-cases diff --git a/tests/unit/check-qjson.c b/tests/unit/check-qjson.c index a89293c..780a365 100644 --- a/tests/unit/check-qjson.c +++ b/tests/unit/check-qjson.c @@ -14,12 +14,12 @@ #include "qemu/osdep.h" #include "qapi/error.h" -#include "qapi/qmp/qbool.h" -#include "qapi/qmp/qjson.h" -#include "qapi/qmp/qlit.h" -#include "qapi/qmp/qnull.h" -#include "qapi/qmp/qnum.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qbool.h" +#include "qobject/qjson.h" +#include "qobject/qlit.h" +#include "qobject/qnull.h" +#include "qobject/qnum.h" +#include "qobject/qstring.h" #include "qemu/unicode.h" static QString *from_json_str(const char *jstr, bool single, Error **errp) diff --git a/tests/unit/check-qlist.c b/tests/unit/check-qlist.c index 3cd0ccb..1388aee 100644 --- a/tests/unit/check-qlist.c +++ b/tests/unit/check-qlist.c @@ -11,8 +11,8 @@ */ #include "qemu/osdep.h" -#include "qapi/qmp/qnum.h" -#include "qapi/qmp/qlist.h" +#include "qobject/qnum.h" +#include "qobject/qlist.h" /* * Public Interface test-cases diff --git a/tests/unit/check-qlit.c b/tests/unit/check-qlit.c index bd6798d..ea7a0d9 100644 --- a/tests/unit/check-qlit.c +++ b/tests/unit/check-qlit.c @@ -9,12 +9,12 @@ #include "qemu/osdep.h" -#include "qapi/qmp/qbool.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qlist.h" -#include "qapi/qmp/qlit.h" -#include "qapi/qmp/qnum.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qbool.h" +#include "qobject/qdict.h" +#include "qobject/qlist.h" +#include "qobject/qlit.h" +#include "qobject/qnum.h" +#include "qobject/qstring.h" static QLitObject qlit = QLIT_QDICT(((QLitDictEntry[]) { { "foo", QLIT_QNUM(42) }, diff --git a/tests/unit/check-qnull.c b/tests/unit/check-qnull.c index 5ceacc6..724a66d 100644 --- a/tests/unit/check-qnull.c +++ b/tests/unit/check-qnull.c @@ -8,7 +8,7 @@ */ #include "qemu/osdep.h" -#include "qapi/qmp/qnull.h" +#include "qobject/qnull.h" #include "qapi/qobject-input-visitor.h" #include "qapi/qobject-output-visitor.h" #include "qapi/error.h" diff --git a/tests/unit/check-qnum.c b/tests/unit/check-qnum.c index bf7fe45..a40120e 100644 --- a/tests/unit/check-qnum.c +++ b/tests/unit/check-qnum.c @@ -14,7 +14,7 @@ #include "qemu/osdep.h" -#include "qapi/qmp/qnum.h" +#include "qobject/qnum.h" /* * Public Interface test-cases diff --git a/tests/unit/check-qobject.c b/tests/unit/check-qobject.c index 022b7c7..ccb2566 100644 --- a/tests/unit/check-qobject.c +++ b/tests/unit/check-qobject.c @@ -9,12 +9,12 @@ #include "qemu/osdep.h" #include "qapi/error.h" -#include "qapi/qmp/qbool.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qlist.h" -#include "qapi/qmp/qnull.h" -#include "qapi/qmp/qnum.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qbool.h" +#include "qobject/qdict.h" +#include "qobject/qlist.h" +#include "qobject/qnull.h" +#include "qobject/qnum.h" +#include "qobject/qstring.h" #include <math.h> diff --git a/tests/unit/check-qom-interface.c b/tests/unit/check-qom-interface.c index c99be97..86ae5f6 100644 --- a/tests/unit/check-qom-interface.c +++ b/tests/unit/check-qom-interface.c @@ -38,7 +38,7 @@ static const TypeInfo test_if_info = { #define PATTERN 0xFAFBFCFD -static void test_class_init(ObjectClass *oc, void *data) +static void test_class_init(ObjectClass *oc, const void *data) { TestIfClass *tc = TEST_IF_CLASS(oc); @@ -52,7 +52,7 @@ static const TypeInfo direct_impl_info = { .name = TYPE_DIRECT_IMPL, .parent = TYPE_OBJECT, .class_init = test_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_TEST_IF }, { } } diff --git a/tests/unit/check-qom-proplist.c b/tests/unit/check-qom-proplist.c index 79d4a8b..ee3c6fb 100644 --- a/tests/unit/check-qom-proplist.c +++ b/tests/unit/check-qom-proplist.c @@ -22,8 +22,8 @@ #include "qapi/error.h" #include "qapi/qobject-input-visitor.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qobject.h" +#include "qobject/qdict.h" +#include "qobject/qobject.h" #include "qom/object.h" #include "qemu/module.h" #include "qemu/option.h" @@ -135,7 +135,7 @@ static void dummy_init(Object *obj) } -static void dummy_class_init(ObjectClass *cls, void *data) +static void dummy_class_init(ObjectClass *cls, const void *data) { object_class_property_add_str(cls, "sv", dummy_get_sv, @@ -164,7 +164,7 @@ static const TypeInfo dummy_info = { .instance_finalize = dummy_finalize, .class_size = sizeof(DummyObjectClass), .class_init = dummy_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_USER_CREATABLE }, { } } @@ -264,7 +264,7 @@ static void dummy_dev_unparent(Object *obj) object_unparent(OBJECT(dev->bus)); } -static void dummy_dev_class_init(ObjectClass *klass, void *opaque) +static void dummy_dev_class_init(ObjectClass *klass, const void *opaque) { klass->unparent = dummy_dev_unparent; } @@ -288,7 +288,7 @@ static void dummy_bus_unparent(Object *obj) object_unparent(OBJECT(bus->backend)); } -static void dummy_bus_class_init(ObjectClass *klass, void *opaque) +static void dummy_bus_class_init(ObjectClass *klass, const void *opaque) { klass->unparent = dummy_bus_unparent; } @@ -610,7 +610,7 @@ static void test_dummy_delchild(void) static void test_qom_partial_path(void) { Object *root = object_get_objects_root(); - Object *cont1 = container_get(root, "/cont1"); + Object *cont1 = object_property_add_new_container(root, "cont1"); Object *obj1 = object_new(TYPE_DUMMY); Object *obj2a = object_new(TYPE_DUMMY); Object *obj2b = object_new(TYPE_DUMMY); diff --git a/tests/unit/check-qstring.c b/tests/unit/check-qstring.c index bd861f4..2e6a005 100644 --- a/tests/unit/check-qstring.c +++ b/tests/unit/check-qstring.c @@ -11,7 +11,7 @@ */ #include "qemu/osdep.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qstring.h" /* * Public Interface test-cases diff --git a/tests/unit/meson.build b/tests/unit/meson.build index 972d792..d5248ae 100644 --- a/tests/unit/meson.build +++ b/tests/unit/meson.build @@ -47,6 +47,7 @@ tests = { 'test-logging': [], 'test-qapi-util': [], 'test-interval-tree': [], + 'test-fifo': [], } if have_system or have_tools @@ -115,7 +116,7 @@ if have_block if host_os != 'windows' tests += { 'test-image-locking': [testblock], - 'test-nested-aio-poll': [testblock], + 'test-nested-aio-poll': [], } endif if config_host_data.get('CONFIG_REPLICATION') diff --git a/tests/unit/socket-helpers.c b/tests/unit/socket-helpers.c index f3439cc..37db24f 100644 --- a/tests/unit/socket-helpers.c +++ b/tests/unit/socket-helpers.c @@ -170,5 +170,4 @@ void socket_check_afunix_support(bool *has_afunix) if (*has_afunix) { close(fd); } - return; } diff --git a/tests/unit/test-aio-multithread.c b/tests/unit/test-aio-multithread.c index 08d4570..0ead6bf 100644 --- a/tests/unit/test-aio-multithread.c +++ b/tests/unit/test-aio-multithread.c @@ -305,7 +305,9 @@ static void mcs_mutex_lock(void) prev = qatomic_xchg(&mutex_head, id); if (prev != -1) { qatomic_set(&nodes[prev].next, id); - qemu_futex_wait(&nodes[id].locked, 1); + while (qatomic_read(&nodes[id].locked) == 1) { + qemu_futex_wait(&nodes[id].locked, 1); + } } } @@ -328,7 +330,7 @@ static void mcs_mutex_unlock(void) /* Wake up the next in line. */ next = qatomic_read(&nodes[id].next); nodes[next].locked = 0; - qemu_futex_wake(&nodes[next].locked, 1); + qemu_futex_wake_single(&nodes[next].locked); } static void test_multi_fair_mutex_entry(void *opaque) diff --git a/tests/unit/test-bdrv-drain.c b/tests/unit/test-bdrv-drain.c index 6668804..59c2793 100644 --- a/tests/unit/test-bdrv-drain.c +++ b/tests/unit/test-bdrv-drain.c @@ -25,7 +25,7 @@ #include "qemu/osdep.h" #include "block/block_int.h" #include "block/blockjob_int.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #include "qapi/error.h" #include "qemu/main-loop.h" #include "iothread.h" @@ -632,6 +632,8 @@ typedef struct TestBlockJob { BlockDriverState *bs; int run_ret; int prepare_ret; + + /* Accessed with atomics */ bool running; bool should_complete; } TestBlockJob; @@ -667,10 +669,10 @@ static int coroutine_fn test_job_run(Job *job, Error **errp) /* We are running the actual job code past the pause point in * job_co_entry(). */ - s->running = true; + qatomic_set(&s->running, true); job_transition_to_ready(&s->common.job); - while (!s->should_complete) { + while (!qatomic_read(&s->should_complete)) { /* Avoid job_sleep_ns() because it marks the job as !busy. We want to * emulate some actual activity (probably some I/O) here so that drain * has to wait for this activity to stop. */ @@ -685,7 +687,7 @@ static int coroutine_fn test_job_run(Job *job, Error **errp) static void test_job_complete(Job *job, Error **errp) { TestBlockJob *s = container_of(job, TestBlockJob, common.job); - s->should_complete = true; + qatomic_set(&s->should_complete, true); } BlockJobDriver test_job_driver = { @@ -722,7 +724,7 @@ static void test_blockjob_common_drain_node(enum drain_type drain_type, BlockJob *job; TestBlockJob *tjob; IOThread *iothread = NULL; - int ret; + int ret = -1; src = bdrv_new_open_driver(&bdrv_test, "source", BDRV_O_RDWR, &error_abort); @@ -770,9 +772,11 @@ static void test_blockjob_common_drain_node(enum drain_type drain_type, tjob->bs = src; job = &tjob->common; + bdrv_drain_all_begin(); bdrv_graph_wrlock(); block_job_add_bdrv(job, "target", target, 0, BLK_PERM_ALL, &error_abort); bdrv_graph_wrunlock(); + bdrv_drain_all_end(); switch (result) { case TEST_JOB_SUCCESS: @@ -791,7 +795,7 @@ static void test_blockjob_common_drain_node(enum drain_type drain_type, /* job_co_entry() is run in the I/O thread, wait for the actual job * code to start (we don't want to catch the job in the pause point in * job_co_entry(). */ - while (!tjob->running) { + while (!qatomic_read(&tjob->running)) { aio_poll(qemu_get_aio_context(), false); } } @@ -799,7 +803,7 @@ static void test_blockjob_common_drain_node(enum drain_type drain_type, WITH_JOB_LOCK_GUARD() { g_assert_cmpint(job->job.pause_count, ==, 0); g_assert_false(job->job.paused); - g_assert_true(tjob->running); + g_assert_true(qatomic_read(&tjob->running)); g_assert_true(job->job.busy); /* We're in qemu_co_sleep_ns() */ } @@ -825,7 +829,7 @@ static void test_blockjob_common_drain_node(enum drain_type drain_type, * * paused is reset in the I/O thread, wait for it */ - while (job->job.paused) { + while (job_is_paused(&job->job)) { aio_poll(qemu_get_aio_context(), false); } } @@ -858,7 +862,7 @@ static void test_blockjob_common_drain_node(enum drain_type drain_type, * * paused is reset in the I/O thread, wait for it */ - while (job->job.paused) { + while (job_is_paused(&job->job)) { aio_poll(qemu_get_aio_context(), false); } } @@ -951,11 +955,13 @@ static void bdrv_test_top_close(BlockDriverState *bs) { BdrvChild *c, *next_c; + bdrv_drain_all_begin(); bdrv_graph_wrlock(); QLIST_FOREACH_SAFE(c, &bs->children, next, next_c) { bdrv_unref_child(bs, c); } bdrv_graph_wrunlock(); + bdrv_drain_all_end(); } static int coroutine_fn GRAPH_RDLOCK @@ -1012,7 +1018,9 @@ static void coroutine_fn test_co_delete_by_drain(void *opaque) bdrv_graph_co_rdlock(); QLIST_FOREACH_SAFE(c, &bs->children, next, next_c) { bdrv_graph_co_rdunlock(); + bdrv_drain_all_begin(); bdrv_co_unref_child(bs, c); + bdrv_drain_all_end(); bdrv_graph_co_rdlock(); } bdrv_graph_co_rdunlock(); @@ -1045,10 +1053,12 @@ static void do_test_delete_by_drain(bool detach_instead_of_delete, null_bs = bdrv_open("null-co://", NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL, &error_abort); + bdrv_drain_all_begin(); bdrv_graph_wrlock(); bdrv_attach_child(bs, null_bs, "null-child", &child_of_bds, BDRV_CHILD_DATA, &error_abort); bdrv_graph_wrunlock(); + bdrv_drain_all_end(); /* This child will be the one to pass to requests through to, and * it will stall until a drain occurs */ @@ -1056,21 +1066,25 @@ static void do_test_delete_by_drain(bool detach_instead_of_delete, &error_abort); child_bs->total_sectors = 65536 >> BDRV_SECTOR_BITS; /* Takes our reference to child_bs */ + bdrv_drain_all_begin(); bdrv_graph_wrlock(); tts->wait_child = bdrv_attach_child(bs, child_bs, "wait-child", &child_of_bds, BDRV_CHILD_DATA | BDRV_CHILD_PRIMARY, &error_abort); bdrv_graph_wrunlock(); + bdrv_drain_all_end(); /* This child is just there to be deleted * (for detach_instead_of_delete == true) */ null_bs = bdrv_open("null-co://", NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL, &error_abort); + bdrv_drain_all_begin(); bdrv_graph_wrlock(); bdrv_attach_child(bs, null_bs, "null-child", &child_of_bds, BDRV_CHILD_DATA, &error_abort); bdrv_graph_wrunlock(); + bdrv_drain_all_end(); blk = blk_new(qemu_get_aio_context(), BLK_PERM_ALL, BLK_PERM_ALL); blk_insert_bs(blk, bs, &error_abort); @@ -1153,6 +1167,7 @@ static void no_coroutine_fn detach_indirect_bh(void *opaque) bdrv_dec_in_flight(data->child_b->bs); + bdrv_drain_all_begin(); bdrv_graph_wrlock(); bdrv_unref_child(data->parent_b, data->child_b); @@ -1161,6 +1176,7 @@ static void no_coroutine_fn detach_indirect_bh(void *opaque) &child_of_bds, BDRV_CHILD_DATA, &error_abort); bdrv_graph_wrunlock(); + bdrv_drain_all_end(); } static void coroutine_mixed_fn detach_by_parent_aio_cb(void *opaque, int ret) @@ -1258,6 +1274,7 @@ static void TSA_NO_TSA test_detach_indirect(bool by_parent_cb) /* Set child relationships */ bdrv_ref(b); bdrv_ref(a); + bdrv_drain_all_begin(); bdrv_graph_wrlock(); child_b = bdrv_attach_child(parent_b, b, "PB-B", &child_of_bds, BDRV_CHILD_DATA, &error_abort); @@ -1269,6 +1286,7 @@ static void TSA_NO_TSA test_detach_indirect(bool by_parent_cb) by_parent_cb ? &child_of_bds : &detach_by_driver_cb_class, BDRV_CHILD_DATA, &error_abort); bdrv_graph_wrunlock(); + bdrv_drain_all_end(); g_assert_cmpint(parent_a->refcnt, ==, 1); g_assert_cmpint(parent_b->refcnt, ==, 1); @@ -1394,14 +1412,10 @@ static void test_set_aio_context(void) bs = bdrv_new_open_driver(&bdrv_test, "test-node", BDRV_O_RDWR, &error_abort); - bdrv_drained_begin(bs); bdrv_try_change_aio_context(bs, ctx_a, NULL, &error_abort); - bdrv_drained_end(bs); - bdrv_drained_begin(bs); bdrv_try_change_aio_context(bs, ctx_b, NULL, &error_abort); bdrv_try_change_aio_context(bs, qemu_get_aio_context(), NULL, &error_abort); - bdrv_drained_end(bs); bdrv_unref(bs); iothread_join(a); @@ -1411,10 +1425,12 @@ static void test_set_aio_context(void) typedef struct TestDropBackingBlockJob { BlockJob common; - bool should_complete; bool *did_complete; BlockDriverState *detach_also; BlockDriverState *bs; + + /* Accessed with atomics */ + bool should_complete; } TestDropBackingBlockJob; static int coroutine_fn test_drop_backing_job_run(Job *job, Error **errp) @@ -1422,7 +1438,7 @@ static int coroutine_fn test_drop_backing_job_run(Job *job, Error **errp) TestDropBackingBlockJob *s = container_of(job, TestDropBackingBlockJob, common.job); - while (!s->should_complete) { + while (!qatomic_read(&s->should_complete)) { job_sleep_ns(job, 0); } @@ -1541,7 +1557,7 @@ static void test_blockjob_commit_by_drained_end(void) job_start(&job->common.job); - job->should_complete = true; + qatomic_set(&job->should_complete, true); bdrv_drained_begin(bs_child); g_assert(!job_has_completed); bdrv_drained_end(bs_child); @@ -1557,15 +1573,17 @@ static void test_blockjob_commit_by_drained_end(void) typedef struct TestSimpleBlockJob { BlockJob common; - bool should_complete; bool *did_complete; + + /* Accessed with atomics */ + bool should_complete; } TestSimpleBlockJob; static int coroutine_fn test_simple_job_run(Job *job, Error **errp) { TestSimpleBlockJob *s = container_of(job, TestSimpleBlockJob, common.job); - while (!s->should_complete) { + while (!qatomic_read(&s->should_complete)) { job_sleep_ns(job, 0); } @@ -1681,6 +1699,7 @@ static void test_drop_intermediate_poll(void) * Establish the chain last, so the chain links are the first * elements in the BDS.parents lists */ + bdrv_drain_all_begin(); bdrv_graph_wrlock(); for (i = 0; i < 3; i++) { if (i) { @@ -1690,6 +1709,7 @@ static void test_drop_intermediate_poll(void) } } bdrv_graph_wrunlock(); + bdrv_drain_all_end(); job = block_job_create("job", &test_simple_job_driver, NULL, job_node, 0, BLK_PERM_ALL, 0, 0, NULL, NULL, &error_abort); @@ -1700,7 +1720,7 @@ static void test_drop_intermediate_poll(void) job->did_complete = &job_has_completed; job_start(&job->common.job); - job->should_complete = true; + qatomic_set(&job->should_complete, true); g_assert(!job_has_completed); ret = bdrv_drop_intermediate(chain[1], chain[0], NULL, false); @@ -1936,10 +1956,12 @@ static void do_test_replace_child_mid_drain(int old_drain_count, new_child_bs->total_sectors = 1; bdrv_ref(old_child_bs); + bdrv_drain_all_begin(); bdrv_graph_wrlock(); bdrv_attach_child(parent_bs, old_child_bs, "child", &child_of_bds, BDRV_CHILD_COW, &error_abort); bdrv_graph_wrunlock(); + bdrv_drain_all_end(); parent_s->setup_completed = true; for (i = 0; i < old_drain_count; i++) { diff --git a/tests/unit/test-bdrv-graph-mod.c b/tests/unit/test-bdrv-graph-mod.c index cafc023..7b03ebe 100644 --- a/tests/unit/test-bdrv-graph-mod.c +++ b/tests/unit/test-bdrv-graph-mod.c @@ -22,7 +22,7 @@ #include "qapi/error.h" #include "qemu/main-loop.h" #include "block/block_int.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" static BlockDriver bdrv_pass_through = { .format_name = "pass-through", @@ -137,10 +137,12 @@ static void test_update_perm_tree(void) blk_insert_bs(root, bs, &error_abort); + bdrv_drain_all_begin(); bdrv_graph_wrlock(); bdrv_attach_child(filter, bs, "child", &child_of_bds, BDRV_CHILD_DATA, &error_abort); bdrv_graph_wrunlock(); + bdrv_drain_all_end(); ret = bdrv_append(filter, bs, NULL); g_assert_cmpint(ret, <, 0); @@ -204,11 +206,13 @@ static void test_should_update_child(void) bdrv_set_backing_hd(target, bs, &error_abort); + bdrv_drain_all_begin(); bdrv_graph_wrlock(); g_assert(target->backing->bs == bs); bdrv_attach_child(filter, target, "target", &child_of_bds, BDRV_CHILD_DATA, &error_abort); bdrv_graph_wrunlock(); + bdrv_drain_all_end(); bdrv_append(filter, bs, &error_abort); bdrv_graph_rdlock_main_loop(); @@ -244,6 +248,7 @@ static void test_parallel_exclusive_write(void) bdrv_ref(base); bdrv_ref(fl1); + bdrv_drain_all_begin(); bdrv_graph_wrlock(); bdrv_attach_child(top, fl1, "backing", &child_of_bds, BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY, @@ -257,6 +262,7 @@ static void test_parallel_exclusive_write(void) bdrv_replace_node(fl1, fl2, &error_abort); bdrv_graph_wrunlock(); + bdrv_drain_all_end(); bdrv_drained_end(fl2); bdrv_drained_end(fl1); @@ -363,6 +369,7 @@ static void test_parallel_perm_update(void) */ bdrv_ref(base); + bdrv_drain_all_begin(); bdrv_graph_wrlock(); bdrv_attach_child(top, ws, "file", &child_of_bds, BDRV_CHILD_DATA, &error_abort); @@ -377,6 +384,7 @@ static void test_parallel_perm_update(void) BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY, &error_abort); bdrv_graph_wrunlock(); + bdrv_drain_all_end(); /* Select fl1 as first child to be active */ s->selected = c_fl1; @@ -430,11 +438,13 @@ static void test_append_greedy_filter(void) BlockDriverState *base = no_perm_node("base"); BlockDriverState *fl = exclusive_writer_node("fl1"); + bdrv_drain_all_begin(); bdrv_graph_wrlock(); bdrv_attach_child(top, base, "backing", &child_of_bds, BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY, &error_abort); bdrv_graph_wrunlock(); + bdrv_drain_all_end(); bdrv_append(fl, base, &error_abort); bdrv_unref(fl); diff --git a/tests/unit/test-block-backend.c b/tests/unit/test-block-backend.c index 2fb1a44..4257b3f 100644 --- a/tests/unit/test-block-backend.c +++ b/tests/unit/test-block-backend.c @@ -24,7 +24,7 @@ #include "qemu/osdep.h" #include "block/block.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #include "qapi/error.h" #include "qemu/main-loop.h" diff --git a/tests/unit/test-block-iothread.c b/tests/unit/test-block-iothread.c index 3766d5d..e26b3be 100644 --- a/tests/unit/test-block-iothread.c +++ b/tests/unit/test-block-iothread.c @@ -26,9 +26,9 @@ #include "block/block.h" #include "block/block_int-global-state.h" #include "block/blockjob_int.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #include "qapi/error.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "qemu/main-loop.h" #include "iothread.h" @@ -63,7 +63,7 @@ bdrv_test_co_truncate(BlockDriverState *bs, int64_t offset, bool exact, } static int coroutine_fn bdrv_test_co_block_status(BlockDriverState *bs, - bool want_zero, + unsigned int mode, int64_t offset, int64_t count, int64_t *pnum, int64_t *map, BlockDriverState **file) @@ -745,7 +745,7 @@ static void test_propagate_mirror(void) AioContext *main_ctx = qemu_get_aio_context(); BlockDriverState *src, *target, *filter; BlockBackend *blk; - Job *job; + Job *job = NULL; Error *local_err = NULL; /* Create src and target*/ diff --git a/tests/unit/test-blockjob-txn.c b/tests/unit/test-blockjob-txn.c index d3b0bb2..118503a 100644 --- a/tests/unit/test-blockjob-txn.c +++ b/tests/unit/test-blockjob-txn.c @@ -14,8 +14,8 @@ #include "qapi/error.h" #include "qemu/main-loop.h" #include "block/blockjob_int.h" -#include "sysemu/block-backend.h" -#include "qapi/qmp/qdict.h" +#include "system/block-backend.h" +#include "qobject/qdict.h" typedef struct { BlockJob common; diff --git a/tests/unit/test-blockjob.c b/tests/unit/test-blockjob.c index fe3e0d2..abdbe4b 100644 --- a/tests/unit/test-blockjob.c +++ b/tests/unit/test-blockjob.c @@ -14,8 +14,8 @@ #include "qapi/error.h" #include "qemu/main-loop.h" #include "block/blockjob_int.h" -#include "sysemu/block-backend.h" -#include "qapi/qmp/qdict.h" +#include "system/block-backend.h" +#include "qobject/qdict.h" #include "iothread.h" static const BlockJobDriver test_block_job_driver = { diff --git a/tests/unit/test-char.c b/tests/unit/test-char.c index f273ce5..f30a39f 100644 --- a/tests/unit/test-char.c +++ b/tests/unit/test-char.c @@ -1,15 +1,16 @@ #include "qemu/osdep.h" #include <glib/gstdio.h> +#include "qapi/error.h" #include "qemu/config-file.h" #include "qemu/module.h" #include "qemu/option.h" #include "qemu/sockets.h" #include "chardev/char-fe.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "qapi/error.h" #include "qapi/qapi-commands-char.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "qom/qom-qobject.h" #include "io/channel-socket.h" #include "qapi/qobject-input-visitor.h" @@ -184,6 +185,21 @@ static void char_mux_test(void) char *data; FeHandler h1 = { 0, false, 0, false, }, h2 = { 0, false, 0, false, }; CharBackend chr_be1, chr_be2; + Error *error = NULL; + + /* Create mux and chardev to be immediately removed */ + opts = qemu_opts_create(qemu_find_opts("chardev"), "mux-label", + 1, &error_abort); + qemu_opt_set(opts, "backend", "ringbuf", &error_abort); + qemu_opt_set(opts, "size", "128", &error_abort); + qemu_opt_set(opts, "mux", "on", &error_abort); + chr = qemu_chr_new_from_opts(opts, NULL, &error_abort); + g_assert_nonnull(chr); + qemu_opts_del(opts); + + /* Remove just created mux and chardev */ + qmp_chardev_remove("mux-label", &error_abort); + qmp_chardev_remove("mux-label-base", &error_abort); opts = qemu_opts_create(qemu_find_opts("chardev"), "mux-label", 1, &error_abort); @@ -334,9 +350,412 @@ static void char_mux_test(void) g_free(data); qemu_chr_fe_deinit(&chr_be1, false); - qemu_chr_fe_deinit(&chr_be2, true); + + qmp_chardev_remove("mux-label", &error); + g_assert_cmpstr(error_get_pretty(error), ==, "Chardev 'mux-label' is busy"); + error_free(error); + + qemu_chr_fe_deinit(&chr_be2, false); + qmp_chardev_remove("mux-label", &error_abort); } +static void char_hub_test(void) +{ + QemuOpts *opts; + Chardev *hub, *chr1, *chr2, *base; + char *data; + FeHandler h = { 0, false, 0, false, }; + Error *error = NULL; + CharBackend chr_be; + int ret, i; + +#define RB_SIZE 128 + + /* + * Create invalid hub + * 1. Create hub without a 'chardevs.N' defined (expect error) + */ + opts = qemu_opts_create(qemu_find_opts("chardev"), "hub0", + 1, &error_abort); + qemu_opt_set(opts, "backend", "hub", &error_abort); + hub = qemu_chr_new_from_opts(opts, NULL, &error); + g_assert_cmpstr(error_get_pretty(error), ==, + "hub: 'chardevs' list is not defined"); + error_free(error); + error = NULL; + qemu_opts_del(opts); + + /* + * Create invalid hub + * 1. Create chardev with embedded mux: 'mux=on' + * 2. Create hub which refers mux + * 3. Create hub which refers chardev already attached + * to the mux (already in use, expect error) + */ + opts = qemu_opts_create(qemu_find_opts("chardev"), "chr0", + 1, &error_abort); + qemu_opt_set(opts, "mux", "on", &error_abort); + qemu_opt_set(opts, "backend", "ringbuf", &error_abort); + qemu_opt_set(opts, "size", stringify(RB_SIZE), &error_abort); + base = qemu_chr_new_from_opts(opts, NULL, &error_abort); + g_assert_nonnull(base); + qemu_opts_del(opts); + + opts = qemu_opts_create(qemu_find_opts("chardev"), "hub0", + 1, &error_abort); + qemu_opt_set(opts, "backend", "hub", &error_abort); + qemu_opt_set(opts, "chardevs.0", "chr0", &error_abort); + hub = qemu_chr_new_from_opts(opts, NULL, &error); + g_assert_cmpstr(error_get_pretty(error), ==, + "hub: multiplexers and hub devices can't be " + "stacked, check chardev 'chr0', chardev should " + "not be a hub device or have 'mux=on' enabled"); + error_free(error); + error = NULL; + qemu_opts_del(opts); + + opts = qemu_opts_create(qemu_find_opts("chardev"), "hub0", + 1, &error_abort); + qemu_opt_set(opts, "backend", "hub", &error_abort); + qemu_opt_set(opts, "chardevs.0", "chr0-base", &error_abort); + hub = qemu_chr_new_from_opts(opts, NULL, &error); + g_assert_cmpstr(error_get_pretty(error), ==, + "chardev 'chr0-base' is already in use"); + error_free(error); + error = NULL; + qemu_opts_del(opts); + + /* Finalize chr0 */ + qmp_chardev_remove("chr0", &error_abort); + + /* + * Create invalid hub with more than maximum allowed backends + * 1. Create more than maximum allowed 'chardevs.%d' options for + * hub (expect error) + */ + opts = qemu_opts_create(qemu_find_opts("chardev"), "hub0", + 1, &error_abort); + for (i = 0; i < 10; i++) { + char key[32], val[32]; + + snprintf(key, sizeof(key), "chardevs.%d", i); + snprintf(val, sizeof(val), "chr%d", i); + qemu_opt_set(opts, key, val, &error); + if (error) { + char buf[64]; + + snprintf(buf, sizeof(buf), "Invalid parameter 'chardevs.%d'", i); + g_assert_cmpstr(error_get_pretty(error), ==, buf); + error_free(error); + break; + } + } + g_assert_nonnull(error); + error = NULL; + qemu_opts_del(opts); + + /* + * Create hub with 2 backend chardevs and 1 frontend and perform + * data aggregation + * 1. Create 2 ringbuf backend chardevs + * 2. Create 1 frontend + * 3. Create hub which refers 2 backend chardevs + * 4. Attach hub to a frontend + * 5. Attach hub to a frontend second time (expect error) + * 6. Perform data aggregation + * 7. Remove chr1 ("chr1 is busy", expect error) + * 8. Remove hub0 ("hub0 is busy", expect error); + * 9. Finilize frontend, hub and backend chardevs in correct order + */ + + /* Create first chardev */ + opts = qemu_opts_create(qemu_find_opts("chardev"), "chr1", + 1, &error_abort); + qemu_opt_set(opts, "backend", "ringbuf", &error_abort); + qemu_opt_set(opts, "size", stringify(RB_SIZE), &error_abort); + chr1 = qemu_chr_new_from_opts(opts, NULL, &error_abort); + g_assert_nonnull(chr1); + qemu_opts_del(opts); + + /* Create second chardev */ + opts = qemu_opts_create(qemu_find_opts("chardev"), "chr2", + 1, &error_abort); + qemu_opt_set(opts, "backend", "ringbuf", &error_abort); + qemu_opt_set(opts, "size", stringify(RB_SIZE), &error_abort); + chr2 = qemu_chr_new_from_opts(opts, NULL, &error_abort); + g_assert_nonnull(chr2); + qemu_opts_del(opts); + + /* Create hub0 and refer 2 backend chardevs */ + opts = qemu_opts_create(qemu_find_opts("chardev"), "hub0", + 1, &error_abort); + qemu_opt_set(opts, "backend", "hub", &error_abort); + qemu_opt_set(opts, "chardevs.0", "chr1", &error_abort); + qemu_opt_set(opts, "chardevs.1", "chr2", &error_abort); + hub = qemu_chr_new_from_opts(opts, NULL, &error_abort); + g_assert_nonnull(hub); + qemu_opts_del(opts); + + /* Attach hub to a frontend */ + qemu_chr_fe_init(&chr_be, hub, &error_abort); + qemu_chr_fe_set_handlers(&chr_be, + fe_can_read, + fe_read, + fe_event, + NULL, + &h, + NULL, true); + + /* Fails second time */ + qemu_chr_fe_init(&chr_be, hub, &error); + g_assert_cmpstr(error_get_pretty(error), ==, "chardev 'hub0' is already in use"); + error_free(error); + error = NULL; + + /* Write to backend, chr1 */ + base = qemu_chr_find("chr1"); + g_assert_cmpint(qemu_chr_be_can_write(base), !=, 0); + + qemu_chr_be_write(base, (void *)"hello", 6); + g_assert_cmpint(h.read_count, ==, 6); + g_assert_cmpstr(h.read_buf, ==, "hello"); + h.read_count = 0; + + /* Write to backend, chr2 */ + base = qemu_chr_find("chr2"); + g_assert_cmpint(qemu_chr_be_can_write(base), !=, 0); + + qemu_chr_be_write(base, (void *)"olleh", 6); + g_assert_cmpint(h.read_count, ==, 6); + g_assert_cmpstr(h.read_buf, ==, "olleh"); + h.read_count = 0; + + /* Write to frontend, chr_be */ + ret = qemu_chr_fe_write(&chr_be, (void *)"heyhey", 6); + g_assert_cmpint(ret, ==, 6); + + data = qmp_ringbuf_read("chr1", RB_SIZE, false, 0, &error_abort); + g_assert_cmpint(strlen(data), ==, 6); + g_assert_cmpstr(data, ==, "heyhey"); + g_free(data); + + data = qmp_ringbuf_read("chr2", RB_SIZE, false, 0, &error_abort); + g_assert_cmpint(strlen(data), ==, 6); + g_assert_cmpstr(data, ==, "heyhey"); + g_free(data); + + /* Can't be removed, depends on hub0 */ + qmp_chardev_remove("chr1", &error); + g_assert_cmpstr(error_get_pretty(error), ==, "Chardev 'chr1' is busy"); + error_free(error); + error = NULL; + + /* Can't be removed, depends on frontend chr_be */ + qmp_chardev_remove("hub0", &error); + g_assert_cmpstr(error_get_pretty(error), ==, "Chardev 'hub0' is busy"); + error_free(error); + error = NULL; + + /* Finalize frontend */ + qemu_chr_fe_deinit(&chr_be, false); + + /* Finalize hub0 */ + qmp_chardev_remove("hub0", &error_abort); + + /* Finalize backend chardevs */ + qmp_chardev_remove("chr1", &error_abort); + qmp_chardev_remove("chr2", &error_abort); + +#ifndef _WIN32 + /* + * Create 3 backend chardevs to simulate EAGAIN and watcher. + * Mainly copied from char_pipe_test(). + * 1. Create 2 ringbuf backend chardevs + * 2. Create 1 pipe backend chardev + * 3. Create 1 frontend + * 4. Create hub which refers 2 backend chardevs + * 5. Attach hub to a frontend + * 6. Perform data aggregation and check watcher + * 7. Finilize frontend, hub and backend chardevs in correct order + */ + { + gchar *tmp_path = g_dir_make_tmp("qemu-test-char.XXXXXX", NULL); + gchar *in, *out, *pipe = g_build_filename(tmp_path, "pipe", NULL); + Chardev *chr3; + int fd, len; + char buf[128]; + + in = g_strdup_printf("%s.in", pipe); + if (mkfifo(in, 0600) < 0) { + abort(); + } + out = g_strdup_printf("%s.out", pipe); + if (mkfifo(out, 0600) < 0) { + abort(); + } + + /* Create first chardev */ + opts = qemu_opts_create(qemu_find_opts("chardev"), "chr1", + 1, &error_abort); + qemu_opt_set(opts, "backend", "ringbuf", &error_abort); + qemu_opt_set(opts, "size", stringify(RB_SIZE), &error_abort); + chr1 = qemu_chr_new_from_opts(opts, NULL, &error_abort); + g_assert_nonnull(chr1); + qemu_opts_del(opts); + + /* Create second chardev */ + opts = qemu_opts_create(qemu_find_opts("chardev"), "chr2", + 1, &error_abort); + qemu_opt_set(opts, "backend", "ringbuf", &error_abort); + qemu_opt_set(opts, "size", stringify(RB_SIZE), &error_abort); + chr2 = qemu_chr_new_from_opts(opts, NULL, &error_abort); + g_assert_nonnull(chr2); + qemu_opts_del(opts); + + /* Create third chardev */ + opts = qemu_opts_create(qemu_find_opts("chardev"), "chr3", + 1, &error_abort); + qemu_opt_set(opts, "backend", "pipe", &error_abort); + qemu_opt_set(opts, "path", pipe, &error_abort); + chr3 = qemu_chr_new_from_opts(opts, NULL, &error_abort); + g_assert_nonnull(chr3); + + /* Create hub0 and refer 3 backend chardevs */ + opts = qemu_opts_create(qemu_find_opts("chardev"), "hub0", + 1, &error_abort); + qemu_opt_set(opts, "backend", "hub", &error_abort); + qemu_opt_set(opts, "chardevs.0", "chr1", &error_abort); + qemu_opt_set(opts, "chardevs.1", "chr2", &error_abort); + qemu_opt_set(opts, "chardevs.2", "chr3", &error_abort); + hub = qemu_chr_new_from_opts(opts, NULL, &error_abort); + g_assert_nonnull(hub); + qemu_opts_del(opts); + + /* Attach hub to a frontend */ + qemu_chr_fe_init(&chr_be, hub, &error_abort); + qemu_chr_fe_set_handlers(&chr_be, + fe_can_read, + fe_read, + fe_event, + NULL, + &h, + NULL, true); + + /* Write to frontend, chr_be */ + ret = qemu_chr_fe_write(&chr_be, (void *)"thisis", 6); + g_assert_cmpint(ret, ==, 6); + + data = qmp_ringbuf_read("chr1", RB_SIZE, false, 0, &error_abort); + g_assert_cmpint(strlen(data), ==, 6); + g_assert_cmpstr(data, ==, "thisis"); + g_free(data); + + data = qmp_ringbuf_read("chr2", RB_SIZE, false, 0, &error_abort); + g_assert_cmpint(strlen(data), ==, 6); + g_assert_cmpstr(data, ==, "thisis"); + g_free(data); + + fd = open(out, O_RDWR); + ret = read(fd, buf, sizeof(buf)); + g_assert_cmpint(ret, ==, 6); + buf[ret] = 0; + g_assert_cmpstr(buf, ==, "thisis"); + close(fd); + + /* Add watch. 0 indicates no watches if nothing to wait for */ + ret = qemu_chr_fe_add_watch(&chr_be, G_IO_OUT | G_IO_HUP, + NULL, NULL); + g_assert_cmpint(ret, ==, 0); + + /* + * Write to frontend, chr_be, until EAGAIN. Make sure length is + * power of two to fit nicely the whole pipe buffer. + */ + len = 0; + while ((ret = qemu_chr_fe_write(&chr_be, (void *)"thisisit", 8)) + != -1) { + len += ret; + } + g_assert_cmpint(errno, ==, EAGAIN); + + /* Further all writes should cause EAGAIN */ + ret = qemu_chr_fe_write(&chr_be, (void *)"b", 1); + g_assert_cmpint(ret, ==, -1); + g_assert_cmpint(errno, ==, EAGAIN); + + /* + * Add watch. Non 0 indicates we have a blocked chardev, which + * can wakes us up when write is possible. + */ + ret = qemu_chr_fe_add_watch(&chr_be, G_IO_OUT | G_IO_HUP, + NULL, NULL); + g_assert_cmpint(ret, !=, 0); + g_source_remove(ret); + + /* Drain pipe and ring buffers */ + fd = open(out, O_RDWR); + while ((ret = read(fd, buf, MIN(sizeof(buf), len))) != -1 && len > 0) { + len -= ret; + } + close(fd); + + data = qmp_ringbuf_read("chr1", RB_SIZE, false, 0, &error_abort); + g_assert_cmpint(strlen(data), ==, 128); + g_free(data); + + data = qmp_ringbuf_read("chr2", RB_SIZE, false, 0, &error_abort); + g_assert_cmpint(strlen(data), ==, 128); + g_free(data); + + /* + * Now we are good to go, first repeat "lost" sequence, which + * was already consumed and drained by the ring buffers, but + * pipe have not recieved that yet. + */ + ret = qemu_chr_fe_write(&chr_be, (void *)"thisisit", 8); + g_assert_cmpint(ret, ==, 8); + + ret = qemu_chr_fe_write(&chr_be, (void *)"streamisrestored", 16); + g_assert_cmpint(ret, ==, 16); + + data = qmp_ringbuf_read("chr1", RB_SIZE, false, 0, &error_abort); + g_assert_cmpint(strlen(data), ==, 16); + /* Only last 16 bytes, see big comment above */ + g_assert_cmpstr(data, ==, "streamisrestored"); + g_free(data); + + data = qmp_ringbuf_read("chr2", RB_SIZE, false, 0, &error_abort); + g_assert_cmpint(strlen(data), ==, 16); + /* Only last 16 bytes, see big comment above */ + g_assert_cmpstr(data, ==, "streamisrestored"); + g_free(data); + + fd = open(out, O_RDWR); + ret = read(fd, buf, sizeof(buf)); + g_assert_cmpint(ret, ==, 24); + buf[ret] = 0; + /* Both 8 and 16 bytes */ + g_assert_cmpstr(buf, ==, "thisisitstreamisrestored"); + close(fd); + + g_free(in); + g_free(out); + g_free(tmp_path); + g_free(pipe); + + /* Finalize frontend */ + qemu_chr_fe_deinit(&chr_be, false); + + /* Finalize hub0 */ + qmp_chardev_remove("hub0", &error_abort); + + /* Finalize backend chardevs */ + qmp_chardev_remove("chr1", &error_abort); + qmp_chardev_remove("chr2", &error_abort); + qmp_chardev_remove("chr3", &error_abort); + } +#endif +} static void websock_server_read(void *opaque, const uint8_t *buf, int size) { @@ -574,7 +993,7 @@ static void char_udp_test_internal(Chardev *reuse_chr, int sock) struct sockaddr_in other; SocketIdleData d = { 0, }; Chardev *chr; - CharBackend *be; + CharBackend stack_be, *be = &stack_be; socklen_t alen = sizeof(other); int ret; char buf[10]; @@ -590,7 +1009,6 @@ static void char_udp_test_internal(Chardev *reuse_chr, int sock) chr = qemu_chr_new("client", tmp, NULL); g_assert_nonnull(chr); - be = g_alloca(sizeof(CharBackend)); qemu_chr_fe_init(be, chr, &error_abort); } @@ -1485,6 +1903,7 @@ int main(int argc, char **argv) g_test_add_func("/char/invalid", char_invalid_test); g_test_add_func("/char/ringbuf", char_ringbuf_test); g_test_add_func("/char/mux", char_mux_test); + g_test_add_func("/char/hub", char_hub_test); #ifdef _WIN32 g_test_add_func("/char/console/subprocess", char_console_test_subprocess); g_test_add_func("/char/console", char_console_test); @@ -1523,18 +1942,18 @@ int main(int argc, char **argv) static CharSocketClientTestConfig client2 ## name = \ { addr, NULL, true, false, char_socket_event }; \ static CharSocketClientTestConfig client3 ## name = \ - { addr, ",reconnect=1", false, false, char_socket_event }; \ + { addr, ",reconnect-ms=1000", false, false, char_socket_event }; \ static CharSocketClientTestConfig client4 ## name = \ - { addr, ",reconnect=1", true, false, char_socket_event }; \ + { addr, ",reconnect-ms=1000", true, false, char_socket_event }; \ static CharSocketClientTestConfig client5 ## name = \ { addr, NULL, false, true, char_socket_event }; \ static CharSocketClientTestConfig client6 ## name = \ { addr, NULL, true, true, char_socket_event }; \ static CharSocketClientTestConfig client7 ## name = \ - { addr, ",reconnect=1", true, false, \ + { addr, ",reconnect-ms=1000", true, false, \ char_socket_event_with_error }; \ static CharSocketClientTestConfig client8 ## name = \ - { addr, ",reconnect=1", false, false, char_socket_event }; \ + { addr, ",reconnect-ms=1000", false, false, char_socket_event };\ g_test_add_data_func("/char/socket/client/mainloop/" # name, \ &client1 ##name, char_socket_client_test); \ g_test_add_data_func("/char/socket/client/wait-conn/" # name, \ diff --git a/tests/unit/test-crypto-afsplit.c b/tests/unit/test-crypto-afsplit.c index 00a7c18..45e9046 100644 --- a/tests/unit/test-crypto-afsplit.c +++ b/tests/unit/test-crypto-afsplit.c @@ -26,7 +26,7 @@ typedef struct QCryptoAFSplitTestData QCryptoAFSplitTestData; struct QCryptoAFSplitTestData { const char *path; - QCryptoHashAlgorithm hash; + QCryptoHashAlgo hash; uint32_t stripes; size_t blocklen; const uint8_t *key; @@ -36,7 +36,7 @@ struct QCryptoAFSplitTestData { static QCryptoAFSplitTestData test_data[] = { { .path = "/crypto/afsplit/sha256/5", - .hash = QCRYPTO_HASH_ALG_SHA256, + .hash = QCRYPTO_HASH_ALGO_SHA256, .stripes = 5, .blocklen = 32, .key = (const uint8_t *) @@ -68,7 +68,7 @@ static QCryptoAFSplitTestData test_data[] = { }, { .path = "/crypto/afsplit/sha256/5000", - .hash = QCRYPTO_HASH_ALG_SHA256, + .hash = QCRYPTO_HASH_ALGO_SHA256, .stripes = 5000, .blocklen = 16, .key = (const uint8_t *) @@ -77,7 +77,7 @@ static QCryptoAFSplitTestData test_data[] = { }, { .path = "/crypto/afsplit/sha1/1000", - .hash = QCRYPTO_HASH_ALG_SHA1, + .hash = QCRYPTO_HASH_ALGO_SHA1, .stripes = 1000, .blocklen = 32, .key = (const uint8_t *) @@ -88,7 +88,7 @@ static QCryptoAFSplitTestData test_data[] = { }, { .path = "/crypto/afsplit/sha256/big", - .hash = QCRYPTO_HASH_ALG_SHA256, + .hash = QCRYPTO_HASH_ALGO_SHA256, .stripes = 1000, .blocklen = 64, .key = (const uint8_t *) diff --git a/tests/unit/test-crypto-akcipher.c b/tests/unit/test-crypto-akcipher.c index 4f1f421..53c2211 100644 --- a/tests/unit/test-crypto-akcipher.c +++ b/tests/unit/test-crypto-akcipher.c @@ -692,7 +692,7 @@ struct QCryptoAkCipherTestData { static QCryptoRSAKeyTestData rsakey_test_data[] = { { .path = "/crypto/akcipher/rsakey-1024-public", - .key_type = QCRYPTO_AKCIPHER_KEY_TYPE_PUBLIC, + .key_type = QCRYPTO_AK_CIPHER_KEY_TYPE_PUBLIC, .key = rsa1024_public_key, .keylen = sizeof(rsa1024_public_key), .is_valid_key = true, @@ -700,7 +700,7 @@ static QCryptoRSAKeyTestData rsakey_test_data[] = { }, { .path = "/crypto/akcipher/rsakey-1024-private", - .key_type = QCRYPTO_AKCIPHER_KEY_TYPE_PRIVATE, + .key_type = QCRYPTO_AK_CIPHER_KEY_TYPE_PRIVATE, .key = rsa1024_private_key, .keylen = sizeof(rsa1024_private_key), .is_valid_key = true, @@ -708,7 +708,7 @@ static QCryptoRSAKeyTestData rsakey_test_data[] = { }, { .path = "/crypto/akcipher/rsakey-2048-public", - .key_type = QCRYPTO_AKCIPHER_KEY_TYPE_PUBLIC, + .key_type = QCRYPTO_AK_CIPHER_KEY_TYPE_PUBLIC, .key = rsa2048_public_key, .keylen = sizeof(rsa2048_public_key), .is_valid_key = true, @@ -716,7 +716,7 @@ static QCryptoRSAKeyTestData rsakey_test_data[] = { }, { .path = "/crypto/akcipher/rsakey-2048-private", - .key_type = QCRYPTO_AKCIPHER_KEY_TYPE_PRIVATE, + .key_type = QCRYPTO_AK_CIPHER_KEY_TYPE_PRIVATE, .key = rsa2048_private_key, .keylen = sizeof(rsa2048_private_key), .is_valid_key = true, @@ -724,56 +724,56 @@ static QCryptoRSAKeyTestData rsakey_test_data[] = { }, { .path = "/crypto/akcipher/rsakey-public-lack-elem", - .key_type = QCRYPTO_AKCIPHER_KEY_TYPE_PUBLIC, + .key_type = QCRYPTO_AK_CIPHER_KEY_TYPE_PUBLIC, .key = rsa_public_key_lack_element, .keylen = sizeof(rsa_public_key_lack_element), .is_valid_key = false, }, { .path = "/crypto/akcipher/rsakey-private-lack-elem", - .key_type = QCRYPTO_AKCIPHER_KEY_TYPE_PRIVATE, + .key_type = QCRYPTO_AK_CIPHER_KEY_TYPE_PRIVATE, .key = rsa_private_key_lack_element, .keylen = sizeof(rsa_private_key_lack_element), .is_valid_key = false, }, { .path = "/crypto/akcipher/rsakey-public-empty-elem", - .key_type = QCRYPTO_AKCIPHER_KEY_TYPE_PUBLIC, + .key_type = QCRYPTO_AK_CIPHER_KEY_TYPE_PUBLIC, .key = rsa_public_key_empty_element, .keylen = sizeof(rsa_public_key_empty_element), .is_valid_key = false, }, { .path = "/crypto/akcipher/rsakey-private-empty-elem", - .key_type = QCRYPTO_AKCIPHER_KEY_TYPE_PRIVATE, + .key_type = QCRYPTO_AK_CIPHER_KEY_TYPE_PRIVATE, .key = rsa_private_key_empty_element, .keylen = sizeof(rsa_private_key_empty_element), .is_valid_key = false, }, { .path = "/crypto/akcipher/rsakey-public-empty-key", - .key_type = QCRYPTO_AKCIPHER_KEY_TYPE_PUBLIC, + .key_type = QCRYPTO_AK_CIPHER_KEY_TYPE_PUBLIC, .key = NULL, .keylen = 0, .is_valid_key = false, }, { .path = "/crypto/akcipher/rsakey-private-empty-key", - .key_type = QCRYPTO_AKCIPHER_KEY_TYPE_PRIVATE, + .key_type = QCRYPTO_AK_CIPHER_KEY_TYPE_PRIVATE, .key = NULL, .keylen = 0, .is_valid_key = false, }, { .path = "/crypto/akcipher/rsakey-public-invalid-length-val", - .key_type = QCRYPTO_AKCIPHER_KEY_TYPE_PUBLIC, + .key_type = QCRYPTO_AK_CIPHER_KEY_TYPE_PUBLIC, .key = rsa_public_key_invalid_length_val, .keylen = sizeof(rsa_public_key_invalid_length_val), .is_valid_key = false, }, { .path = "/crypto/akcipher/rsakey-public-extra-elem", - .key_type = QCRYPTO_AKCIPHER_KEY_TYPE_PUBLIC, + .key_type = QCRYPTO_AK_CIPHER_KEY_TYPE_PUBLIC, .key = rsa_public_key_extra_elem, .keylen = sizeof(rsa_public_key_extra_elem), .is_valid_key = false, @@ -785,9 +785,9 @@ static QCryptoAkCipherTestData akcipher_test_data[] = { { .path = "/crypto/akcipher/rsa1024-raw", .opt = { - .alg = QCRYPTO_AKCIPHER_ALG_RSA, + .alg = QCRYPTO_AK_CIPHER_ALGO_RSA, .u.rsa = { - .padding_alg = QCRYPTO_RSA_PADDING_ALG_RAW, + .padding_alg = QCRYPTO_RSA_PADDING_ALGO_RAW, }, }, .pub_key = rsa1024_public_key, @@ -805,10 +805,10 @@ static QCryptoAkCipherTestData akcipher_test_data[] = { { .path = "/crypto/akcipher/rsa1024-pkcs1", .opt = { - .alg = QCRYPTO_AKCIPHER_ALG_RSA, + .alg = QCRYPTO_AK_CIPHER_ALGO_RSA, .u.rsa = { - .padding_alg = QCRYPTO_RSA_PADDING_ALG_PKCS1, - .hash_alg = QCRYPTO_HASH_ALG_SHA1, + .padding_alg = QCRYPTO_RSA_PADDING_ALGO_PKCS1, + .hash_alg = QCRYPTO_HASH_ALGO_SHA1, }, }, .pub_key = rsa1024_public_key, @@ -830,9 +830,9 @@ static QCryptoAkCipherTestData akcipher_test_data[] = { { .path = "/crypto/akcipher/rsa2048-raw", .opt = { - .alg = QCRYPTO_AKCIPHER_ALG_RSA, + .alg = QCRYPTO_AK_CIPHER_ALGO_RSA, .u.rsa = { - .padding_alg = QCRYPTO_RSA_PADDING_ALG_RAW, + .padding_alg = QCRYPTO_RSA_PADDING_ALGO_RAW, }, }, .pub_key = rsa2048_public_key, @@ -850,10 +850,10 @@ static QCryptoAkCipherTestData akcipher_test_data[] = { { .path = "/crypto/akcipher/rsa2048-pkcs1", .opt = { - .alg = QCRYPTO_AKCIPHER_ALG_RSA, + .alg = QCRYPTO_AK_CIPHER_ALGO_RSA, .u.rsa = { - .padding_alg = QCRYPTO_RSA_PADDING_ALG_PKCS1, - .hash_alg = QCRYPTO_HASH_ALG_SHA1, + .padding_alg = QCRYPTO_RSA_PADDING_ALGO_PKCS1, + .hash_alg = QCRYPTO_HASH_ALGO_SHA1, }, }, .pub_key = rsa2048_public_key, @@ -885,12 +885,12 @@ static void test_akcipher(const void *opaque) return; } pub_key = qcrypto_akcipher_new(&data->opt, - QCRYPTO_AKCIPHER_KEY_TYPE_PUBLIC, + QCRYPTO_AK_CIPHER_KEY_TYPE_PUBLIC, data->pub_key, data->pub_key_len, &error_abort); g_assert(pub_key != NULL); priv_key = qcrypto_akcipher_new(&data->opt, - QCRYPTO_AKCIPHER_KEY_TYPE_PRIVATE, + QCRYPTO_AK_CIPHER_KEY_TYPE_PRIVATE, data->priv_key, data->priv_key_len, &error_abort); g_assert(priv_key != NULL); @@ -944,10 +944,10 @@ static void test_rsakey(const void *opaque) { const QCryptoRSAKeyTestData *data = (const QCryptoRSAKeyTestData *)opaque; QCryptoAkCipherOptions opt = { - .alg = QCRYPTO_AKCIPHER_ALG_RSA, + .alg = QCRYPTO_AK_CIPHER_ALGO_RSA, .u.rsa = { - .padding_alg = QCRYPTO_RSA_PADDING_ALG_PKCS1, - .hash_alg = QCRYPTO_HASH_ALG_SHA1, + .padding_alg = QCRYPTO_RSA_PADDING_ALGO_PKCS1, + .hash_alg = QCRYPTO_HASH_ALGO_SHA1, } }; g_autoptr(QCryptoAkCipher) key = qcrypto_akcipher_new( diff --git a/tests/unit/test-crypto-block.c b/tests/unit/test-crypto-block.c index 42cfab6..3ac7f17 100644 --- a/tests/unit/test-crypto-block.c +++ b/tests/unit/test-crypto-block.c @@ -39,14 +39,14 @@ #endif static QCryptoBlockCreateOptions qcow_create_opts = { - .format = Q_CRYPTO_BLOCK_FORMAT_QCOW, + .format = QCRYPTO_BLOCK_FORMAT_QCOW, .u.qcow = { .key_secret = (char *)"sec0", }, }; static QCryptoBlockOpenOptions qcow_open_opts = { - .format = Q_CRYPTO_BLOCK_FORMAT_QCOW, + .format = QCRYPTO_BLOCK_FORMAT_QCOW, .u.qcow = { .key_secret = (char *)"sec0", }, @@ -55,7 +55,7 @@ static QCryptoBlockOpenOptions qcow_open_opts = { #ifdef TEST_LUKS static QCryptoBlockOpenOptions luks_open_opts = { - .format = Q_CRYPTO_BLOCK_FORMAT_LUKS, + .format = QCRYPTO_BLOCK_FORMAT_LUKS, .u.luks = { .key_secret = (char *)"sec0", }, @@ -64,7 +64,7 @@ static QCryptoBlockOpenOptions luks_open_opts = { /* Creation with all default values */ static QCryptoBlockCreateOptions luks_create_opts_default = { - .format = Q_CRYPTO_BLOCK_FORMAT_LUKS, + .format = QCRYPTO_BLOCK_FORMAT_LUKS, .u.luks = { .key_secret = (char *)"sec0", }, @@ -73,33 +73,33 @@ static QCryptoBlockCreateOptions luks_create_opts_default = { /* ...and with explicit values */ static QCryptoBlockCreateOptions luks_create_opts_aes256_cbc_plain64 = { - .format = Q_CRYPTO_BLOCK_FORMAT_LUKS, + .format = QCRYPTO_BLOCK_FORMAT_LUKS, .u.luks = { .key_secret = (char *)"sec0", .has_cipher_alg = true, - .cipher_alg = QCRYPTO_CIPHER_ALG_AES_256, + .cipher_alg = QCRYPTO_CIPHER_ALGO_AES_256, .has_cipher_mode = true, .cipher_mode = QCRYPTO_CIPHER_MODE_CBC, .has_ivgen_alg = true, - .ivgen_alg = QCRYPTO_IVGEN_ALG_PLAIN64, + .ivgen_alg = QCRYPTO_IV_GEN_ALGO_PLAIN64, }, }; static QCryptoBlockCreateOptions luks_create_opts_aes256_cbc_essiv = { - .format = Q_CRYPTO_BLOCK_FORMAT_LUKS, + .format = QCRYPTO_BLOCK_FORMAT_LUKS, .u.luks = { .key_secret = (char *)"sec0", .has_cipher_alg = true, - .cipher_alg = QCRYPTO_CIPHER_ALG_AES_256, + .cipher_alg = QCRYPTO_CIPHER_ALGO_AES_256, .has_cipher_mode = true, .cipher_mode = QCRYPTO_CIPHER_MODE_CBC, .has_ivgen_alg = true, - .ivgen_alg = QCRYPTO_IVGEN_ALG_ESSIV, + .ivgen_alg = QCRYPTO_IV_GEN_ALGO_ESSIV, .has_ivgen_hash_alg = true, - .ivgen_hash_alg = QCRYPTO_HASH_ALG_SHA256, + .ivgen_hash_alg = QCRYPTO_HASH_ALGO_SHA256, .has_hash_alg = true, - .hash_alg = QCRYPTO_HASH_ALG_SHA1, + .hash_alg = QCRYPTO_HASH_ALGO_SHA1, }, }; #endif /* TEST_LUKS */ @@ -112,12 +112,12 @@ static struct QCryptoBlockTestData { bool expect_header; - QCryptoCipherAlgorithm cipher_alg; + QCryptoCipherAlgo cipher_alg; QCryptoCipherMode cipher_mode; - QCryptoHashAlgorithm hash_alg; + QCryptoHashAlgo hash_alg; - QCryptoIVGenAlgorithm ivgen_alg; - QCryptoHashAlgorithm ivgen_hash; + QCryptoIVGenAlgo ivgen_alg; + QCryptoHashAlgo ivgen_hash; bool slow; } test_data[] = { @@ -128,10 +128,10 @@ static struct QCryptoBlockTestData { .expect_header = false, - .cipher_alg = QCRYPTO_CIPHER_ALG_AES_128, + .cipher_alg = QCRYPTO_CIPHER_ALGO_AES_128, .cipher_mode = QCRYPTO_CIPHER_MODE_CBC, - .ivgen_alg = QCRYPTO_IVGEN_ALG_PLAIN64, + .ivgen_alg = QCRYPTO_IV_GEN_ALGO_PLAIN64, }, #ifdef TEST_LUKS { @@ -141,11 +141,11 @@ static struct QCryptoBlockTestData { .expect_header = true, - .cipher_alg = QCRYPTO_CIPHER_ALG_AES_256, + .cipher_alg = QCRYPTO_CIPHER_ALGO_AES_256, .cipher_mode = QCRYPTO_CIPHER_MODE_XTS, - .hash_alg = QCRYPTO_HASH_ALG_SHA256, + .hash_alg = QCRYPTO_HASH_ALGO_SHA256, - .ivgen_alg = QCRYPTO_IVGEN_ALG_PLAIN64, + .ivgen_alg = QCRYPTO_IV_GEN_ALGO_PLAIN64, .slow = true, }, @@ -156,11 +156,11 @@ static struct QCryptoBlockTestData { .expect_header = true, - .cipher_alg = QCRYPTO_CIPHER_ALG_AES_256, + .cipher_alg = QCRYPTO_CIPHER_ALGO_AES_256, .cipher_mode = QCRYPTO_CIPHER_MODE_CBC, - .hash_alg = QCRYPTO_HASH_ALG_SHA256, + .hash_alg = QCRYPTO_HASH_ALGO_SHA256, - .ivgen_alg = QCRYPTO_IVGEN_ALG_PLAIN64, + .ivgen_alg = QCRYPTO_IV_GEN_ALGO_PLAIN64, .slow = true, }, @@ -171,12 +171,12 @@ static struct QCryptoBlockTestData { .expect_header = true, - .cipher_alg = QCRYPTO_CIPHER_ALG_AES_256, + .cipher_alg = QCRYPTO_CIPHER_ALGO_AES_256, .cipher_mode = QCRYPTO_CIPHER_MODE_CBC, - .hash_alg = QCRYPTO_HASH_ALG_SHA1, + .hash_alg = QCRYPTO_HASH_ALGO_SHA1, - .ivgen_alg = QCRYPTO_IVGEN_ALG_ESSIV, - .ivgen_hash = QCRYPTO_HASH_ALG_SHA256, + .ivgen_alg = QCRYPTO_IV_GEN_ALGO_ESSIV, + .ivgen_hash = QCRYPTO_HASH_ALGO_SHA256, .slow = true, }, @@ -572,8 +572,15 @@ int main(int argc, char **argv) g_assert(qcrypto_init(NULL) == 0); for (i = 0; i < G_N_ELEMENTS(test_data); i++) { - if (test_data[i].open_opts->format == Q_CRYPTO_BLOCK_FORMAT_LUKS && + if (test_data[i].open_opts->format == QCRYPTO_BLOCK_FORMAT_LUKS && !qcrypto_hash_supports(test_data[i].hash_alg)) { + g_printerr("# skip unsupported %s\n", + QCryptoHashAlgo_str(test_data[i].hash_alg)); + continue; + } + if (!qcrypto_cipher_supports(QCRYPTO_CIPHER_ALGO_AES_128, + QCRYPTO_CIPHER_MODE_CBC)) { + g_printerr("# skip unsupported aes-128:cbc\n"); continue; } if (!test_data[i].slow || diff --git a/tests/unit/test-crypto-cipher.c b/tests/unit/test-crypto-cipher.c index f5152e5..1331d55 100644 --- a/tests/unit/test-crypto-cipher.c +++ b/tests/unit/test-crypto-cipher.c @@ -27,7 +27,7 @@ typedef struct QCryptoCipherTestData QCryptoCipherTestData; struct QCryptoCipherTestData { const char *path; - QCryptoCipherAlgorithm alg; + QCryptoCipherAlgo alg; QCryptoCipherMode mode; const char *key; const char *plaintext; @@ -43,7 +43,7 @@ static QCryptoCipherTestData test_data[] = { { /* NIST F.1.1 ECB-AES128.Encrypt */ .path = "/crypto/cipher/aes-ecb-128", - .alg = QCRYPTO_CIPHER_ALG_AES_128, + .alg = QCRYPTO_CIPHER_ALGO_AES_128, .mode = QCRYPTO_CIPHER_MODE_ECB, .key = "2b7e151628aed2a6abf7158809cf4f3c", .plaintext = @@ -60,7 +60,7 @@ static QCryptoCipherTestData test_data[] = { { /* NIST F.1.3 ECB-AES192.Encrypt */ .path = "/crypto/cipher/aes-ecb-192", - .alg = QCRYPTO_CIPHER_ALG_AES_192, + .alg = QCRYPTO_CIPHER_ALGO_AES_192, .mode = QCRYPTO_CIPHER_MODE_ECB, .key = "8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b", .plaintext = @@ -77,7 +77,7 @@ static QCryptoCipherTestData test_data[] = { { /* NIST F.1.5 ECB-AES256.Encrypt */ .path = "/crypto/cipher/aes-ecb-256", - .alg = QCRYPTO_CIPHER_ALG_AES_256, + .alg = QCRYPTO_CIPHER_ALGO_AES_256, .mode = QCRYPTO_CIPHER_MODE_ECB, .key = "603deb1015ca71be2b73aef0857d7781" @@ -96,7 +96,7 @@ static QCryptoCipherTestData test_data[] = { { /* NIST F.2.1 CBC-AES128.Encrypt */ .path = "/crypto/cipher/aes-cbc-128", - .alg = QCRYPTO_CIPHER_ALG_AES_128, + .alg = QCRYPTO_CIPHER_ALGO_AES_128, .mode = QCRYPTO_CIPHER_MODE_CBC, .key = "2b7e151628aed2a6abf7158809cf4f3c", .iv = "000102030405060708090a0b0c0d0e0f", @@ -114,7 +114,7 @@ static QCryptoCipherTestData test_data[] = { { /* NIST F.2.3 CBC-AES128.Encrypt */ .path = "/crypto/cipher/aes-cbc-192", - .alg = QCRYPTO_CIPHER_ALG_AES_192, + .alg = QCRYPTO_CIPHER_ALGO_AES_192, .mode = QCRYPTO_CIPHER_MODE_CBC, .key = "8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b", .iv = "000102030405060708090a0b0c0d0e0f", @@ -132,7 +132,7 @@ static QCryptoCipherTestData test_data[] = { { /* NIST F.2.5 CBC-AES128.Encrypt */ .path = "/crypto/cipher/aes-cbc-256", - .alg = QCRYPTO_CIPHER_ALG_AES_256, + .alg = QCRYPTO_CIPHER_ALGO_AES_256, .mode = QCRYPTO_CIPHER_MODE_CBC, .key = "603deb1015ca71be2b73aef0857d7781" @@ -156,7 +156,7 @@ static QCryptoCipherTestData test_data[] = { * ciphertext in ECB and CBC modes */ .path = "/crypto/cipher/des-ecb-56-one-block", - .alg = QCRYPTO_CIPHER_ALG_DES, + .alg = QCRYPTO_CIPHER_ALGO_DES, .mode = QCRYPTO_CIPHER_MODE_ECB, .key = "80c4a2e691d5b3f7", .plaintext = "70617373776f7264", @@ -165,7 +165,7 @@ static QCryptoCipherTestData test_data[] = { { /* See previous comment */ .path = "/crypto/cipher/des-cbc-56-one-block", - .alg = QCRYPTO_CIPHER_ALG_DES, + .alg = QCRYPTO_CIPHER_ALGO_DES, .mode = QCRYPTO_CIPHER_MODE_CBC, .key = "80c4a2e691d5b3f7", .iv = "0000000000000000", @@ -174,7 +174,7 @@ static QCryptoCipherTestData test_data[] = { }, { .path = "/crypto/cipher/des-ecb-56", - .alg = QCRYPTO_CIPHER_ALG_DES, + .alg = QCRYPTO_CIPHER_ALGO_DES, .mode = QCRYPTO_CIPHER_MODE_ECB, .key = "80c4a2e691d5b3f7", .plaintext = @@ -191,7 +191,7 @@ static QCryptoCipherTestData test_data[] = { { /* Borrowed from linux-kernel crypto/testmgr.h */ .path = "/crypto/cipher/3des-cbc", - .alg = QCRYPTO_CIPHER_ALG_3DES, + .alg = QCRYPTO_CIPHER_ALGO_3DES, .mode = QCRYPTO_CIPHER_MODE_CBC, .key = "e9c0ff2e760b6424444d995a12d640c0" @@ -220,7 +220,7 @@ static QCryptoCipherTestData test_data[] = { { /* Borrowed from linux-kernel crypto/testmgr.h */ .path = "/crypto/cipher/3des-ecb", - .alg = QCRYPTO_CIPHER_ALG_3DES, + .alg = QCRYPTO_CIPHER_ALGO_3DES, .mode = QCRYPTO_CIPHER_MODE_ECB, .key = "0123456789abcdef5555555555555555" @@ -233,7 +233,7 @@ static QCryptoCipherTestData test_data[] = { { /* Borrowed from linux-kernel crypto/testmgr.h */ .path = "/crypto/cipher/3des-ctr", - .alg = QCRYPTO_CIPHER_ALG_3DES, + .alg = QCRYPTO_CIPHER_ALGO_3DES, .mode = QCRYPTO_CIPHER_MODE_CTR, .key = "9cd6f39cb95a67005a67002dceeb2dce" @@ -308,7 +308,7 @@ static QCryptoCipherTestData test_data[] = { { /* RFC 2144, Appendix B.1 */ .path = "/crypto/cipher/cast5-128", - .alg = QCRYPTO_CIPHER_ALG_CAST5_128, + .alg = QCRYPTO_CIPHER_ALGO_CAST5_128, .mode = QCRYPTO_CIPHER_MODE_ECB, .key = "0123456712345678234567893456789A", .plaintext = "0123456789abcdef", @@ -317,7 +317,7 @@ static QCryptoCipherTestData test_data[] = { { /* libgcrypt serpent.c */ .path = "/crypto/cipher/serpent-128", - .alg = QCRYPTO_CIPHER_ALG_SERPENT_128, + .alg = QCRYPTO_CIPHER_ALGO_SERPENT_128, .mode = QCRYPTO_CIPHER_MODE_ECB, .key = "00000000000000000000000000000000", .plaintext = "d29d576fcea3a3a7ed9099f29273d78e", @@ -326,7 +326,7 @@ static QCryptoCipherTestData test_data[] = { { /* libgcrypt serpent.c */ .path = "/crypto/cipher/serpent-192", - .alg = QCRYPTO_CIPHER_ALG_SERPENT_192, + .alg = QCRYPTO_CIPHER_ALGO_SERPENT_192, .mode = QCRYPTO_CIPHER_MODE_ECB, .key = "00000000000000000000000000000000" "0000000000000000", @@ -336,7 +336,7 @@ static QCryptoCipherTestData test_data[] = { { /* libgcrypt serpent.c */ .path = "/crypto/cipher/serpent-256a", - .alg = QCRYPTO_CIPHER_ALG_SERPENT_256, + .alg = QCRYPTO_CIPHER_ALGO_SERPENT_256, .mode = QCRYPTO_CIPHER_MODE_ECB, .key = "00000000000000000000000000000000" "00000000000000000000000000000000", @@ -346,7 +346,7 @@ static QCryptoCipherTestData test_data[] = { { /* libgcrypt serpent.c */ .path = "/crypto/cipher/serpent-256b", - .alg = QCRYPTO_CIPHER_ALG_SERPENT_256, + .alg = QCRYPTO_CIPHER_ALGO_SERPENT_256, .mode = QCRYPTO_CIPHER_MODE_ECB, .key = "00000000000000000000000000000000" "00000000000000000000000000000000", @@ -356,7 +356,7 @@ static QCryptoCipherTestData test_data[] = { { /* Twofish paper "Known Answer Test" */ .path = "/crypto/cipher/twofish-128", - .alg = QCRYPTO_CIPHER_ALG_TWOFISH_128, + .alg = QCRYPTO_CIPHER_ALGO_TWOFISH_128, .mode = QCRYPTO_CIPHER_MODE_ECB, .key = "d491db16e7b1c39e86cb086b789f5419", .plaintext = "019f9809de1711858faac3a3ba20fbc3", @@ -365,7 +365,7 @@ static QCryptoCipherTestData test_data[] = { { /* Twofish paper "Known Answer Test", I=3 */ .path = "/crypto/cipher/twofish-192", - .alg = QCRYPTO_CIPHER_ALG_TWOFISH_192, + .alg = QCRYPTO_CIPHER_ALGO_TWOFISH_192, .mode = QCRYPTO_CIPHER_MODE_ECB, .key = "88b2b2706b105e36b446bb6d731a1e88" "efa71f788965bd44", @@ -375,7 +375,7 @@ static QCryptoCipherTestData test_data[] = { { /* Twofish paper "Known Answer Test", I=4 */ .path = "/crypto/cipher/twofish-256", - .alg = QCRYPTO_CIPHER_ALG_TWOFISH_256, + .alg = QCRYPTO_CIPHER_ALGO_TWOFISH_256, .mode = QCRYPTO_CIPHER_MODE_ECB, .key = "d43bb7556ea32e46f2a282b7d45b4e0d" "57ff739d4dc92c1bd7fc01700cc8216f", @@ -386,7 +386,7 @@ static QCryptoCipherTestData test_data[] = { { /* SM4, GB/T 32907-2016, Appendix A.1 */ .path = "/crypto/cipher/sm4", - .alg = QCRYPTO_CIPHER_ALG_SM4, + .alg = QCRYPTO_CIPHER_ALGO_SM4, .mode = QCRYPTO_CIPHER_MODE_ECB, .key = "0123456789abcdeffedcba9876543210", .plaintext = @@ -398,7 +398,7 @@ static QCryptoCipherTestData test_data[] = { { /* #1 32 byte key, 32 byte PTX */ .path = "/crypto/cipher/aes-xts-128-1", - .alg = QCRYPTO_CIPHER_ALG_AES_128, + .alg = QCRYPTO_CIPHER_ALGO_AES_128, .mode = QCRYPTO_CIPHER_MODE_XTS, .key = "00000000000000000000000000000000" @@ -415,7 +415,7 @@ static QCryptoCipherTestData test_data[] = { { /* #2, 32 byte key, 32 byte PTX */ .path = "/crypto/cipher/aes-xts-128-2", - .alg = QCRYPTO_CIPHER_ALG_AES_128, + .alg = QCRYPTO_CIPHER_ALGO_AES_128, .mode = QCRYPTO_CIPHER_MODE_XTS, .key = "11111111111111111111111111111111" @@ -432,7 +432,7 @@ static QCryptoCipherTestData test_data[] = { { /* #5 from xts.7, 32 byte key, 32 byte PTX */ .path = "/crypto/cipher/aes-xts-128-3", - .alg = QCRYPTO_CIPHER_ALG_AES_128, + .alg = QCRYPTO_CIPHER_ALGO_AES_128, .mode = QCRYPTO_CIPHER_MODE_XTS, .key = "fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0" @@ -449,7 +449,7 @@ static QCryptoCipherTestData test_data[] = { { /* #4, 32 byte key, 512 byte PTX */ .path = "/crypto/cipher/aes-xts-128-4", - .alg = QCRYPTO_CIPHER_ALG_AES_128, + .alg = QCRYPTO_CIPHER_ALGO_AES_128, .mode = QCRYPTO_CIPHER_MODE_XTS, .key = "27182818284590452353602874713526" @@ -528,7 +528,7 @@ static QCryptoCipherTestData test_data[] = { * which is incompatible with XTS */ .path = "/crypto/cipher/cast5-xts-128", - .alg = QCRYPTO_CIPHER_ALG_CAST5_128, + .alg = QCRYPTO_CIPHER_ALGO_CAST5_128, .mode = QCRYPTO_CIPHER_MODE_XTS, .key = "27182818284590452353602874713526" @@ -537,7 +537,7 @@ static QCryptoCipherTestData test_data[] = { { /* NIST F.5.1 CTR-AES128.Encrypt */ .path = "/crypto/cipher/aes-ctr-128", - .alg = QCRYPTO_CIPHER_ALG_AES_128, + .alg = QCRYPTO_CIPHER_ALGO_AES_128, .mode = QCRYPTO_CIPHER_MODE_CTR, .key = "2b7e151628aed2a6abf7158809cf4f3c", .iv = "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff", @@ -555,7 +555,7 @@ static QCryptoCipherTestData test_data[] = { { /* NIST F.5.3 CTR-AES192.Encrypt */ .path = "/crypto/cipher/aes-ctr-192", - .alg = QCRYPTO_CIPHER_ALG_AES_192, + .alg = QCRYPTO_CIPHER_ALGO_AES_192, .mode = QCRYPTO_CIPHER_MODE_CTR, .key = "8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b", .iv = "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff", @@ -573,7 +573,7 @@ static QCryptoCipherTestData test_data[] = { { /* NIST F.5.5 CTR-AES256.Encrypt */ .path = "/crypto/cipher/aes-ctr-256", - .alg = QCRYPTO_CIPHER_ALG_AES_256, + .alg = QCRYPTO_CIPHER_ALGO_AES_256, .mode = QCRYPTO_CIPHER_MODE_CTR, .key = "603deb1015ca71be2b73aef0857d7781" "1f352c073b6108d72d9810a30914dff4", @@ -750,7 +750,7 @@ static void test_cipher_null_iv(void) uint8_t ciphertext[32] = { 0 }; cipher = qcrypto_cipher_new( - QCRYPTO_CIPHER_ALG_AES_256, + QCRYPTO_CIPHER_ALGO_AES_256, QCRYPTO_CIPHER_MODE_CBC, key, sizeof(key), &error_abort); @@ -779,7 +779,7 @@ static void test_cipher_short_plaintext(void) int ret; cipher = qcrypto_cipher_new( - QCRYPTO_CIPHER_ALG_AES_256, + QCRYPTO_CIPHER_ALGO_AES_256, QCRYPTO_CIPHER_MODE_CBC, key, sizeof(key), &error_abort); @@ -823,16 +823,21 @@ int main(int argc, char **argv) g_test_add_data_func(test_data[i].path, &test_data[i], test_cipher); } else { g_printerr("# skip unsupported %s:%s\n", - QCryptoCipherAlgorithm_str(test_data[i].alg), + QCryptoCipherAlgo_str(test_data[i].alg), QCryptoCipherMode_str(test_data[i].mode)); } } - g_test_add_func("/crypto/cipher/null-iv", - test_cipher_null_iv); + if (qcrypto_cipher_supports(QCRYPTO_CIPHER_ALGO_AES_256, + QCRYPTO_CIPHER_MODE_CBC)) { + g_test_add_func("/crypto/cipher/null-iv", + test_cipher_null_iv); - g_test_add_func("/crypto/cipher/short-plaintext", - test_cipher_short_plaintext); + g_test_add_func("/crypto/cipher/short-plaintext", + test_cipher_short_plaintext); + } else { + g_printerr("# skip unsupported aes-256:cbc\n"); + } return g_test_run(); } diff --git a/tests/unit/test-crypto-hash.c b/tests/unit/test-crypto-hash.c index 1f4abb8..8fee159 100644 --- a/tests/unit/test-crypto-hash.c +++ b/tests/unit/test-crypto-hash.c @@ -1,6 +1,7 @@ /* * QEMU Crypto hash algorithms * + * Copyright (c) 2024 Seagate Technology LLC and/or its Affiliates * Copyright (c) 2015 Red Hat, Inc. * * This library is free software; you can redistribute it and/or @@ -42,6 +43,9 @@ "63b54e4cb2d2032b393994aa263c0dbb" \ "e00a9f2fe9ef6037352232a1eec55ee7" #define OUTPUT_RIPEMD160 "f3d658fad3fdfb2b52c9369cf0d441249ddfa8a0" +#ifdef CONFIG_CRYPTO_SM3 +#define OUTPUT_SM3 "d4a97db105b477b84c4f20ec9c31a6c814e2705a0b83a5a89748d75f0ef456a1" +#endif #define OUTPUT_MD5_B64 "Yo0gY3FWMDWrjvYvSSveyQ==" #define OUTPUT_SHA1_B64 "sudPJnWKOkIeUJzuBFJEt4dTzAI=" @@ -54,32 +58,45 @@ "7sVe5w==" #define OUTPUT_RIPEMD160_B64 "89ZY+tP9+ytSyTac8NRBJJ3fqKA=" +#ifdef CONFIG_CRYPTO_SM3 +#define OUTPUT_SM3_B64 "1Kl9sQW0d7hMTyDsnDGmyBTicFoLg6Wol0jXXw70VqE=" +#endif + static const char *expected_outputs[] = { - [QCRYPTO_HASH_ALG_MD5] = OUTPUT_MD5, - [QCRYPTO_HASH_ALG_SHA1] = OUTPUT_SHA1, - [QCRYPTO_HASH_ALG_SHA224] = OUTPUT_SHA224, - [QCRYPTO_HASH_ALG_SHA256] = OUTPUT_SHA256, - [QCRYPTO_HASH_ALG_SHA384] = OUTPUT_SHA384, - [QCRYPTO_HASH_ALG_SHA512] = OUTPUT_SHA512, - [QCRYPTO_HASH_ALG_RIPEMD160] = OUTPUT_RIPEMD160, + [QCRYPTO_HASH_ALGO_MD5] = OUTPUT_MD5, + [QCRYPTO_HASH_ALGO_SHA1] = OUTPUT_SHA1, + [QCRYPTO_HASH_ALGO_SHA224] = OUTPUT_SHA224, + [QCRYPTO_HASH_ALGO_SHA256] = OUTPUT_SHA256, + [QCRYPTO_HASH_ALGO_SHA384] = OUTPUT_SHA384, + [QCRYPTO_HASH_ALGO_SHA512] = OUTPUT_SHA512, + [QCRYPTO_HASH_ALGO_RIPEMD160] = OUTPUT_RIPEMD160, +#ifdef CONFIG_CRYPTO_SM3 + [QCRYPTO_HASH_ALGO_SM3] = OUTPUT_SM3, +#endif }; static const char *expected_outputs_b64[] = { - [QCRYPTO_HASH_ALG_MD5] = OUTPUT_MD5_B64, - [QCRYPTO_HASH_ALG_SHA1] = OUTPUT_SHA1_B64, - [QCRYPTO_HASH_ALG_SHA224] = OUTPUT_SHA224_B64, - [QCRYPTO_HASH_ALG_SHA256] = OUTPUT_SHA256_B64, - [QCRYPTO_HASH_ALG_SHA384] = OUTPUT_SHA384_B64, - [QCRYPTO_HASH_ALG_SHA512] = OUTPUT_SHA512_B64, - [QCRYPTO_HASH_ALG_RIPEMD160] = OUTPUT_RIPEMD160_B64, + [QCRYPTO_HASH_ALGO_MD5] = OUTPUT_MD5_B64, + [QCRYPTO_HASH_ALGO_SHA1] = OUTPUT_SHA1_B64, + [QCRYPTO_HASH_ALGO_SHA224] = OUTPUT_SHA224_B64, + [QCRYPTO_HASH_ALGO_SHA256] = OUTPUT_SHA256_B64, + [QCRYPTO_HASH_ALGO_SHA384] = OUTPUT_SHA384_B64, + [QCRYPTO_HASH_ALGO_SHA512] = OUTPUT_SHA512_B64, + [QCRYPTO_HASH_ALGO_RIPEMD160] = OUTPUT_RIPEMD160_B64, +#ifdef CONFIG_CRYPTO_SM3 + [QCRYPTO_HASH_ALGO_SM3] = OUTPUT_SM3_B64, +#endif }; static const int expected_lens[] = { - [QCRYPTO_HASH_ALG_MD5] = 16, - [QCRYPTO_HASH_ALG_SHA1] = 20, - [QCRYPTO_HASH_ALG_SHA224] = 28, - [QCRYPTO_HASH_ALG_SHA256] = 32, - [QCRYPTO_HASH_ALG_SHA384] = 48, - [QCRYPTO_HASH_ALG_SHA512] = 64, - [QCRYPTO_HASH_ALG_RIPEMD160] = 20, + [QCRYPTO_HASH_ALGO_MD5] = 16, + [QCRYPTO_HASH_ALGO_SHA1] = 20, + [QCRYPTO_HASH_ALGO_SHA224] = 28, + [QCRYPTO_HASH_ALGO_SHA256] = 32, + [QCRYPTO_HASH_ALGO_SHA384] = 48, + [QCRYPTO_HASH_ALGO_SHA512] = 64, + [QCRYPTO_HASH_ALGO_RIPEMD160] = 20, +#ifdef CONFIG_CRYPTO_SM3 + [QCRYPTO_HASH_ALGO_SM3] = 32, +#endif }; static const char hex[] = "0123456789abcdef"; @@ -122,7 +139,7 @@ static void test_hash_prealloc(void) size_t i; for (i = 0; i < G_N_ELEMENTS(expected_outputs) ; i++) { - uint8_t *result; + uint8_t *result, *origresult; size_t resultlen; int ret; size_t j; @@ -132,7 +149,7 @@ static void test_hash_prealloc(void) } resultlen = expected_lens[i]; - result = g_new0(uint8_t, resultlen); + origresult = result = g_new0(uint8_t, resultlen); ret = qcrypto_hash_bytes(i, INPUT_TEXT, @@ -141,7 +158,8 @@ static void test_hash_prealloc(void) &resultlen, &error_fatal); g_assert(ret == 0); - + /* Validate that our pre-allocated pointer was not replaced */ + g_assert(result == origresult); g_assert(resultlen == expected_lens[i]); for (j = 0; j < resultlen; j++) { g_assert(expected_outputs[i][j * 2] == hex[(result[j] >> 4) & 0xf]); @@ -241,6 +259,50 @@ static void test_hash_base64(void) } } +static void test_hash_accumulate(void) +{ + size_t i; + + for (i = 0; i < G_N_ELEMENTS(expected_outputs) ; i++) { + g_autoptr(QCryptoHash) hash = NULL; + struct iovec iov[] = { + { .iov_base = (char *)INPUT_TEXT1, .iov_len = strlen(INPUT_TEXT1) }, + { .iov_base = (char *)INPUT_TEXT2, .iov_len = strlen(INPUT_TEXT2) }, + { .iov_base = (char *)INPUT_TEXT3, .iov_len = strlen(INPUT_TEXT3) }, + }; + g_autofree uint8_t *result = NULL; + size_t resultlen = 0; + int ret; + size_t j; + + if (!qcrypto_hash_supports(i)) { + continue; + } + + hash = qcrypto_hash_new(i, &error_fatal); + g_assert(hash != NULL); + + /* Add each iovec to the hash context separately */ + for (j = 0; j < G_N_ELEMENTS(iov); j++) { + ret = qcrypto_hash_updatev(hash, + &iov[j], 1, + &error_fatal); + + g_assert(ret == 0); + } + + ret = qcrypto_hash_finalize_bytes(hash, &result, &resultlen, + &error_fatal); + + g_assert(ret == 0); + g_assert(resultlen == expected_lens[i]); + for (j = 0; j < resultlen; j++) { + g_assert(expected_outputs[i][j * 2] == hex[(result[j] >> 4) & 0xf]); + g_assert(expected_outputs[i][j * 2 + 1] == hex[result[j] & 0xf]); + } + } +} + int main(int argc, char **argv) { int ret = qcrypto_init(&error_fatal); @@ -252,5 +314,6 @@ int main(int argc, char **argv) g_test_add_func("/crypto/hash/prealloc", test_hash_prealloc); g_test_add_func("/crypto/hash/digest", test_hash_digest); g_test_add_func("/crypto/hash/base64", test_hash_base64); + g_test_add_func("/crypto/hash/accumulate", test_hash_accumulate); return g_test_run(); } diff --git a/tests/unit/test-crypto-hmac.c b/tests/unit/test-crypto-hmac.c index 23eb724..20c60eb 100644 --- a/tests/unit/test-crypto-hmac.c +++ b/tests/unit/test-crypto-hmac.c @@ -27,43 +27,43 @@ typedef struct QCryptoHmacTestData QCryptoHmacTestData; struct QCryptoHmacTestData { - QCryptoHashAlgorithm alg; + QCryptoHashAlgo alg; const char *hex_digest; }; static QCryptoHmacTestData test_data[] = { { - .alg = QCRYPTO_HASH_ALG_MD5, + .alg = QCRYPTO_HASH_ALGO_MD5, .hex_digest = "ede9cb83679ba82d88fbeae865b3f8fc", }, { - .alg = QCRYPTO_HASH_ALG_SHA1, + .alg = QCRYPTO_HASH_ALGO_SHA1, .hex_digest = "c7b5a631e3aac975c4ededfcd346e469" "dbc5f2d1", }, { - .alg = QCRYPTO_HASH_ALG_SHA224, + .alg = QCRYPTO_HASH_ALGO_SHA224, .hex_digest = "5f768179dbb29ca722875d0f461a2e2f" "597d0210340a84df1a8e9c63", }, { - .alg = QCRYPTO_HASH_ALG_SHA256, + .alg = QCRYPTO_HASH_ALGO_SHA256, .hex_digest = "3798f363c57afa6edaffe39016ca7bad" "efd1e670afb0e3987194307dec3197db", }, { - .alg = QCRYPTO_HASH_ALG_SHA384, + .alg = QCRYPTO_HASH_ALGO_SHA384, .hex_digest = "d218680a6032d33dccd9882d6a6a7164" "64f26623be257a9b2919b185294f4a49" "9e54b190bfd6bc5cedd2cd05c7e65e82", }, { - .alg = QCRYPTO_HASH_ALG_SHA512, + .alg = QCRYPTO_HASH_ALGO_SHA512, .hex_digest = "835a4f5b3750b4c1fccfa88da2f746a4" "900160c9f18964309bb736c13b59491b" @@ -71,11 +71,19 @@ static QCryptoHmacTestData test_data[] = { "94c4ba26862b2dadb59b7ede1d08d53e", }, { - .alg = QCRYPTO_HASH_ALG_RIPEMD160, + .alg = QCRYPTO_HASH_ALGO_RIPEMD160, .hex_digest = "94964ed4c1155b62b668c241d67279e5" "8a711676", }, +#ifdef CONFIG_CRYPTO_SM3 + { + .alg = QCRYPTO_HASH_ALGO_SM3, + .hex_digest = + "760e3799332bc913819b930085360ddb" + "c05529261313d5b15b75bab4fd7ae91e", + }, +#endif }; static const char hex[] = "0123456789abcdef"; @@ -126,7 +134,7 @@ static void test_hmac_prealloc(void) for (i = 0; i < G_N_ELEMENTS(test_data); i++) { QCryptoHmacTestData *data = &test_data[i]; QCryptoHmac *hmac = NULL; - uint8_t *result = NULL; + uint8_t *result = NULL, *origresult = NULL; size_t resultlen = 0; const char *exp_output = NULL; int ret; @@ -139,7 +147,7 @@ static void test_hmac_prealloc(void) exp_output = data->hex_digest; resultlen = strlen(exp_output) / 2; - result = g_new0(uint8_t, resultlen); + origresult = result = g_new0(uint8_t, resultlen); hmac = qcrypto_hmac_new(data->alg, (const uint8_t *)KEY, strlen(KEY), &error_fatal); @@ -149,6 +157,8 @@ static void test_hmac_prealloc(void) strlen(INPUT_TEXT), &result, &resultlen, &error_fatal); g_assert(ret == 0); + /* Validate that our pre-allocated pointer was not replaced */ + g_assert(result == origresult); exp_output = data->hex_digest; for (j = 0; j < resultlen; j++) { diff --git a/tests/unit/test-crypto-ivgen.c b/tests/unit/test-crypto-ivgen.c index 29630ed..bc9ffe3 100644 --- a/tests/unit/test-crypto-ivgen.c +++ b/tests/unit/test-crypto-ivgen.c @@ -26,9 +26,9 @@ struct QCryptoIVGenTestData { const char *path; uint64_t sector; - QCryptoIVGenAlgorithm ivalg; - QCryptoHashAlgorithm hashalg; - QCryptoCipherAlgorithm cipheralg; + QCryptoIVGenAlgo ivalg; + QCryptoHashAlgo hashalg; + QCryptoCipherAlgo cipheralg; const uint8_t *key; size_t nkey; const uint8_t *iv; @@ -38,7 +38,7 @@ struct QCryptoIVGenTestData { { "/crypto/ivgen/plain/1", .sector = 0x1, - .ivalg = QCRYPTO_IVGEN_ALG_PLAIN, + .ivalg = QCRYPTO_IV_GEN_ALGO_PLAIN, .iv = (const uint8_t *)"\x01\x00\x00\x00\x00\x00\x00\x00" "\x00\x00\x00\x00\x00\x00\x00\x00", .niv = 16, @@ -47,7 +47,7 @@ struct QCryptoIVGenTestData { { "/crypto/ivgen/plain/1f2e3d4c", .sector = 0x1f2e3d4cULL, - .ivalg = QCRYPTO_IVGEN_ALG_PLAIN, + .ivalg = QCRYPTO_IV_GEN_ALGO_PLAIN, .iv = (const uint8_t *)"\x4c\x3d\x2e\x1f\x00\x00\x00\x00" "\x00\x00\x00\x00\x00\x00\x00\x00", .niv = 16, @@ -56,7 +56,7 @@ struct QCryptoIVGenTestData { { "/crypto/ivgen/plain/1f2e3d4c5b6a7988", .sector = 0x1f2e3d4c5b6a7988ULL, - .ivalg = QCRYPTO_IVGEN_ALG_PLAIN, + .ivalg = QCRYPTO_IV_GEN_ALGO_PLAIN, .iv = (const uint8_t *)"\x88\x79\x6a\x5b\x00\x00\x00\x00" "\x00\x00\x00\x00\x00\x00\x00\x00", .niv = 16, @@ -65,7 +65,7 @@ struct QCryptoIVGenTestData { { "/crypto/ivgen/plain64/1", .sector = 0x1, - .ivalg = QCRYPTO_IVGEN_ALG_PLAIN64, + .ivalg = QCRYPTO_IV_GEN_ALGO_PLAIN64, .iv = (const uint8_t *)"\x01\x00\x00\x00\x00\x00\x00\x00" "\x00\x00\x00\x00\x00\x00\x00\x00", .niv = 16, @@ -74,7 +74,7 @@ struct QCryptoIVGenTestData { { "/crypto/ivgen/plain64/1f2e3d4c", .sector = 0x1f2e3d4cULL, - .ivalg = QCRYPTO_IVGEN_ALG_PLAIN64, + .ivalg = QCRYPTO_IV_GEN_ALGO_PLAIN64, .iv = (const uint8_t *)"\x4c\x3d\x2e\x1f\x00\x00\x00\x00" "\x00\x00\x00\x00\x00\x00\x00\x00", .niv = 16, @@ -83,7 +83,7 @@ struct QCryptoIVGenTestData { { "/crypto/ivgen/plain64/1f2e3d4c5b6a7988", .sector = 0x1f2e3d4c5b6a7988ULL, - .ivalg = QCRYPTO_IVGEN_ALG_PLAIN64, + .ivalg = QCRYPTO_IV_GEN_ALGO_PLAIN64, .iv = (const uint8_t *)"\x88\x79\x6a\x5b\x4c\x3d\x2e\x1f" "\x00\x00\x00\x00\x00\x00\x00\x00", .niv = 16, @@ -92,9 +92,9 @@ struct QCryptoIVGenTestData { { "/crypto/ivgen/essiv/1", .sector = 0x1, - .ivalg = QCRYPTO_IVGEN_ALG_ESSIV, - .cipheralg = QCRYPTO_CIPHER_ALG_AES_128, - .hashalg = QCRYPTO_HASH_ALG_SHA256, + .ivalg = QCRYPTO_IV_GEN_ALGO_ESSIV, + .cipheralg = QCRYPTO_CIPHER_ALGO_AES_128, + .hashalg = QCRYPTO_HASH_ALGO_SHA256, .key = (const uint8_t *)"\x00\x01\x02\x03\x04\x05\x06\x07" "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", .nkey = 16, @@ -106,9 +106,9 @@ struct QCryptoIVGenTestData { { "/crypto/ivgen/essiv/1f2e3d4c", .sector = 0x1f2e3d4cULL, - .ivalg = QCRYPTO_IVGEN_ALG_ESSIV, - .cipheralg = QCRYPTO_CIPHER_ALG_AES_128, - .hashalg = QCRYPTO_HASH_ALG_SHA256, + .ivalg = QCRYPTO_IV_GEN_ALGO_ESSIV, + .cipheralg = QCRYPTO_CIPHER_ALGO_AES_128, + .hashalg = QCRYPTO_HASH_ALGO_SHA256, .key = (const uint8_t *)"\x00\x01\x02\x03\x04\x05\x06\x07" "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", .nkey = 16, @@ -120,9 +120,9 @@ struct QCryptoIVGenTestData { { "/crypto/ivgen/essiv/1f2e3d4c5b6a7988", .sector = 0x1f2e3d4c5b6a7988ULL, - .ivalg = QCRYPTO_IVGEN_ALG_ESSIV, - .cipheralg = QCRYPTO_CIPHER_ALG_AES_128, - .hashalg = QCRYPTO_HASH_ALG_SHA256, + .ivalg = QCRYPTO_IV_GEN_ALGO_ESSIV, + .cipheralg = QCRYPTO_CIPHER_ALGO_AES_128, + .hashalg = QCRYPTO_HASH_ALGO_SHA256, .key = (const uint8_t *)"\x00\x01\x02\x03\x04\x05\x06\x07" "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", .nkey = 16, @@ -166,7 +166,7 @@ int main(int argc, char **argv) size_t i; g_test_init(&argc, &argv, NULL); for (i = 0; i < G_N_ELEMENTS(test_data); i++) { - if (test_data[i].ivalg == QCRYPTO_IVGEN_ALG_ESSIV && + if (test_data[i].ivalg == QCRYPTO_IV_GEN_ALGO_ESSIV && !qcrypto_hash_supports(test_data[i].hashalg)) { continue; } diff --git a/tests/unit/test-crypto-pbkdf.c b/tests/unit/test-crypto-pbkdf.c index 39264cb..ddb7244 100644 --- a/tests/unit/test-crypto-pbkdf.c +++ b/tests/unit/test-crypto-pbkdf.c @@ -25,13 +25,13 @@ #include <sys/resource.h> #endif -#if defined(_WIN32) || defined(RUSAGE_THREAD) || defined(CONFIG_DARWNI) +#if defined(_WIN32) || defined(RUSAGE_THREAD) || defined(CONFIG_DARWIN) #include "crypto/pbkdf.h" typedef struct QCryptoPbkdfTestData QCryptoPbkdfTestData; struct QCryptoPbkdfTestData { const char *path; - QCryptoHashAlgorithm hash; + QCryptoHashAlgo hash; unsigned int iterations; const char *key; size_t nkey; @@ -52,7 +52,7 @@ static QCryptoPbkdfTestData test_data[] = { /* RFC 3962 test data */ { .path = "/crypto/pbkdf/rfc3962/sha1/iter1", - .hash = QCRYPTO_HASH_ALG_SHA1, + .hash = QCRYPTO_HASH_ALGO_SHA1, .iterations = 1, .key = "password", .nkey = 8, @@ -66,7 +66,7 @@ static QCryptoPbkdfTestData test_data[] = { }, { .path = "/crypto/pbkdf/rfc3962/sha1/iter2", - .hash = QCRYPTO_HASH_ALG_SHA1, + .hash = QCRYPTO_HASH_ALGO_SHA1, .iterations = 2, .key = "password", .nkey = 8, @@ -80,7 +80,7 @@ static QCryptoPbkdfTestData test_data[] = { }, { .path = "/crypto/pbkdf/rfc3962/sha1/iter1200a", - .hash = QCRYPTO_HASH_ALG_SHA1, + .hash = QCRYPTO_HASH_ALGO_SHA1, .iterations = 1200, .key = "password", .nkey = 8, @@ -94,7 +94,7 @@ static QCryptoPbkdfTestData test_data[] = { }, { .path = "/crypto/pbkdf/rfc3962/sha1/iter5", - .hash = QCRYPTO_HASH_ALG_SHA1, + .hash = QCRYPTO_HASH_ALGO_SHA1, .iterations = 5, .key = "password", .nkey = 8, @@ -108,7 +108,7 @@ static QCryptoPbkdfTestData test_data[] = { }, { .path = "/crypto/pbkdf/rfc3962/sha1/iter1200b", - .hash = QCRYPTO_HASH_ALG_SHA1, + .hash = QCRYPTO_HASH_ALGO_SHA1, .iterations = 1200, .key = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", @@ -123,7 +123,7 @@ static QCryptoPbkdfTestData test_data[] = { }, { .path = "/crypto/pbkdf/rfc3962/sha1/iter1200c", - .hash = QCRYPTO_HASH_ALG_SHA1, + .hash = QCRYPTO_HASH_ALGO_SHA1, .iterations = 1200, .key = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", @@ -138,7 +138,7 @@ static QCryptoPbkdfTestData test_data[] = { }, { .path = "/crypto/pbkdf/rfc3962/sha1/iter50", - .hash = QCRYPTO_HASH_ALG_SHA1, + .hash = QCRYPTO_HASH_ALGO_SHA1, .iterations = 50, .key = "\360\235\204\236", /* g-clef ("\xf09d849e) */ .nkey = 4, @@ -154,7 +154,7 @@ static QCryptoPbkdfTestData test_data[] = { /* RFC-6070 test data */ { .path = "/crypto/pbkdf/rfc6070/sha1/iter1", - .hash = QCRYPTO_HASH_ALG_SHA1, + .hash = QCRYPTO_HASH_ALGO_SHA1, .iterations = 1, .key = "password", .nkey = 8, @@ -166,7 +166,7 @@ static QCryptoPbkdfTestData test_data[] = { }, { .path = "/crypto/pbkdf/rfc6070/sha1/iter2", - .hash = QCRYPTO_HASH_ALG_SHA1, + .hash = QCRYPTO_HASH_ALGO_SHA1, .iterations = 2, .key = "password", .nkey = 8, @@ -178,7 +178,7 @@ static QCryptoPbkdfTestData test_data[] = { }, { .path = "/crypto/pbkdf/rfc6070/sha1/iter4096", - .hash = QCRYPTO_HASH_ALG_SHA1, + .hash = QCRYPTO_HASH_ALGO_SHA1, .iterations = 4096, .key = "password", .nkey = 8, @@ -190,7 +190,7 @@ static QCryptoPbkdfTestData test_data[] = { }, { .path = "/crypto/pbkdf/rfc6070/sha1/iter16777216", - .hash = QCRYPTO_HASH_ALG_SHA1, + .hash = QCRYPTO_HASH_ALGO_SHA1, .iterations = 16777216, .key = "password", .nkey = 8, @@ -203,7 +203,7 @@ static QCryptoPbkdfTestData test_data[] = { }, { .path = "/crypto/pbkdf/rfc6070/sha1/iter4096a", - .hash = QCRYPTO_HASH_ALG_SHA1, + .hash = QCRYPTO_HASH_ALGO_SHA1, .iterations = 4096, .key = "passwordPASSWORDpassword", .nkey = 24, @@ -216,7 +216,7 @@ static QCryptoPbkdfTestData test_data[] = { }, { .path = "/crypto/pbkdf/rfc6070/sha1/iter4096b", - .hash = QCRYPTO_HASH_ALG_SHA1, + .hash = QCRYPTO_HASH_ALGO_SHA1, .iterations = 4096, .key = "pass\0word", .nkey = 9, @@ -231,7 +231,7 @@ static QCryptoPbkdfTestData test_data[] = { { /* empty password test. */ .path = "/crypto/pbkdf/nonrfc/sha1/iter2", - .hash = QCRYPTO_HASH_ALG_SHA1, + .hash = QCRYPTO_HASH_ALGO_SHA1, .iterations = 2, .key = "", .nkey = 0, @@ -244,7 +244,7 @@ static QCryptoPbkdfTestData test_data[] = { { /* Password exceeds block size test */ .path = "/crypto/pbkdf/nonrfc/sha256/iter1200", - .hash = QCRYPTO_HASH_ALG_SHA256, + .hash = QCRYPTO_HASH_ALGO_SHA256, .iterations = 1200, .key = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", @@ -259,7 +259,7 @@ static QCryptoPbkdfTestData test_data[] = { }, { .path = "/crypto/pbkdf/nonrfc/sha512/iter1200", - .hash = QCRYPTO_HASH_ALG_SHA512, + .hash = QCRYPTO_HASH_ALGO_SHA512, .iterations = 1200, .key = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" @@ -276,7 +276,7 @@ static QCryptoPbkdfTestData test_data[] = { }, { .path = "/crypto/pbkdf/nonrfc/sha224/iter1200", - .hash = QCRYPTO_HASH_ALG_SHA224, + .hash = QCRYPTO_HASH_ALGO_SHA224, .iterations = 1200, .key = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" @@ -293,7 +293,7 @@ static QCryptoPbkdfTestData test_data[] = { }, { .path = "/crypto/pbkdf/nonrfc/sha384/iter1200", - .hash = QCRYPTO_HASH_ALG_SHA384, + .hash = QCRYPTO_HASH_ALGO_SHA384, .iterations = 1200, .key = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" @@ -310,7 +310,7 @@ static QCryptoPbkdfTestData test_data[] = { }, { .path = "/crypto/pbkdf/nonrfc/ripemd160/iter1200", - .hash = QCRYPTO_HASH_ALG_RIPEMD160, + .hash = QCRYPTO_HASH_ALGO_RIPEMD160, .iterations = 1200, .key = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" @@ -325,10 +325,26 @@ static QCryptoPbkdfTestData test_data[] = { "\xce\xbf\x91\x14\x8b\x5c\x48\x41", .nout = 32 }, +#ifdef CONFIG_CRYPTO_SM3 + { + .path = "/crypto/pbkdf/nonrfc/sm3/iter2", + .hash = QCRYPTO_HASH_ALGO_SM3, + .iterations = 2, + .key = "password", + .nkey = 8, + .salt = "ATHENA.MIT.EDUraeburn", + .nsalt = 21, + .out = "\x48\x71\x1b\x58\xa3\xcb\xce\x06" + "\xba\xad\x77\xa8\xb5\xb9\xd8\x07" + "\x6a\xe2\xb3\x5b\x95\xce\xc8\xce" + "\xe7\xb1\xcb\xee\x61\xdf\x04\xea", + .nout = 32 + }, +#endif #if 0 { .path = "/crypto/pbkdf/nonrfc/whirlpool/iter1200", - .hash = QCRYPTO_HASH_ALG_WHIRLPOOL, + .hash = QCRYPTO_HASH_ALGO_WHIRLPOOL, .iterations = 1200, .key = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", @@ -402,7 +418,7 @@ static void test_pbkdf_timing_sha256(void) memset(key, 0x5d, sizeof(key)); memset(salt, 0x7c, sizeof(salt)); - iters = qcrypto_pbkdf2_count_iters(QCRYPTO_HASH_ALG_SHA256, + iters = qcrypto_pbkdf2_count_iters(QCRYPTO_HASH_ALGO_SHA256, key, sizeof(key), salt, sizeof(salt), 32, @@ -431,7 +447,7 @@ int main(int argc, char **argv) } } - if (g_test_slow() && qcrypto_pbkdf2_supports(QCRYPTO_HASH_ALG_SHA256)) { + if (g_test_slow() && qcrypto_pbkdf2_supports(QCRYPTO_HASH_ALGO_SHA256)) { g_test_add_func("/crypt0/pbkdf/timing/sha256", test_pbkdf_timing_sha256); } diff --git a/tests/unit/test-crypto-secret.c b/tests/unit/test-crypto-secret.c index ffd13ff..fc32a01 100644 --- a/tests/unit/test-crypto-secret.c +++ b/tests/unit/test-crypto-secret.c @@ -22,6 +22,7 @@ #include "crypto/init.h" #include "crypto/secret.h" +#include "crypto/cipher.h" #include "qapi/error.h" #include "qemu/module.h" #if defined(CONFIG_KEYUTILS) && defined(CONFIG_SECRET_KEYRING) @@ -597,18 +598,21 @@ int main(int argc, char **argv) g_test_add_func("/crypto/secret/conv/utf8/base64", test_secret_conv_utf8_base64); - g_test_add_func("/crypto/secret/crypt/raw", - test_secret_crypt_raw); - g_test_add_func("/crypto/secret/crypt/base64", - test_secret_crypt_base64); - g_test_add_func("/crypto/secret/crypt/shortkey", - test_secret_crypt_short_key); - g_test_add_func("/crypto/secret/crypt/shortiv", - test_secret_crypt_short_iv); - g_test_add_func("/crypto/secret/crypt/missingiv", - test_secret_crypt_missing_iv); - g_test_add_func("/crypto/secret/crypt/badiv", - test_secret_crypt_bad_iv); + if (qcrypto_cipher_supports(QCRYPTO_CIPHER_ALGO_AES_128, + QCRYPTO_CIPHER_MODE_CBC)) { + g_test_add_func("/crypto/secret/crypt/raw", + test_secret_crypt_raw); + g_test_add_func("/crypto/secret/crypt/base64", + test_secret_crypt_base64); + g_test_add_func("/crypto/secret/crypt/shortkey", + test_secret_crypt_short_key); + g_test_add_func("/crypto/secret/crypt/shortiv", + test_secret_crypt_short_iv); + g_test_add_func("/crypto/secret/crypt/missingiv", + test_secret_crypt_missing_iv); + g_test_add_func("/crypto/secret/crypt/badiv", + test_secret_crypt_bad_iv); + } return g_test_run(); } diff --git a/tests/unit/test-crypto-tlssession.c b/tests/unit/test-crypto-tlssession.c index 3395f73..554054e 100644 --- a/tests/unit/test-crypto-tlssession.c +++ b/tests/unit/test-crypto-tlssession.c @@ -158,8 +158,7 @@ static void test_crypto_tls_session_psk(void) rv = qcrypto_tls_session_handshake(serverSess, &error_abort); g_assert(rv >= 0); - if (qcrypto_tls_session_get_handshake_status(serverSess) == - QCRYPTO_TLS_HANDSHAKE_COMPLETE) { + if (rv == QCRYPTO_TLS_HANDSHAKE_COMPLETE) { serverShake = true; } } @@ -167,8 +166,7 @@ static void test_crypto_tls_session_psk(void) rv = qcrypto_tls_session_handshake(clientSess, &error_abort); g_assert(rv >= 0); - if (qcrypto_tls_session_get_handshake_status(clientSess) == - QCRYPTO_TLS_HANDSHAKE_COMPLETE) { + if (rv == QCRYPTO_TLS_HANDSHAKE_COMPLETE) { clientShake = true; } } @@ -352,8 +350,7 @@ static void test_crypto_tls_session_x509(const void *opaque) rv = qcrypto_tls_session_handshake(serverSess, &error_abort); g_assert(rv >= 0); - if (qcrypto_tls_session_get_handshake_status(serverSess) == - QCRYPTO_TLS_HANDSHAKE_COMPLETE) { + if (rv == QCRYPTO_TLS_HANDSHAKE_COMPLETE) { serverShake = true; } } @@ -361,8 +358,7 @@ static void test_crypto_tls_session_x509(const void *opaque) rv = qcrypto_tls_session_handshake(clientSess, &error_abort); g_assert(rv >= 0); - if (qcrypto_tls_session_get_handshake_status(clientSess) == - QCRYPTO_TLS_HANDSHAKE_COMPLETE) { + if (rv == QCRYPTO_TLS_HANDSHAKE_COMPLETE) { clientShake = true; } } diff --git a/tests/unit/test-fifo.c b/tests/unit/test-fifo.c new file mode 100644 index 0000000..14153c4 --- /dev/null +++ b/tests/unit/test-fifo.c @@ -0,0 +1,449 @@ +/* + * Fifo8 tests + * + * Copyright 2024 Mark Cave-Ayland + * + * Authors: + * Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk> + * + * This work is licensed under the terms of the GNU LGPL, version 2 or later. + * See the COPYING.LIB file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "migration/vmstate.h" +#include "qemu/fifo8.h" + +const VMStateInfo vmstate_info_uint32; +const VMStateInfo vmstate_info_buffer; + + +static void test_fifo8_pop_bufptr_wrap(void) +{ + Fifo8 fifo; + uint8_t data_in1[] = { 0x1, 0x2, 0x3, 0x4 }; + uint8_t data_in2[] = { 0x5, 0x6, 0x7, 0x8, 0x9, 0xa }; + const uint8_t *buf; + uint32_t count; + + fifo8_create(&fifo, 8); + /* + * head --v-- tail used = 0 + * FIFO: [ . . . . . . . . ] + */ + + fifo8_push_all(&fifo, data_in1, sizeof(data_in1)); + /* + * head --v ]-- tail used = 4 + * FIFO: [ 1 2 3 4 . . . . ] + */ + buf = fifo8_pop_bufptr(&fifo, 2, &count); + /* + * head --v ]-- tail used = 2 + * FIFO: [ 1 2 3 4 . . . . ] + * buf --^ count = 2 + */ + g_assert(count == 2); + g_assert(buf[0] == 0x1 && buf[1] == 0x2); + + fifo8_push_all(&fifo, data_in2, sizeof(data_in2)); + /* + * tail --]v-- head used = 8 + * FIFO: [ 9 a 3 4 5 6 7 8 ] + */ + buf = fifo8_pop_bufptr(&fifo, 8, &count); + /* + * head --v ]-- tail used = 2 + * FIFO: [ 9 a 3 4 5 6 7 8 ] + * buf --^ count = 6 + */ + g_assert(count == 6); + g_assert(buf[0] == 0x3 && buf[1] == 0x4 && buf[2] == 0x5 && + buf[3] == 0x6 && buf[4] == 0x7 && buf[5] == 0x8); + + g_assert(fifo8_num_used(&fifo) == 2); + fifo8_destroy(&fifo); +} + +static void test_fifo8_pop_bufptr(void) +{ + Fifo8 fifo; + uint8_t data_in[] = { 0x1, 0x2, 0x3, 0x4 }; + const uint8_t *buf; + uint32_t count; + + fifo8_create(&fifo, 8); + /* + * head --v-- tail used = 0 + * FIFO: [ . . . . . . . . ] + */ + + fifo8_push_all(&fifo, data_in, sizeof(data_in)); + /* + * head --v ]-- tail used = 4 + * FIFO: [ 1 2 3 4 . . . . ] + */ + buf = fifo8_pop_bufptr(&fifo, 2, &count); + /* + * head --v ]-- tail used = 2 + * FIFO: [ 1 2 3 4 . . . . ] + * buf --^ count = 2 + */ + g_assert(count == 2); + g_assert(buf[0] == 0x1 && buf[1] == 0x2); + + g_assert(fifo8_num_used(&fifo) == 2); + fifo8_destroy(&fifo); +} + +static void test_fifo8_peek_bufptr_wrap(void) +{ + Fifo8 fifo; + uint8_t data_in1[] = { 0x1, 0x2, 0x3, 0x4 }; + uint8_t data_in2[] = { 0x5, 0x6, 0x7, 0x8, 0x9, 0xa }; + const uint8_t *buf; + uint32_t count; + + fifo8_create(&fifo, 8); + /* + * head --v-- tail used = 0 + * FIFO: { . . . . . . . . } + */ + + fifo8_push_all(&fifo, data_in1, sizeof(data_in1)); + /* + * head --v ]-- tail used = 4 + * FIFO: { 1 2 3 4 . . . . } + */ + buf = fifo8_peek_bufptr(&fifo, 2, &count); + /* + * head --v ]-- tail used = 4 + * FIFO: { 1 2 3 4 . . . . } + * buf: [ 1 2 ] count = 2 + */ + g_assert(count == 2); + g_assert(buf[0] == 0x1 && buf[1] == 0x2); + + buf = fifo8_pop_bufptr(&fifo, 2, &count); + /* + * head --v ]-- tail used = 2 + * FIFO: { 1 2 3 4 . . . . } + * buf: [ 1 2 ] count = 2 + */ + g_assert(count == 2); + g_assert(buf[0] == 0x1 && buf[1] == 0x2); + fifo8_push_all(&fifo, data_in2, sizeof(data_in2)); + /* + * tail ---]v-- head used = 8 + * FIFO: { 9 a 3 4 5 6 7 8 } + */ + + buf = fifo8_peek_bufptr(&fifo, 8, &count); + /* + * tail --]v-- head used = 8 + * FIFO: { 9 a 3 4 5 6 7 8 } + * buf: [ 3 4 5 6 7 8 ] count = 6 + */ + g_assert(count == 6); + g_assert(buf[0] == 0x3 && buf[1] == 0x4 && buf[2] == 0x5 && + buf[3] == 0x6 && buf[4] == 0x7 && buf[5] == 0x8); + + g_assert(fifo8_num_used(&fifo) == 8); + fifo8_destroy(&fifo); +} + +static void test_fifo8_peek_bufptr(void) +{ + Fifo8 fifo; + uint8_t data_in[] = { 0x1, 0x2, 0x3, 0x4 }; + const uint8_t *buf; + uint32_t count; + + fifo8_create(&fifo, 8); + /* + * head --v-- tail used = 0 + * FIFO: { . . . . . . . . } + */ + + fifo8_push_all(&fifo, data_in, sizeof(data_in)); + /* + * head --v ]-- tail used = 4 + * FIFO: { 1 2 3 4 . . . . } + */ + buf = fifo8_peek_bufptr(&fifo, 2, &count); + /* + * head --v ]-- tail used = 4 + * FIFO: { 1 2 3 4 . . . . } + * buf: [ 1 2 ] count = 2 + */ + g_assert(count == 2); + g_assert(buf[0] == 0x1 && buf[1] == 0x2); + + g_assert(fifo8_num_used(&fifo) == 4); + fifo8_destroy(&fifo); +} + +static void test_fifo8_pop_buf_wrap(void) +{ + Fifo8 fifo; + uint8_t data_in1[] = { 0x1, 0x2, 0x3, 0x4 }; + uint8_t data_in2[] = { 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc }; + uint8_t data_out[4]; + int count; + + fifo8_create(&fifo, 8); + /* + * head --v-- tail used = 0 + * FIFO: { . . . . . . . . } + */ + + fifo8_push_all(&fifo, data_in1, sizeof(data_in1)); + /* + * head --v ]-- tail used = 4 + * FIFO: { 1 2 3 4 . . . . } + */ + fifo8_pop_buf(&fifo, NULL, 4); + /* + * tail --]v-- head used = 0 + * FIFO: [ 1 2 3 4 . . . . ] + */ + + fifo8_push_all(&fifo, data_in2, sizeof(data_in2)); + /* + * tail --]v-- head used = 8 + * FIFO: { 9 a b c 5 6 7 8 } + */ + count = fifo8_pop_buf(&fifo, NULL, 4); + /* + * head --v ]-- tail used = 4 + * FIFO: { 9 a b c 5 6 7 8 } + */ + g_assert(count == 4); + count = fifo8_pop_buf(&fifo, data_out, 4); + /* + * tail --]v-- head used = 0 + * FIFO: { 9 a b c 5 6 7 8 } + */ + g_assert(count == 4); + g_assert(data_out[0] == 0x9 && data_out[1] == 0xa && + data_out[2] == 0xb && data_out[3] == 0xc); + + g_assert(fifo8_num_used(&fifo) == 0); + fifo8_destroy(&fifo); +} + +static void test_fifo8_pop_buf(void) +{ + Fifo8 fifo; + uint8_t data_in[] = { 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8 }; + uint8_t data_out[] = { 0xff, 0xff, 0xff, 0xff }; + int count; + + fifo8_create(&fifo, 8); + /* + * head --v-- tail used = 0 + * FIFO: { . . . . . . . . } + */ + + fifo8_push_all(&fifo, data_in, sizeof(data_in)); + /* + * head --v ]-- tail used = 4 + * FIFO: { 1 2 3 4 . . . . } + */ + count = fifo8_pop_buf(&fifo, NULL, 4); + /* + * tail --]v-- head used = 0 + * FIFO: { 1 2 3 4 . . . . } + */ + g_assert(count == 4); + count = fifo8_pop_buf(&fifo, data_out, 4); + g_assert(data_out[0] == 0x5 && data_out[1] == 0x6 && + data_out[2] == 0x7 && data_out[3] == 0x8); + + g_assert(fifo8_num_used(&fifo) == 0); + fifo8_destroy(&fifo); +} + +static void test_fifo8_peek_buf_wrap(void) +{ + Fifo8 fifo; + uint8_t data_in1[] = { 0x1, 0x2, 0x3, 0x4 }; + uint8_t data_in2[] = { 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc }; + uint8_t data_out[8]; + int count; + + fifo8_create(&fifo, 8); + /* + * head --v-- tail used = 0 + * FIFO: { . . . . . . . . } + */ + + fifo8_push_all(&fifo, data_in1, sizeof(data_in1)); + /* + * head --v ]-- tail used = 4 + * FIFO: { 1 2 3 4 . . . . } + */ + fifo8_pop_buf(&fifo, NULL, 4); + /* + * tail --]v-- head used = 0 + * FIFO: { 1 2 3 4 . . . . } + */ + + fifo8_push_all(&fifo, data_in2, sizeof(data_in2)); + /* + * tail --]v-- head used = 8 + * FIFO: { 9 a b c 5 6 7 8 } + */ + count = fifo8_peek_buf(&fifo, NULL, 4); + g_assert(count == 4); + count = fifo8_peek_buf(&fifo, data_out, 4); + /* + * tail --]v-- head used = 8 + * FIFO: { 9 a b c 5 6 7 8 } + * buf: [ 5 6 7 8 ] count = 4 + */ + g_assert(count == 4); + g_assert(data_out[0] == 0x5 && data_out[1] == 0x6 && + data_out[2] == 0x7 && data_out[3] == 0x8); + + count = fifo8_peek_buf(&fifo, data_out, 8); + /* + * tail --]v-- head used = 8 + * FIFO: { 9 a b c 5 6 7 8 } + * buf: [ 5 6 7 8 9 a b c ] count = 8 + */ + g_assert(count == 8); + g_assert(data_out[0] == 0x5 && data_out[1] == 0x6 && + data_out[2] == 0x7 && data_out[3] == 0x8); + g_assert(data_out[4] == 0x9 && data_out[5] == 0xa && + data_out[6] == 0xb && data_out[7] == 0xc); + + g_assert(fifo8_num_used(&fifo) == 8); + fifo8_destroy(&fifo); +} + +static void test_fifo8_peek_buf(void) +{ + Fifo8 fifo; + uint8_t data_in[] = { 0x1, 0x2, 0x3, 0x4 }; + uint8_t data_out[] = { 0xff, 0xff, 0xff, 0xff }; + int count; + + fifo8_create(&fifo, 8); + /* + * head --v-- tail used = 0 + * FIFO: { . . . . . . . . } + */ + + fifo8_push_all(&fifo, data_in, sizeof(data_in)); + /* + * head --v ]-- tail used = 4 + * FIFO: { 1 2 3 4 . . . . } + */ + count = fifo8_peek_buf(&fifo, NULL, 4); + g_assert(count == 4); + + g_assert(data_out[0] == 0xff && data_out[1] == 0xff && + data_out[2] == 0xff && data_out[3] == 0xff); + count = fifo8_peek_buf(&fifo, data_out, 4); + /* + * head --v ]-- tail used = 4 + * FIFO: { 1 2 3 4 . . . . } + * buf: [ 1 2 3 4 ] count = 4 + */ + g_assert(count == 4); + g_assert(data_out[0] == 0x1 && data_out[1] == 0x2 && + data_out[2] == 0x3 && data_out[3] == 0x4); + + g_assert(fifo8_num_used(&fifo) == 4); + fifo8_destroy(&fifo); +} + +static void test_fifo8_peek(void) +{ + Fifo8 fifo; + uint8_t c; + + fifo8_create(&fifo, 8); + /* + * head --v-- tail used = 0 + * FIFO: { . . . . . . . . } + */ + fifo8_push(&fifo, 0x1); + /* + * head --v]-- tail used = 1 + * FIFO: { 1 . . . . . . . } + */ + fifo8_push(&fifo, 0x2); + /* + * head --v ]-- tail used = 2 + * FIFO: { 1 2 . . . . . . } + */ + + c = fifo8_peek(&fifo); + g_assert(c == 0x1); + fifo8_pop(&fifo); + /* + * head --v]-- tail used = 1 + * FIFO: { 1 2 . . . . . . } + */ + c = fifo8_peek(&fifo); + g_assert(c == 0x2); + + g_assert(fifo8_num_used(&fifo) == 1); + fifo8_destroy(&fifo); +} + +static void test_fifo8_pushpop(void) +{ + Fifo8 fifo; + uint8_t c; + + fifo8_create(&fifo, 8); + /* + * head --v-- tail used = 0 + * FIFO: { . . . . . . . . } + */ + fifo8_push(&fifo, 0x1); + /* + * head --v]-- tail used = 1 + * FIFO: { 1 . . . . . . . } + */ + fifo8_push(&fifo, 0x2); + /* + * head --v ]-- tail used = 2 + * FIFO: { 1 2 . . . . . . } + */ + + c = fifo8_pop(&fifo); + /* + * head --v]-- tail used = 1 + * FIFO: { 1 2 . . . . . . } + */ + g_assert(c == 0x1); + c = fifo8_pop(&fifo); + /* + * tail --]v-- head used = 0 + * FIFO: { 1 2 . . . . . . } + */ + g_assert(c == 0x2); + + g_assert(fifo8_num_used(&fifo) == 0); + fifo8_destroy(&fifo); +} + +int main(int argc, char *argv[]) +{ + g_test_init(&argc, &argv, NULL); + g_test_add_func("/fifo8/pushpop", test_fifo8_pushpop); + g_test_add_func("/fifo8/peek", test_fifo8_peek); + g_test_add_func("/fifo8/peek_buf", test_fifo8_peek_buf); + g_test_add_func("/fifo8/peek_buf_wrap", test_fifo8_peek_buf_wrap); + g_test_add_func("/fifo8/pop_buf", test_fifo8_pop_buf); + g_test_add_func("/fifo8/pop_buf_wrap", test_fifo8_pop_buf_wrap); + g_test_add_func("/fifo8/peek_bufptr", test_fifo8_peek_bufptr); + g_test_add_func("/fifo8/peek_bufptr_wrap", test_fifo8_peek_bufptr_wrap); + g_test_add_func("/fifo8/pop_bufptr", test_fifo8_pop_bufptr); + g_test_add_func("/fifo8/pop_bufptr_wrap", test_fifo8_pop_bufptr_wrap); + return g_test_run(); +} diff --git a/tests/unit/test-forward-visitor.c b/tests/unit/test-forward-visitor.c index eea8ffc..aad1c89 100644 --- a/tests/unit/test-forward-visitor.c +++ b/tests/unit/test-forward-visitor.c @@ -12,8 +12,8 @@ #include "qapi/forward-visitor.h" #include "qapi/qobject-input-visitor.h" #include "qapi/error.h" -#include "qapi/qmp/qobject.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qobject.h" +#include "qobject/qdict.h" #include "test-qapi-visit.h" #include "qemu/keyval.h" diff --git a/tests/unit/test-image-locking.c b/tests/unit/test-image-locking.c index 2624cec..019195f 100644 --- a/tests/unit/test-image-locking.c +++ b/tests/unit/test-image-locking.c @@ -26,9 +26,9 @@ #include "qemu/osdep.h" #include "block/block.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #include "qapi/error.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "qemu/main-loop.h" static BlockBackend *open_image(const char *path, diff --git a/tests/unit/test-io-channel-socket.c b/tests/unit/test-io-channel-socket.c index b964bb2..dc7be96 100644 --- a/tests/unit/test-io-channel-socket.c +++ b/tests/unit/test-io-channel-socket.c @@ -506,7 +506,7 @@ static void test_io_channel_unix_listen_cleanup(void) { QIOChannelSocket *ioc; struct sockaddr_un un; - int sock; + int sock, ret = 0; #define TEST_SOCKET "test-io-channel-socket.sock" @@ -519,7 +519,9 @@ static void test_io_channel_unix_listen_cleanup(void) un.sun_family = AF_UNIX; snprintf(un.sun_path, sizeof(un.sun_path), "%s", TEST_SOCKET); unlink(TEST_SOCKET); - bind(sock, (struct sockaddr *)&un, sizeof(un)); + ret = bind(sock, (struct sockaddr *)&un, sizeof(un)); + g_assert_cmpint(ret, ==, 0); + ioc->fd = sock; ioc->localAddrLen = sizeof(ioc->localAddr); getsockname(sock, (struct sockaddr *)&ioc->localAddr, diff --git a/tests/unit/test-keyval.c b/tests/unit/test-keyval.c index 4dc52c7..c6e8f4f 100644 --- a/tests/unit/test-keyval.c +++ b/tests/unit/test-keyval.c @@ -13,9 +13,9 @@ #include "qemu/osdep.h" #include "qemu/units.h" #include "qapi/error.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qlist.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qdict.h" +#include "qobject/qlist.h" +#include "qobject/qstring.h" #include "qapi/qobject-input-visitor.h" #include "test-qapi-visit.h" #include "qemu/cutils.h" diff --git a/tests/unit/test-qdev-global-props.c b/tests/unit/test-qdev-global-props.c index c8862ca..3306276 100644 --- a/tests/unit/test-qdev-global-props.c +++ b/tests/unit/test-qdev-global-props.c @@ -46,13 +46,12 @@ struct MyType { uint32_t prop2; }; -static Property static_props[] = { +static const Property static_props[] = { DEFINE_PROP_UINT32("prop1", MyType, prop1, PROP_DEFAULT), DEFINE_PROP_UINT32("prop2", MyType, prop2, PROP_DEFAULT), - DEFINE_PROP_END_OF_LIST() }; -static void static_prop_class_init(ObjectClass *oc, void *data) +static void static_prop_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); @@ -72,6 +71,26 @@ static const TypeInfo subclass_type = { .parent = TYPE_STATIC_PROPS, }; +/* + * Initialize a fake machine, being prepared for future tests. + * + * All the tests later (even if to be run in subprocesses.. which will + * inherit the global states of the parent process) will try to create qdev + * and realize the device. + * + * Realization of such anonymous qdev (with no parent object) requires both + * the machine object and its "unattached" container to be at least present. + */ +static void test_init_machine(void) +{ + /* This is a fake machine - it doesn't need to be a machine object */ + Object *machine = object_property_add_new_container( + object_get_root(), "machine"); + + /* This container must exist for anonymous qdevs to realize() */ + object_property_add_new_container(machine, "unattached"); +} + /* Test simple static property setting to default value */ static void test_static_prop_subprocess(void) { @@ -158,7 +177,7 @@ static void dynamic_instance_init(Object *obj) NULL, NULL); } -static void dynamic_class_init(ObjectClass *oc, void *data) +static void dynamic_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); @@ -174,7 +193,7 @@ static const TypeInfo dynamic_prop_type = { .class_init = dynamic_class_init, }; -static void hotplug_class_init(ObjectClass *oc, void *data) +static void hotplug_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); @@ -190,7 +209,7 @@ static const TypeInfo hotplug_type = { .class_init = hotplug_class_init, }; -static void nohotplug_class_init(ObjectClass *oc, void *data) +static void nohotplug_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); @@ -295,6 +314,8 @@ int main(int argc, char **argv) type_register_static(&nohotplug_type); type_register_static(&nondevice_type); + test_init_machine(); + g_test_add_func("/qdev/properties/static/default/subprocess", test_static_prop_subprocess); g_test_add_func("/qdev/properties/static/default", diff --git a/tests/unit/test-qemu-opts.c b/tests/unit/test-qemu-opts.c index 828d40e..8d03a69 100644 --- a/tests/unit/test-qemu-opts.c +++ b/tests/unit/test-qemu-opts.c @@ -12,8 +12,8 @@ #include "qemu/option.h" #include "qemu/option_int.h" #include "qapi/error.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qdict.h" +#include "qobject/qstring.h" #include "qemu/config-file.h" diff --git a/tests/unit/test-qga.c b/tests/unit/test-qga.c index 8cddf5d..587e30c 100644 --- a/tests/unit/test-qga.c +++ b/tests/unit/test-qga.c @@ -5,8 +5,8 @@ #include <sys/un.h> #include "../qtest/libqtest.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qlist.h" +#include "qobject/qdict.h" +#include "qobject/qlist.h" typedef struct { char *test_dir; @@ -332,6 +332,22 @@ static void test_qga_get_fsinfo(gconstpointer fix) } } +static void test_qga_get_load(gconstpointer fix) +{ + const TestFixture *fixture = fix; + g_autoptr(QDict) ret = NULL; + QDict *load; + + ret = qmp_fd(fixture->fd, "{'execute': 'guest-get-load'}"); + g_assert_nonnull(ret); + qmp_assert_no_error(ret); + + load = qdict_get_qdict(ret, "return"); + g_assert(qdict_haskey(load, "load1m")); + g_assert(qdict_haskey(load, "load5m")); + g_assert(qdict_haskey(load, "load15m")); +} + static void test_qga_get_memory_block_info(gconstpointer fix) { const TestFixture *fixture = fix; @@ -1105,6 +1121,7 @@ int main(int argc, char **argv) g_test_add_data_func("/qga/get-vcpus", &fix, test_qga_get_vcpus); } g_test_add_data_func("/qga/get-fsinfo", &fix, test_qga_get_fsinfo); + g_test_add_data_func("/qga/get-load", &fix, test_qga_get_load); g_test_add_data_func("/qga/get-memory-block-info", &fix, test_qga_get_memory_block_info); g_test_add_data_func("/qga/get-memory-blocks", &fix, diff --git a/tests/unit/test-qgraph.c b/tests/unit/test-qgraph.c index 334c76c..ca1d60f 100644 --- a/tests/unit/test-qgraph.c +++ b/tests/unit/test-qgraph.c @@ -44,7 +44,6 @@ static void *driverfunct(void *obj, QGuestAllocator *machine, void *arg) static void testfunct(void *obj, void *arg, QGuestAllocator *alloc) { - return; } static void check_interface(const char *interface) diff --git a/tests/unit/test-qmp-cmds.c b/tests/unit/test-qmp-cmds.c index 6d52b4e..ad53886 100644 --- a/tests/unit/test-qmp-cmds.c +++ b/tests/unit/test-qmp-cmds.c @@ -1,9 +1,9 @@ #include "qemu/osdep.h" #include "qapi/compat-policy.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qjson.h" -#include "qapi/qmp/qnum.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qdict.h" +#include "qobject/qjson.h" +#include "qobject/qnum.h" +#include "qobject/qstring.h" #include "qapi/error.h" #include "qapi/qobject-input-visitor.h" #include "tests/test-qapi-types.h" diff --git a/tests/unit/test-qmp-event.c b/tests/unit/test-qmp-event.c index 08e95a3..2aac271 100644 --- a/tests/unit/test-qmp-event.c +++ b/tests/unit/test-qmp-event.c @@ -15,11 +15,11 @@ #include "qapi/compat-policy.h" #include "qapi/error.h" -#include "qapi/qmp/qbool.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qjson.h" -#include "qapi/qmp/qnum.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qbool.h" +#include "qobject/qdict.h" +#include "qobject/qjson.h" +#include "qobject/qnum.h" +#include "qobject/qstring.h" #include "qapi/qmp-event.h" #include "test-qapi-events.h" #include "test-qapi-emit-events.h" diff --git a/tests/unit/test-qobject-input-visitor.c b/tests/unit/test-qobject-input-visitor.c index 024e26c..84bdcdf 100644 --- a/tests/unit/test-qobject-input-visitor.c +++ b/tests/unit/test-qobject-input-visitor.c @@ -17,12 +17,12 @@ #include "qapi/qapi-visit-introspect.h" #include "qapi/qobject-input-visitor.h" #include "test-qapi-visit.h" -#include "qapi/qmp/qbool.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qnull.h" -#include "qapi/qmp/qnum.h" -#include "qapi/qmp/qstring.h" -#include "qapi/qmp/qjson.h" +#include "qobject/qbool.h" +#include "qobject/qdict.h" +#include "qobject/qnull.h" +#include "qobject/qnum.h" +#include "qobject/qstring.h" +#include "qobject/qjson.h" #include "test-qapi-introspect.h" #include "qapi/qapi-introspect.h" @@ -720,7 +720,7 @@ static void test_visitor_in_union_in_union(TestInputVisitorData *data, visit_type_TestUnionInUnion(v, NULL, &tmp, &error_abort); g_assert_cmpint(tmp->type, ==, TEST_UNION_ENUM_VALUE_A); - g_assert_cmpint(tmp->u.value_a.type_a, ==, TEST_UNION_ENUMA_VALUE_A1); + g_assert_cmpint(tmp->u.value_a.type_a, ==, TEST_UNION_ENUM_A_VALUE_A1); g_assert_cmpint(tmp->u.value_a.u.value_a1.integer, ==, 2); g_assert_cmpint(strcmp(tmp->u.value_a.u.value_a1.name, "fish"), ==, 0); @@ -734,7 +734,7 @@ static void test_visitor_in_union_in_union(TestInputVisitorData *data, visit_type_TestUnionInUnion(v, NULL, &tmp, &error_abort); g_assert_cmpint(tmp->type, ==, TEST_UNION_ENUM_VALUE_A); - g_assert_cmpint(tmp->u.value_a.type_a, ==, TEST_UNION_ENUMA_VALUE_A2); + g_assert_cmpint(tmp->u.value_a.type_a, ==, TEST_UNION_ENUM_A_VALUE_A2); g_assert_cmpint(tmp->u.value_a.u.value_a2.integer, ==, 1729); g_assert_cmpint(tmp->u.value_a.u.value_a2.size, ==, 87539319); diff --git a/tests/unit/test-qobject-output-visitor.c b/tests/unit/test-qobject-output-visitor.c index 1535b3a..407ab9e 100644 --- a/tests/unit/test-qobject-output-visitor.c +++ b/tests/unit/test-qobject-output-visitor.c @@ -15,12 +15,12 @@ #include "qapi/error.h" #include "qapi/qobject-output-visitor.h" #include "test-qapi-visit.h" -#include "qapi/qmp/qbool.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qlist.h" -#include "qapi/qmp/qnull.h" -#include "qapi/qmp/qnum.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qbool.h" +#include "qobject/qdict.h" +#include "qobject/qlist.h" +#include "qobject/qnull.h" +#include "qobject/qnum.h" +#include "qobject/qstring.h" typedef struct TestOutputVisitorData { Visitor *ov; @@ -359,7 +359,7 @@ static void test_visitor_out_union_in_union(TestOutputVisitorData *data, TestUnionInUnion *tmp = g_new0(TestUnionInUnion, 1); tmp->type = TEST_UNION_ENUM_VALUE_A; - tmp->u.value_a.type_a = TEST_UNION_ENUMA_VALUE_A1; + tmp->u.value_a.type_a = TEST_UNION_ENUM_A_VALUE_A1; tmp->u.value_a.u.value_a1.integer = 42; tmp->u.value_a.u.value_a1.name = g_strdup("fish"); @@ -377,7 +377,7 @@ static void test_visitor_out_union_in_union(TestOutputVisitorData *data, visitor_reset(data); tmp = g_new0(TestUnionInUnion, 1); tmp->type = TEST_UNION_ENUM_VALUE_A; - tmp->u.value_a.type_a = TEST_UNION_ENUMA_VALUE_A2; + tmp->u.value_a.type_a = TEST_UNION_ENUM_A_VALUE_A2; tmp->u.value_a.u.value_a2.integer = 1729; tmp->u.value_a.u.value_a2.size = 87539319; diff --git a/tests/unit/test-replication.c b/tests/unit/test-replication.c index 5d2003b..3aa98e6 100644 --- a/tests/unit/test-replication.c +++ b/tests/unit/test-replication.c @@ -11,13 +11,13 @@ #include "qemu/osdep.h" #include "qapi/error.h" -#include "qapi/qmp/qdict.h" +#include "qobject/qdict.h" #include "qemu/option.h" #include "qemu/main-loop.h" #include "block/replication.h" #include "block/block_int.h" #include "block/qdict.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" #define IMG_SIZE (64 * 1024 * 1024) diff --git a/tests/unit/test-resv-mem.c b/tests/unit/test-resv-mem.c index cd8f731..4de2d04 100644 --- a/tests/unit/test-resv-mem.c +++ b/tests/unit/test-resv-mem.c @@ -10,7 +10,7 @@ #include "qemu/osdep.h" #include "qemu/range.h" -#include "exec/memory.h" +#include "system/memory.h" #include "qemu/reserved-region.h" #define DEBUG 0 diff --git a/tests/unit/test-seccomp.c b/tests/unit/test-seccomp.c index bab93fd..71d4083 100644 --- a/tests/unit/test-seccomp.c +++ b/tests/unit/test-seccomp.c @@ -21,7 +21,7 @@ #include "qemu/osdep.h" #include "qemu/config-file.h" #include "qemu/option.h" -#include "sysemu/seccomp.h" +#include "system/seccomp.h" #include "qapi/error.h" #include "qemu/module.h" diff --git a/tests/unit/test-smp-parse.c b/tests/unit/test-smp-parse.c index f9bccb5..326045e 100644 --- a/tests/unit/test-smp-parse.c +++ b/tests/unit/test-smp-parse.c @@ -924,7 +924,7 @@ static void unsupported_params_init(const MachineClass *mc, SMPTestData *data) } } -static void machine_base_class_init(ObjectClass *oc, void *data) +static void machine_base_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); @@ -934,7 +934,8 @@ static void machine_base_class_init(ObjectClass *oc, void *data) mc->name = g_strdup(SMP_MACHINE_NAME); } -static void machine_generic_invalid_class_init(ObjectClass *oc, void *data) +static void machine_generic_invalid_class_init(ObjectClass *oc, + const void *data) { MachineClass *mc = MACHINE_CLASS(oc); @@ -943,21 +944,22 @@ static void machine_generic_invalid_class_init(ObjectClass *oc, void *data) mc->max_cpus = MAX_CPUS - 1; } -static void machine_with_modules_class_init(ObjectClass *oc, void *data) +static void machine_with_modules_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); mc->smp_props.modules_supported = true; } -static void machine_with_dies_class_init(ObjectClass *oc, void *data) +static void machine_with_dies_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); mc->smp_props.dies_supported = true; } -static void machine_with_modules_dies_class_init(ObjectClass *oc, void *data) +static void machine_with_modules_dies_class_init(ObjectClass *oc, + const void *data) { MachineClass *mc = MACHINE_CLASS(oc); @@ -965,28 +967,29 @@ static void machine_with_modules_dies_class_init(ObjectClass *oc, void *data) mc->smp_props.dies_supported = true; } -static void machine_with_clusters_class_init(ObjectClass *oc, void *data) +static void machine_with_clusters_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); mc->smp_props.clusters_supported = true; } -static void machine_with_books_class_init(ObjectClass *oc, void *data) +static void machine_with_books_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); mc->smp_props.books_supported = true; } -static void machine_with_drawers_class_init(ObjectClass *oc, void *data) +static void machine_with_drawers_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); mc->smp_props.drawers_supported = true; } -static void machine_with_drawers_books_class_init(ObjectClass *oc, void *data) +static void machine_with_drawers_books_class_init(ObjectClass *oc, + const void *data) { MachineClass *mc = MACHINE_CLASS(oc); @@ -994,7 +997,7 @@ static void machine_with_drawers_books_class_init(ObjectClass *oc, void *data) mc->smp_props.books_supported = true; } -static void machine_full_topo_class_init(ObjectClass *oc, void *data) +static void machine_full_topo_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); diff --git a/tests/unit/test-thread-pool.c b/tests/unit/test-thread-pool.c index 1483e53..33407b5 100644 --- a/tests/unit/test-thread-pool.c +++ b/tests/unit/test-thread-pool.c @@ -43,10 +43,10 @@ static void done_cb(void *opaque, int ret) active--; } -static void test_submit(void) +static void test_submit_no_complete(void) { WorkerTestData data = { .n = 0 }; - thread_pool_submit(worker_cb, &data); + thread_pool_submit_aio(worker_cb, &data, NULL, NULL); while (data.n == 0) { aio_poll(ctx, true); } @@ -236,7 +236,7 @@ int main(int argc, char **argv) ctx = qemu_get_current_aio_context(); g_test_init(&argc, &argv, NULL); - g_test_add_func("/thread-pool/submit", test_submit); + g_test_add_func("/thread-pool/submit-no-complete", test_submit_no_complete); g_test_add_func("/thread-pool/submit-aio", test_submit_aio); g_test_add_func("/thread-pool/submit-co", test_submit_co); g_test_add_func("/thread-pool/submit-many", test_submit_many); diff --git a/tests/unit/test-throttle.c b/tests/unit/test-throttle.c index 24032a0..dfa61c7 100644 --- a/tests/unit/test-throttle.c +++ b/tests/unit/test-throttle.c @@ -21,7 +21,7 @@ #include "qemu/main-loop.h" #include "qemu/module.h" #include "block/throttle-groups.h" -#include "sysemu/block-backend.h" +#include "system/block-backend.h" static AioContext *ctx; static LeakyBucket bkt; diff --git a/tests/unit/test-timed-average.c b/tests/unit/test-timed-average.c index 82c9250..747ed1e 100644 --- a/tests/unit/test-timed-average.c +++ b/tests/unit/test-timed-average.c @@ -11,7 +11,7 @@ */ #include "qemu/osdep.h" -#include "sysemu/cpu-timers.h" +#include "system/cpu-timers.h" #include "qemu/timed-average.h" /* This is the clock for QEMU_CLOCK_VIRTUAL */ diff --git a/tests/unit/test-util-sockets.c b/tests/unit/test-util-sockets.c index 4c9dd0b..ee66d72 100644 --- a/tests/unit/test-util-sockets.c +++ b/tests/unit/test-util-sockets.c @@ -332,6 +332,220 @@ static void test_socket_unix_abstract(void) #endif /* CONFIG_LINUX */ +static void inet_parse_test_helper(const char *str, + InetSocketAddress *exp_addr, bool success) +{ + InetSocketAddress addr; + Error *error = NULL; + + int rc = inet_parse(&addr, str, &error); + + if (success) { + if (error) { + error_report_err(error); + } + g_assert_cmpint(rc, ==, 0); + } else { + error_free(error); + g_assert_cmpint(rc, <, 0); + } + if (exp_addr != NULL) { + g_assert_cmpstr(addr.host, ==, exp_addr->host); + g_assert_cmpstr(addr.port, ==, exp_addr->port); + /* Own members: */ + g_assert_cmpint(addr.has_numeric, ==, exp_addr->has_numeric); + g_assert_cmpint(addr.numeric, ==, exp_addr->numeric); + g_assert_cmpint(addr.has_to, ==, exp_addr->has_to); + g_assert_cmpint(addr.to, ==, exp_addr->to); + g_assert_cmpint(addr.has_ipv4, ==, exp_addr->has_ipv4); + g_assert_cmpint(addr.ipv4, ==, exp_addr->ipv4); + g_assert_cmpint(addr.has_ipv6, ==, exp_addr->has_ipv6); + g_assert_cmpint(addr.ipv6, ==, exp_addr->ipv6); + g_assert_cmpint(addr.has_keep_alive, ==, exp_addr->has_keep_alive); + g_assert_cmpint(addr.keep_alive, ==, exp_addr->keep_alive); +#ifdef HAVE_TCP_KEEPCNT + g_assert_cmpint(addr.has_keep_alive_count, ==, + exp_addr->has_keep_alive_count); + g_assert_cmpint(addr.keep_alive_count, ==, + exp_addr->keep_alive_count); +#endif +#ifdef HAVE_TCP_KEEPIDLE + g_assert_cmpint(addr.has_keep_alive_idle, ==, + exp_addr->has_keep_alive_idle); + g_assert_cmpint(addr.keep_alive_idle, ==, + exp_addr->keep_alive_idle); +#endif +#ifdef HAVE_TCP_KEEPINTVL + g_assert_cmpint(addr.has_keep_alive_interval, ==, + exp_addr->has_keep_alive_interval); + g_assert_cmpint(addr.keep_alive_interval, ==, + exp_addr->keep_alive_interval); +#endif +#ifdef HAVE_IPPROTO_MPTCP + g_assert_cmpint(addr.has_mptcp, ==, exp_addr->has_mptcp); + g_assert_cmpint(addr.mptcp, ==, exp_addr->mptcp); +#endif + } + + g_free(addr.host); + g_free(addr.port); +} + +static void test_inet_parse_nohost_good(void) +{ + char host[] = ""; + char port[] = "5000"; + InetSocketAddress exp_addr = { + .host = host, + .port = port, + }; + inet_parse_test_helper(":5000", &exp_addr, true); +} + +static void test_inet_parse_empty_bad(void) +{ + inet_parse_test_helper("", NULL, false); +} + +static void test_inet_parse_only_colon_bad(void) +{ + inet_parse_test_helper(":", NULL, false); +} + +static void test_inet_parse_ipv4_good(void) +{ + char host[] = "127.0.0.1"; + char port[] = "5000"; + InetSocketAddress exp_addr = { + .host = host, + .port = port, + }; + inet_parse_test_helper("127.0.0.1:5000", &exp_addr, true); +} + +static void test_inet_parse_ipv4_noport_bad(void) +{ + inet_parse_test_helper("127.0.0.1", NULL, false); +} + +static void test_inet_parse_ipv6_good(void) +{ + char host[] = "::1"; + char port[] = "5000"; + InetSocketAddress exp_addr = { + .host = host, + .port = port, + }; + inet_parse_test_helper("[::1]:5000", &exp_addr, true); +} + +static void test_inet_parse_ipv6_noend_bad(void) +{ + inet_parse_test_helper("[::1", NULL, false); +} + +static void test_inet_parse_ipv6_noport_bad(void) +{ + inet_parse_test_helper("[::1]:", NULL, false); +} + +static void test_inet_parse_ipv6_empty_bad(void) +{ + inet_parse_test_helper("[]:5000", NULL, false); +} + +static void test_inet_parse_hostname_good(void) +{ + char host[] = "localhost"; + char port[] = "5000"; + InetSocketAddress exp_addr = { + .host = host, + .port = port, + }; + inet_parse_test_helper("localhost:5000", &exp_addr, true); +} + +static void test_inet_parse_all_options_good(void) +{ + char host[] = "::1"; + char port[] = "5000"; + InetSocketAddress exp_addr = { + .host = host, + .port = port, + .has_numeric = true, + .numeric = true, + .has_to = true, + .to = 5006, + .has_ipv4 = true, + .ipv4 = false, + .has_ipv6 = true, + .ipv6 = true, + .has_keep_alive = true, + .keep_alive = true, +#ifdef HAVE_TCP_KEEPCNT + .has_keep_alive_count = true, + .keep_alive_count = 10, +#endif +#ifdef HAVE_TCP_KEEPIDLE + .has_keep_alive_idle = true, + .keep_alive_idle = 60, +#endif +#ifdef HAVE_TCP_KEEPINTVL + .has_keep_alive_interval = true, + .keep_alive_interval = 30, +#endif +#ifdef HAVE_IPPROTO_MPTCP + .has_mptcp = true, + .mptcp = false, +#endif + }; + inet_parse_test_helper( + "[::1]:5000,numeric=on,to=5006,ipv4=off,ipv6=on,keep-alive=on" +#ifdef HAVE_TCP_KEEPCNT + ",keep-alive-count=10" +#endif +#ifdef HAVE_TCP_KEEPIDLE + ",keep-alive-idle=60" +#endif +#ifdef HAVE_TCP_KEEPINTVL + ",keep-alive-interval=30" +#endif +#ifdef HAVE_IPPROTO_MPTCP + ",mptcp=off" +#endif + , &exp_addr, true); +} + +static void test_inet_parse_all_implicit_bool_good(void) +{ + char host[] = "::1"; + char port[] = "5000"; + InetSocketAddress exp_addr = { + .host = host, + .port = port, + .has_numeric = true, + .numeric = true, + .has_to = true, + .to = 5006, + .has_ipv4 = true, + .ipv4 = true, + .has_ipv6 = true, + .ipv6 = true, + .has_keep_alive = true, + .keep_alive = true, +#ifdef HAVE_IPPROTO_MPTCP + .has_mptcp = true, + .mptcp = true, +#endif + }; + inet_parse_test_helper( + "[::1]:5000,numeric,to=5006,ipv4,ipv6,keep-alive" +#ifdef HAVE_IPPROTO_MPTCP + ",mptcp" +#endif + , &exp_addr, true); +} + int main(int argc, char **argv) { bool has_ipv4, has_ipv6; @@ -377,6 +591,31 @@ int main(int argc, char **argv) test_socket_unix_abstract); #endif + g_test_add_func("/util/socket/inet-parse/nohost-good", + test_inet_parse_nohost_good); + g_test_add_func("/util/socket/inet-parse/empty-bad", + test_inet_parse_empty_bad); + g_test_add_func("/util/socket/inet-parse/only-colon-bad", + test_inet_parse_only_colon_bad); + g_test_add_func("/util/socket/inet-parse/ipv4-good", + test_inet_parse_ipv4_good); + g_test_add_func("/util/socket/inet-parse/ipv4-noport-bad", + test_inet_parse_ipv4_noport_bad); + g_test_add_func("/util/socket/inet-parse/ipv6-good", + test_inet_parse_ipv6_good); + g_test_add_func("/util/socket/inet-parse/ipv6-noend-bad", + test_inet_parse_ipv6_noend_bad); + g_test_add_func("/util/socket/inet-parse/ipv6-noport-bad", + test_inet_parse_ipv6_noport_bad); + g_test_add_func("/util/socket/inet-parse/ipv6-empty-bad", + test_inet_parse_ipv6_empty_bad); + g_test_add_func("/util/socket/inet-parse/hostname-good", + test_inet_parse_hostname_good); + g_test_add_func("/util/socket/inet-parse/all-options-good", + test_inet_parse_all_options_good); + g_test_add_func("/util/socket/inet-parse/all-bare-bool-good", + test_inet_parse_all_implicit_bool_good); + end: return g_test_run(); } diff --git a/tests/unit/test-visitor-serialization.c b/tests/unit/test-visitor-serialization.c index c2056c3..2d36599 100644 --- a/tests/unit/test-visitor-serialization.c +++ b/tests/unit/test-visitor-serialization.c @@ -16,8 +16,8 @@ #include "test-qapi-visit.h" #include "qapi/error.h" -#include "qapi/qmp/qjson.h" -#include "qapi/qmp/qstring.h" +#include "qobject/qjson.h" +#include "qobject/qstring.h" #include "qapi/qobject-input-visitor.h" #include "qapi/qobject-output-visitor.h" #include "qapi/string-input-visitor.h" diff --git a/tests/unit/test-xs-node.c b/tests/unit/test-xs-node.c index ac94e7e..2f447a7 100644 --- a/tests/unit/test-xs-node.c +++ b/tests/unit/test-xs-node.c @@ -212,7 +212,7 @@ static void compare_tx(gpointer key, gpointer val, gpointer opaque) printf("Comparison failure in TX %u after serdes:\n", tx_id); dump_ref("Original", t1->root, 0); dump_ref("Deserialised", t2->root, 0); - g_assert(0); + g_assert_not_reached(); } g_assert(t1->nr_nodes == t2->nr_nodes); } @@ -257,7 +257,7 @@ static void check_serdes(XenstoreImplState *s) printf("Comparison failure in main tree after serdes:\n"); dump_ref("Original", s->root, 0); dump_ref("Deserialised", s2->root, 0); - g_assert(0); + g_assert_not_reached(); } nr_transactions1 = g_hash_table_size(s->transactions); diff --git a/tests/unit/test-yank.c b/tests/unit/test-yank.c index e6c036a..4acfb2f 100644 --- a/tests/unit/test-yank.c +++ b/tests/unit/test-yank.c @@ -14,7 +14,7 @@ #include "qemu/module.h" #include "qemu/option.h" #include "chardev/char-fe.h" -#include "sysemu/sysemu.h" +#include "system/system.h" #include "qapi/error.h" #include "qapi/qapi-commands-char.h" #include "qapi/qapi-types-char.h" diff --git a/tests/vm/Makefile.include b/tests/vm/Makefile.include index 13ed80f..14188bb 100644 --- a/tests/vm/Makefile.include +++ b/tests/vm/Makefile.include @@ -64,23 +64,24 @@ endif @echo " vm-boot-ssh-<guest> - Boot guest and login via ssh" @echo @echo "Special variables:" - @echo " BUILD_TARGET=foo - Override the build target" - @echo " DEBUG=1 - Enable verbose output on host and interactive debugging" - @echo ' EXTRA_CONFIGURE_OPTS="..." - Pass to configure step' - @echo " J=[0..9]* - Override the -jN parameter for make commands" - @echo " LOG_CONSOLE=1 - Log console to file in: ~/.cache/qemu-vm " - @echo " USE_TCG=1 - Use TCG for cross-arch images" - @echo " QEMU=/path/to/qemu - Change path to QEMU binary" + @echo " BUILD_TARGET=foo - Override the build target" + @echo " DEBUG=1 - Enable verbose output on host and interactive debugging" + @echo " ROOT_USER=1 - Login as root user for interactive shell" + @echo ' EXTRA_CONFIGURE_OPTS="..." - Pass to configure step' + @echo " J=[0..9]* - Override the -jN parameter for make commands" + @echo " LOG_CONSOLE=1 - Log console to file in: ~/.cache/qemu-vm " + @echo " USE_TCG=1 - Use TCG for cross-arch images" + @echo " QEMU=/path/to/qemu - Change path to QEMU binary" ifeq ($(HAVE_PYTHON_YAML),yes) - @echo " QEMU_CONFIG=/path/conf.yml - Change path to VM configuration .yml file." + @echo " QEMU_CONFIG=/path/conf.yml - Change path to VM configuration .yml file." else @echo " (install python3-yaml to enable support for yaml file to configure a VM.)" endif - @echo " See conf_example_*.yml for file format details." - @echo " QEMU_IMG=/path/to/qemu-img - Change path to qemu-img tool" - @echo " QEMU_LOCAL=1 - Use QEMU binary local to this build." - @echo " TARGET_LIST=a,b,c - Override target list in builds" - @echo " V=1 - Enable verbose output on host and guest commands" + @echo " See conf_example_*.yml for file format details." + @echo " QEMU_IMG=/path/to/qemu-img - Change path to qemu-img tool" + @echo " QEMU_LOCAL=1 - Use QEMU binary local to this build." + @echo " TARGET_LIST=a,b,c - Override target list in builds" + @echo " V=1 - Enable verbose output on host and guest commands" vm-build-all: $(addprefix vm-build-, $(IMAGES)) @@ -141,6 +142,6 @@ vm-boot-ssh-%: $(IMAGES_DIR)/%.img $(VM_VENV) $(if $(EFI_AARCH64),--efi-aarch64 $(EFI_AARCH64)) \ $(if $(LOG_CONSOLE),--log-console) \ --image "$<" \ - --interactive \ + $(if $(ROOT_USER),--interactive-root,-interactive) \ false, \ " VM-BOOT-SSH $*") || true diff --git a/tests/vm/README b/tests/vm/README index f9c04cc..14ac323 100644 --- a/tests/vm/README +++ b/tests/vm/README @@ -1 +1 @@ -See docs/devel/testing.rst for help. +See docs/devel/testing/main.rst for help. diff --git a/tests/vm/basevm.py b/tests/vm/basevm.py index 4a1af04..9e879e9 100644 --- a/tests/vm/basevm.py +++ b/tests/vm/basevm.py @@ -83,7 +83,7 @@ class BaseVM(object): # command to halt the guest, can be overridden by subclasses poweroff = "poweroff" # Time to wait for shutdown to finish. - shutdown_timeout_default = 30 + shutdown_timeout_default = 90 # enable IPv6 networking ipv6 = True # This is the timeout on the wait for console bytes. @@ -520,8 +520,7 @@ def get_qemu_path(arch, build_path=None): if "QEMU" in os.environ: qemu_path = os.environ["QEMU"] elif build_path: - qemu_path = os.path.join(build_path, arch + "-softmmu") - qemu_path = os.path.join(qemu_path, "qemu-system-" + arch) + qemu_path = os.path.join(build_path, "qemu-system-" + arch) else: # Default is to use system path for qemu. qemu_path = "qemu-system-" + arch @@ -613,8 +612,11 @@ def parse_args(vmcls): parser.add_argument("--source-path", default=None, help="Path of source directory, "\ "for finding additional files. ") - parser.add_argument("--interactive", "-I", action="store_true", - help="Interactively run command") + int_ops = parser.add_mutually_exclusive_group() + int_ops.add_argument("--interactive", "-I", action="store_true", + help="Interactively run command") + int_ops.add_argument("--interactive-root", action="store_true", + help="Interactively run command as root") parser.add_argument("--snapshot", "-s", action="store_true", help="run tests with a snapshot") parser.add_argument("--genisoimage", default="genisoimage", @@ -676,6 +678,8 @@ def main(vmcls, config=None): exitcode = 3 if args.interactive: vm.ssh() + elif args.interactive_root: + vm.ssh_root() if not args.snapshot: vm.graceful_shutdown() diff --git a/tests/vm/freebsd b/tests/vm/freebsd index 1247f40..74b3b1e 100755 --- a/tests/vm/freebsd +++ b/tests/vm/freebsd @@ -28,8 +28,8 @@ class FreeBSDVM(basevm.BaseVM): name = "freebsd" arch = "x86_64" - link = "https://download.freebsd.org/releases/CI-IMAGES/13.2-RELEASE/amd64/Latest/FreeBSD-13.2-RELEASE-amd64-BASIC-CI.raw.xz" - csum = "a4fb3b6c7b75dd4d58fb0d75e4caf72844bffe0ca00e66459c028b198ffb3c0e" + link = "https://download.freebsd.org/releases/CI-IMAGES/14.1-RELEASE/amd64/Latest/FreeBSD-14.1-RELEASE-amd64-BASIC-CI.raw.xz" + csum = "202fe27a05427f0a86d3ebb97712745186f2776ccc4f70d95466dd99a0238ba5" size = "20G" BUILD_SCRIPT = """ @@ -39,7 +39,7 @@ class FreeBSDVM(basevm.BaseVM): mkdir src build; cd src; tar -xf /dev/vtbd1; cd ../build; - ../src/configure --python=python3.9 --extra-ldflags=-L/usr/local/lib \ + ../src/configure --extra-ldflags=-L/usr/local/lib \ --extra-cflags=-I/usr/local/include {configure_opts}; gmake --output-sync -j{jobs} {target} {verbose}; """ diff --git a/tests/vm/generated/freebsd.json b/tests/vm/generated/freebsd.json index d5f0b62..c03e1cd 100644 --- a/tests/vm/generated/freebsd.json +++ b/tests/vm/generated/freebsd.json @@ -5,7 +5,7 @@ "make": "/usr/local/bin/gmake", "ninja": "/usr/local/bin/ninja", "packaging_command": "pkg", - "pip3": "/usr/local/bin/pip-3.8", + "pip3": "/usr/local/bin/pip", "pkgs": [ "alsa-lib", "bash", @@ -13,7 +13,7 @@ "bzip2", "ca_root_nss", "capstone4", - "ccache", + "ccache4", "cmocka", "ctags", "curl", @@ -55,12 +55,14 @@ "py311-numpy", "py311-pillow", "py311-pip", + "py311-pyyaml", "py311-sphinx", "py311-sphinx_rtd_theme", "py311-tomli", - "py311-yaml", "python3", "rpm2cpio", + "rust", + "rust-bindgen-cli", "sdl2", "sdl2_image", "snappy", @@ -71,6 +73,7 @@ "usbredir", "virglrenderer", "vte3", + "vulkan-tools", "xorriso", "zstd" ], diff --git a/tests/vm/openbsd b/tests/vm/openbsd index 49cab08..2ea86a0 100755 --- a/tests/vm/openbsd +++ b/tests/vm/openbsd @@ -22,8 +22,8 @@ class OpenBSDVM(basevm.BaseVM): name = "openbsd" arch = "x86_64" - link = "https://cdn.openbsd.org/pub/OpenBSD/7.5/amd64/install75.iso" - csum = "034435c6e27405d5a7fafb058162943c194eb793dafdc412c08d49bb56b3892a" + link = "https://cdn.openbsd.org/pub/OpenBSD/7.7/amd64/install77.iso" + csum = "da0106e39463f015524dca806f407c37a9bdd17e6dfffe533b06a2dd2edd8a27" size = "20G" pkgs = [ # tools @@ -32,7 +32,6 @@ class OpenBSDVM(basevm.BaseVM): "pkgconf", "bzip2", "xz", "ninja", - "py3-tomli", # gnu tools "bash", |