diff options
Diffstat (limited to 'tests')
25 files changed, 474 insertions, 49 deletions
diff --git a/tests/docker/Makefile.include b/tests/docker/Makefile.include index fa1cbb6..3959d8a 100644 --- a/tests/docker/Makefile.include +++ b/tests/docker/Makefile.include @@ -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) \ diff --git a/tests/docker/dockerfiles/fedora-rust-nightly.docker b/tests/docker/dockerfiles/fedora-rust-nightly.docker index fe4a6ed..4a03330 100644 --- a/tests/docker/dockerfiles/fedora-rust-nightly.docker +++ b/tests/docker/dockerfiles/fedora-rust-nightly.docker @@ -156,6 +156,7 @@ 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' && \ @@ -170,6 +171,7 @@ RUN set -eux && \ /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 diff --git a/tests/docker/dockerfiles/ubuntu2204.docker b/tests/docker/dockerfiles/ubuntu2204.docker index 4a1cf2b..28a6f93 100644 --- a/tests/docker/dockerfiles/ubuntu2204.docker +++ b/tests/docker/dockerfiles/ubuntu2204.docker @@ -151,6 +151,7 @@ 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 \ diff --git a/tests/functional/meson.build b/tests/functional/meson.build index 557d59d..7faa2b6 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -150,6 +150,7 @@ tests_arm_linuxuser_thorough = [ tests_avr_system_thorough = [ 'avr_mega2560', + 'avr_uno', ] tests_hppa_system_quick = [ @@ -312,6 +313,7 @@ tests_x86_64_system_quick = [ 'virtio_version', 'x86_cpu_model_versions', 'vnc', + 'memlock', ] tests_x86_64_system_thorough = [ diff --git a/tests/functional/qemu_test/__init__.py b/tests/functional/qemu_test/__init__.py index af41c2c..6e666a0 100644 --- a/tests/functional/qemu_test/__init__.py +++ b/tests/functional/qemu_test/__init__.py @@ -15,6 +15,6 @@ from .testcase import QemuBaseTest, QemuUserTest, QemuSystemTest from .linuxkernel import LinuxKernelTest from .decorators import skipIfMissingCommands, skipIfNotMachine, \ skipFlakyTest, skipUntrustedTest, skipBigDataTest, skipSlowTest, \ - skipIfMissingImports, skipIfOperatingSystem + skipIfMissingImports, skipIfOperatingSystem, skipLockedMemoryTest from .archive import archive_extract from .uncompress import uncompress diff --git a/tests/functional/qemu_test/decorators.py b/tests/functional/qemu_test/decorators.py index 50d29de..c0d1567 100644 --- a/tests/functional/qemu_test/decorators.py +++ b/tests/functional/qemu_test/decorators.py @@ -5,6 +5,7 @@ import importlib import os import platform +import resource from unittest import skipIf, skipUnless from .cmd import which @@ -131,3 +132,20 @@ def skipIfMissingImports(*args): 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/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_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_vnc.py b/tests/functional/test_vnc.py index 5c0ee5f..f1dd159 100755 --- a/tests/functional/test_vnc.py +++ b/tests/functional/test_vnc.py @@ -31,6 +31,7 @@ def check_connect(port: int) -> bool: class Vnc(QemuSystemTest): def test_no_vnc_change_password(self): + self.set_machine('none') self.vm.add_args('-nodefaults', '-S') self.vm.launch() @@ -62,6 +63,7 @@ class Vnc(QemuSystemTest): raise excp def test_change_password_requires_a_password(self): + 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']) @@ -74,6 +76,7 @@ class Vnc(QemuSystemTest): 'Could not set password') def test_change_password(self): + 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']) @@ -103,6 +106,7 @@ class Vnc(QemuSystemTest): 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) diff --git a/tests/lcitool/mappings.yml b/tests/lcitool/mappings.yml index 673baf3..8f0e95e 100644 --- a/tests/lcitool/mappings.yml +++ b/tests/lcitool/mappings.yml @@ -8,6 +8,10 @@ mappings: meson: OpenSUSELeap15: + # Use Meson from PyPI wherever Rust is enabled + Debian: + Fedora: + Ubuntu: python3: OpenSUSELeap15: python311-base @@ -72,7 +76,7 @@ mappings: pypi_mappings: # Request more recent version meson: - default: meson==1.5.0 + default: meson==1.8.1 # Drop packages that need devel headers python3-numpy: diff --git a/tests/lcitool/refresh b/tests/lcitool/refresh index 8474ea8..d3488b2 100755 --- a/tests/lcitool/refresh +++ b/tests/lcitool/refresh @@ -121,6 +121,7 @@ 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", @@ -135,6 +136,7 @@ fedora_rustup_nightly_extras = [ " /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', @@ -143,6 +145,7 @@ fedora_rustup_nightly_extras = [ 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", 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/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/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/tcg/aarch64/Makefile.softmmu-target b/tests/tcg/aarch64/Makefile.softmmu-target index 9c52475..f7a7d2b 100644 --- a/tests/tcg/aarch64/Makefile.softmmu-target +++ b/tests/tcg/aarch64/Makefile.softmmu-target @@ -68,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 diff --git a/tests/tcg/aarch64/system/boot.S b/tests/tcg/aarch64/system/boot.S index a5df9c1..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,21 +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, 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 @@ -100,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 */ @@ -198,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. */ @@ -233,6 +386,11 @@ __sys_outc: ret .data + + .align 8 +cmdline: + .space 128, 0 + .align 12 /* Translation table @@ -246,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/x86_64/fma.c b/tests/tcg/x86_64/fma.c index 09c622e..3421961 100644 --- a/tests/tcg/x86_64/fma.c +++ b/tests/tcg/x86_64/fma.c @@ -79,14 +79,21 @@ static testdata tests[] = { /* * Flushing of denormal outputs to zero should also happen after * rounding, so setting FTZ should not affect the result or the flags. - * QEMU currently does not emulate this correctly because we do the - * flush-to-zero check before rounding, so we incorrectly produce a - * zero result and set Underflow as well as Precision. */ -#ifdef ENABLE_FAILING_TESTS { 0x3fdfffffffffffff, 0x001fffffffffffff, 0x801fffffffffffff, true, 0x8010000000000000, 0x20 }, /* Enabling FTZ shouldn't change flags */ -#endif + /* + * 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) 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 290cd2a..59c2793 100644 --- a/tests/unit/test-bdrv-drain.c +++ b/tests/unit/test-bdrv-drain.c @@ -772,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: @@ -953,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 @@ -1014,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(); @@ -1047,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 */ @@ -1058,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); @@ -1155,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); @@ -1163,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) @@ -1260,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); @@ -1271,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); @@ -1396,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); @@ -1687,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) { @@ -1696,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); @@ -1942,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 d743abb..7b03ebe 100644 --- a/tests/unit/test-bdrv-graph-mod.c +++ b/tests/unit/test-bdrv-graph-mod.c @@ -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-char.c b/tests/unit/test-char.c index 60a843b..f30a39f 100644 --- a/tests/unit/test-char.c +++ b/tests/unit/test-char.c @@ -993,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]; @@ -1009,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); } 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. |