From 9b03e9cf06b33192d550564169f36eee07cf9fd0 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Wed, 8 Oct 2025 15:19:36 +0200 Subject: [PATCH 01/10] python/qemu: Replace some remaining "avocados" with "functional tests" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The avocado tests have been replaced by the new functional tests, so also update this in the README.rst files in the python directory accordingly. Reviewed-by: Daniel P. Berrangé Reviewed-by: Michael Tokarev Reviewed-by: Alex Bennée Signed-off-by: Thomas Huth Message-ID: <20251008131936.71160-1-thuth@redhat.com> --- python/qemu/machine/README.rst | 2 +- python/qemu/utils/README.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/python/qemu/machine/README.rst b/python/qemu/machine/README.rst index 8de2c3d772..6554c69320 100644 --- a/python/qemu/machine/README.rst +++ b/python/qemu/machine/README.rst @@ -2,7 +2,7 @@ qemu.machine package ==================== This package provides core utilities used for testing and debugging -QEMU. It is used by the iotests, vm tests, avocado tests, and several +QEMU. It is used by the iotests, vm tests, functional tests, and several other utilities in the ./scripts directory. It is not a fully-fledged SDK and it is subject to change at any time. diff --git a/python/qemu/utils/README.rst b/python/qemu/utils/README.rst index d5f2da1454..5027f0b5f1 100644 --- a/python/qemu/utils/README.rst +++ b/python/qemu/utils/README.rst @@ -2,6 +2,6 @@ qemu.utils package ================== This package provides miscellaneous utilities used for testing and -debugging QEMU. It is used primarily by the vm and avocado tests. +debugging QEMU. It is used primarily by the vm and functional tests. See the documentation in ``__init__.py`` for more information. From dadd7419d361f8f9f012cbc1413993132cd55b7a Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Mon, 6 Oct 2025 18:18:50 +0200 Subject: [PATCH 02/10] tests/functional/aarch64: Drop some sbsaref_alpine tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit test_sbsaref_alpine is one of the longest running test in our testsuite, because it does a full Linux boot a couple of times, for various different CPU configurations. That's quite a lot of testing each time, for a rather small additional test coverage. Thus let's drop some of the tests that don't provide much in addition to the other ones. Suggested-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Thomas Huth Message-ID: <20251006161850.181998-1-thuth@redhat.com> --- tests/functional/aarch64/test_sbsaref_alpine.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/tests/functional/aarch64/test_sbsaref_alpine.py b/tests/functional/aarch64/test_sbsaref_alpine.py index abb8f5114b..be84b7adb0 100755 --- a/tests/functional/aarch64/test_sbsaref_alpine.py +++ b/tests/functional/aarch64/test_sbsaref_alpine.py @@ -41,15 +41,9 @@ class Aarch64SbsarefAlpine(QemuSystemTest): 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") From 1d7fdee97b8959871d4093d89a79192155ca674f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Fri, 10 Oct 2025 17:05:45 +0100 Subject: [PATCH 03/10] gitlab: purge msys pacman cache MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For the Windows msys2 CI job we install many packages using pacman and use the GitLab cache to preserve the pacman cache across CI runs. While metadata still needs downloading, this avoids pacman re-downloading packages from msys2 if they have not changed. The problem is that pacman never automatically purges anything from its package cache. Thus the GitLab cache is growing without bound and packing/unpacking the cache is consuming an increasing amount of time in the CI job. If we run 'pacman -Sc' /after/ installing our desired package set, it will purge any cached downloaded packages that are not matching any installed package. This will (currently) cap the pacman download cache at approx 256 MB. Signed-off-by: Daniel P. Berrangé Reviewed-by: Yonggang Luo Reviewed-by: Thomas Huth Tested-by: Thomas Huth Reviewed-by: Peter Maydell Message-ID: <20251010160545.144760-1-berrange@redhat.com> Signed-off-by: Thomas Huth --- .gitlab-ci.d/windows.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitlab-ci.d/windows.yml b/.gitlab-ci.d/windows.yml index 1e6a01bd9a..6e1135d8b8 100644 --- a/.gitlab-ci.d/windows.yml +++ b/.gitlab-ci.d/windows.yml @@ -87,6 +87,7 @@ msys2-64bit: mingw-w64-x86_64-pkgconf mingw-w64-x86_64-python mingw-w64-x86_64-zstd" + - .\msys64\usr\bin\bash -lc "pacman -Sc --noconfirm" - Write-Output "Running build at $(Get-Date -Format u)" - $env:JOBS = $(.\msys64\usr\bin\bash -lc nproc) - $env:CHERE_INVOKING = 'yes' # Preserve the current working directory From cb1379ce646e9f41b096c4d7590e558393e6e960 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 14 Oct 2025 10:34:23 +0200 Subject: [PATCH 04/10] tests/functional: Set current time stamp of assets when using them MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We are going to remove obsolete assets from the cache, so keep the time stamps of the assets that we use up-to-date to have a way to detect stale assets later. Reviewed-by: Daniel P. Berrangé Signed-off-by: Thomas Huth Message-ID: <20251014083424.103202-2-thuth@redhat.com> --- tests/functional/qemu_test/asset.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tests/functional/qemu_test/asset.py b/tests/functional/qemu_test/asset.py index f666125bfa..ab3a7bb591 100644 --- a/tests/functional/qemu_test/asset.py +++ b/tests/functional/qemu_test/asset.py @@ -10,6 +10,7 @@ import logging import os import stat import sys +import time import unittest import urllib.request from time import sleep @@ -113,6 +114,16 @@ class Asset: self.log.debug("Time out while waiting for %s!", tmp_cache_file) raise + def _save_time_stamp(self): + ''' + Update the time stamp of the asset in the cache. Unfortunately, we + cannot use the modification or access time of the asset file itself, + since e.g. the functional jobs in the gitlab CI reload the files + from the gitlab cache and thus always have recent file time stamps, + so we have to save our asset time stamp to a separate file instead. + ''' + self.cache_file.with_suffix(".stamp").write_text(f"{int(time.time())}") + def fetch(self): if not self.cache_dir.exists(): self.cache_dir.mkdir(parents=True, exist_ok=True) @@ -120,6 +131,7 @@ class Asset: if self.valid(): self.log.debug("Using cached asset %s for %s", self.cache_file, self.url) + self._save_time_stamp() return str(self.cache_file) if not self.fetchable(): @@ -208,6 +220,7 @@ class Asset: tmp_cache_file.unlink() raise AssetError(self, "Hash does not match %s" % self.hash) tmp_cache_file.replace(self.cache_file) + self._save_time_stamp() # Remove write perms to stop tests accidentally modifying them os.chmod(self.cache_file, stat.S_IRUSR | stat.S_IRGRP) From dd10dbe61751dc85a4d84ffde73f341ab027eb73 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 14 Oct 2025 10:34:24 +0200 Subject: [PATCH 05/10] tests: Evict stale files in the functional download cache after a while MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The download cache of the functional tests is currently only growing. But sometimes tests get removed or changed to use different assets, thus we should clean up the stale old assets after a while when they are not in use anymore. So add a script that looks at the time stamps of the assets and removes them if they haven't been touched for more than half of a year. Since there might also be some assets around that have been added to the cache before we added the time stamp files, assume a default time stamp that is close to the creation date of this patch, so that we don't delete these files too early (so we still have all assets around in case we have to bisect an issue in the recent past of QEMU). Reviewed-by: Daniel P. Berrangé Signed-off-by: Thomas Huth Message-ID: <20251014083424.103202-3-thuth@redhat.com> --- MAINTAINERS | 1 + scripts/clean_functional_cache.py | 45 +++++++++++++++++++++++++++++++ tests/Makefile.include | 1 + 3 files changed, 47 insertions(+) create mode 100755 scripts/clean_functional_cache.py diff --git a/MAINTAINERS b/MAINTAINERS index 0c766961f3..667acd933c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4397,6 +4397,7 @@ M: Thomas Huth R: Philippe Mathieu-Daudé R: Daniel P. Berrange F: docs/devel/testing/functional.rst +F: scripts/clean_functional_cache.py F: tests/functional/qemu_test/ Windows Hosted Continuous Integration diff --git a/scripts/clean_functional_cache.py b/scripts/clean_functional_cache.py new file mode 100755 index 0000000000..c3370ffbb8 --- /dev/null +++ b/scripts/clean_functional_cache.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +"""Delete stale assets from the download cache of the functional tests""" + +import os +import stat +import sys +import time +from pathlib import Path + + +cache_dir_env = os.getenv('QEMU_TEST_CACHE_DIR') +if cache_dir_env: + cache_dir = Path(cache_dir_env, "download") +else: + cache_dir = Path(Path("~").expanduser(), ".cache", "qemu", "download") + +if not cache_dir.exists(): + print(f"Cache dir {cache_dir} does not exist!", file=sys.stderr) + sys.exit(1) + +os.chdir(cache_dir) + +for file in cache_dir.iterdir(): + # Only consider the files that use a sha256 as filename: + if len(file.name) != 64: + continue + + try: + timestamp = int(file.with_suffix(".stamp").read_text()) + except FileNotFoundError: + # Assume it's an old file that was already in the cache before we + # added the code for evicting stale assets. Use the release date + # of QEMU v10.1 as a default timestamp. + timestamp = time.mktime((2025, 8, 26, 0, 0, 0, 0, 0, 0)) + + age = time.time() - timestamp + + # Delete files older than half of a year (183 days * 24h * 60m * 60s) + if age > 15811200: + print(f"Removing {cache_dir}/{file.name}.") + file.chmod(stat.S_IWRITE) + file.unlink() diff --git a/tests/Makefile.include b/tests/Makefile.include index e47ef4d45c..d4dfbf3716 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -111,6 +111,7 @@ $(FUNCTIONAL_TARGETS): check-venv .PHONY: check-functional check-functional: check-venv @$(NINJA) precache-functional + @$(PYTHON) $(SRC_PATH)/scripts/clean_functional_cache.py @QEMU_TEST_NO_DOWNLOAD=1 $(MAKE) SPEED=thorough check-func check-func-quick .PHONY: check-func check-func-quick From 420f6c041705ebc9a7faf3cc0fd6deba865694ae Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Fri, 10 Oct 2025 16:45:25 +0200 Subject: [PATCH 06/10] tests/functional/alpha: Remove superfluous fetch() line from the clipper test The kernel asset is retrieved automatically via the uncompress() line below the fetch(), so the fetch() is simply not necessary here. Reviewed-by: Richard Henderson Signed-off-by: Thomas Huth Message-ID: <20251010144525.842462-1-thuth@redhat.com> --- tests/functional/alpha/test_clipper.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/functional/alpha/test_clipper.py b/tests/functional/alpha/test_clipper.py index c5d7181953..d2a4c2a4ed 100755 --- a/tests/functional/alpha/test_clipper.py +++ b/tests/functional/alpha/test_clipper.py @@ -17,7 +17,6 @@ class AlphaClipperTest(LinuxKernelTest): def test_alpha_clipper(self): self.set_machine('clipper') - kernel_path = self.ASSET_KERNEL.fetch() uncompressed_kernel = self.uncompress(self.ASSET_KERNEL, format="gz") From 5c2a4b59fad21a3e71b7a8c155d374af952b585f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 14 Oct 2025 15:00:46 +0100 Subject: [PATCH 07/10] tests/functional: remove use of getLogger in reverse debuging MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This fixes the gap left by commit 8a44d8c2ac0921c8064fbfd00ef28e3a2588918e Author: Daniel P. Berrangé Date: Fri Sep 12 19:22:00 2025 +0100 tests/functional: use self.log for all logging ensuring that log message from the reverse debugging test actually make it into the logfile on disk. Signed-off-by: Daniel P. Berrangé Message-ID: <20251014140047.385347-2-berrange@redhat.com> Reviewed-by: Thomas Huth Tested-by: Thomas Huth Signed-off-by: Thomas Huth --- tests/functional/reverse_debugging.py | 49 ++++++++++++--------------- 1 file changed, 22 insertions(+), 27 deletions(-) diff --git a/tests/functional/reverse_debugging.py b/tests/functional/reverse_debugging.py index 68cfcb3985..2c37a62cd0 100644 --- a/tests/functional/reverse_debugging.py +++ b/tests/functional/reverse_debugging.py @@ -36,14 +36,13 @@ class ReverseDebugging(LinuxKernelTest): STEPS = 10 def run_vm(self, record, shift, args, replay_path, image_path, port): - logger = logging.getLogger('replay') vm = self.get_vm(name='record' if record else 'replay') vm.set_console() if record: - logger.info('recording the execution...') + self.log.info('recording the execution...') mode = 'record' else: - logger.info('replaying the execution...') + self.log.info('replaying the execution...') mode = 'replay' vm.add_args('-gdb', 'tcp::%d' % port, '-S') vm.add_args('-icount', 'shift=%s,rr=%s,rrfile=%s,rrsnapshot=init' % @@ -68,10 +67,8 @@ class ReverseDebugging(LinuxKernelTest): def reverse_debugging(self, gdb_arch, shift=7, args=None): from qemu_test import GDB - logger = logging.getLogger('replay') - # create qcow2 for snapshots - logger.info('creating qcow2 image for VM snapshots') + self.log.info('creating qcow2 image for VM snapshots') image_path = os.path.join(self.workdir, 'disk.qcow2') qemu_img = get_qemu_img(self) if qemu_img is None: @@ -79,7 +76,7 @@ class ReverseDebugging(LinuxKernelTest): 'create the temporary qcow2 image') out = check_output([qemu_img, 'create', '-f', 'qcow2', image_path, '128M'], encoding='utf8') - logger.info("qemu-img: %s" % out) + self.log.info("qemu-img: %s" % out) replay_path = os.path.join(self.workdir, 'replay.bin') @@ -90,7 +87,7 @@ class ReverseDebugging(LinuxKernelTest): last_icount = self.vm_get_icount(vm) vm.shutdown() - logger.info("recorded log with %s+ steps" % last_icount) + self.log.info("recorded log with %s+ steps" % last_icount) # replay and run debug commands with Ports() as ports: @@ -98,9 +95,9 @@ class ReverseDebugging(LinuxKernelTest): vm = self.run_vm(False, shift, args, replay_path, image_path, port) try: - logger.info('Connecting to gdbstub...') + self.log.info('Connecting to gdbstub...') self.reverse_debugging_run(vm, port, gdb_arch, last_icount) - logger.info('Test passed.') + self.log.info('Test passed.') except GDB.TimeoutError: # Convert a GDB timeout exception into a unittest failure exception. raise self.failureException("Timeout while connecting to or " @@ -111,8 +108,6 @@ class ReverseDebugging(LinuxKernelTest): raise def reverse_debugging_run(self, vm, port, gdb_arch, last_icount): - logger = logging.getLogger('replay') - gdb_cmd = os.getenv('QEMU_TEST_GDB') gdb = GDB(gdb_cmd) @@ -135,43 +130,43 @@ class ReverseDebugging(LinuxKernelTest): gdb.cli("set debug remote 0") - logger.info('stepping forward') + self.log.info('stepping forward') steps = [] # record first instruction addresses for _ in range(self.STEPS): pc = self.get_pc(gdb) - logger.info('saving position %x' % pc) + self.log.info('saving position %x' % pc) steps.append(pc) gdb.cli("stepi") # visit the recorded instruction in reverse order - logger.info('stepping backward') + self.log.info('stepping backward') for addr in steps[::-1]: - logger.info('found position %x' % addr) + self.log.info('found position %x' % addr) gdb.cli("reverse-stepi") pc = self.get_pc(gdb) if pc != addr: - logger.info('Invalid PC (read %x instead of %x)' % (pc, addr)) + self.log.info('Invalid PC (read %x instead of %x)' % (pc, addr)) self.fail('Reverse stepping failed!') # visit the recorded instruction in forward order - logger.info('stepping forward') + self.log.info('stepping forward') for addr in steps: - logger.info('found position %x' % addr) + self.log.info('found position %x' % addr) pc = self.get_pc(gdb) if pc != addr: - logger.info('Invalid PC (read %x instead of %x)' % (pc, addr)) + self.log.info('Invalid PC (read %x instead of %x)' % (pc, addr)) self.fail('Forward stepping failed!') gdb.cli("stepi") # set breakpoints for the instructions just stepped over - logger.info('setting breakpoints') + self.log.info('setting breakpoints') for addr in steps: gdb.cli(f"break *{hex(addr)}") # this may hit a breakpoint if first instructions are executed # again - logger.info('continuing execution') + self.log.info('continuing execution') vm.qmp('replay-break', icount=last_icount - 1) # continue - will return after pausing # This can stop at the end of the replay-break and gdb gets a SIGINT, @@ -180,12 +175,12 @@ class ReverseDebugging(LinuxKernelTest): gdb.cli("continue") if self.vm_get_icount(vm) == last_icount - 1: - logger.info('reached the end (icount %s)' % (last_icount - 1)) + self.log.info('reached the end (icount %s)' % (last_icount - 1)) else: - logger.info('hit a breakpoint again at %x (icount %s)' % + self.log.info('hit a breakpoint again at %x (icount %s)' % (self.get_pc(gdb), self.vm_get_icount(vm))) - logger.info('running reverse continue to reach %x' % steps[-1]) + self.log.info('running reverse continue to reach %x' % steps[-1]) # reverse continue - will return after stopping at the breakpoint gdb.cli("reverse-continue") @@ -195,8 +190,8 @@ class ReverseDebugging(LinuxKernelTest): if pc != steps[-1]: self.fail("'reverse-continue' did not hit the first PC in reverse order!") - logger.info('successfully reached %x' % steps[-1]) + self.log.info('successfully reached %x' % steps[-1]) - logger.info('exiting gdb and qemu') + self.log.info('exiting gdb and qemu') gdb.exit() vm.shutdown() From 03ec40942d678ef1e98439508d1cdb2c05ff3e72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 14 Oct 2025 15:00:47 +0100 Subject: [PATCH 08/10] tests/functional: ensure GDB client is stopped on error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If the reverse_debugging_run method fails, the GDB client will not be closed resulting in python complaining about resource leaks. Hoisting the GDB client creation into the caller allows this to be cleaned up easily. While doing this, also move the VM shutdown call to match. Signed-off-by: Daniel P. Berrangé Message-ID: <20251014140047.385347-3-berrange@redhat.com> Reviewed-by: Thomas Huth Signed-off-by: Thomas Huth --- tests/functional/reverse_debugging.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/tests/functional/reverse_debugging.py b/tests/functional/reverse_debugging.py index 2c37a62cd0..86fca8d81f 100644 --- a/tests/functional/reverse_debugging.py +++ b/tests/functional/reverse_debugging.py @@ -96,7 +96,14 @@ class ReverseDebugging(LinuxKernelTest): try: self.log.info('Connecting to gdbstub...') - self.reverse_debugging_run(vm, port, gdb_arch, last_icount) + gdb_cmd = os.getenv('QEMU_TEST_GDB') + gdb = GDB(gdb_cmd) + try: + self.reverse_debugging_run(gdb, vm, port, gdb_arch, last_icount) + finally: + self.log.info('exiting gdb and qemu') + gdb.exit() + vm.shutdown() self.log.info('Test passed.') except GDB.TimeoutError: # Convert a GDB timeout exception into a unittest failure exception. @@ -107,10 +114,7 @@ class ReverseDebugging(LinuxKernelTest): # skipTest(), etc. raise - def reverse_debugging_run(self, vm, port, gdb_arch, last_icount): - gdb_cmd = os.getenv('QEMU_TEST_GDB') - gdb = GDB(gdb_cmd) - + def reverse_debugging_run(self, gdb, vm, port, gdb_arch, last_icount): r = gdb.cli("set architecture").get_log() if gdb_arch not in r: self.skipTest(f"GDB does not support arch '{gdb_arch}'") @@ -191,7 +195,3 @@ class ReverseDebugging(LinuxKernelTest): self.fail("'reverse-continue' did not hit the first PC in reverse order!") self.log.info('successfully reached %x' % steps[-1]) - - self.log.info('exiting gdb and qemu') - gdb.exit() - vm.shutdown() From 02cf15e9bbe902523f5a1deb1dd48aef50e26e41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 8 Oct 2025 16:14:08 +0200 Subject: [PATCH 09/10] target/s390x/mmu_helper: Simplify s390_cpu_virt_mem_rw() logic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In order to simplify the next commit, move the trigger_access_exception() call after the address_space_rw() calls. No logical change intended. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Message-ID: <20251008141410.99865-2-philmd@linaro.org> Signed-off-by: Thomas Huth --- target/s390x/mmu_helper.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/target/s390x/mmu_helper.c b/target/s390x/mmu_helper.c index 487c41bf93..22d3d4a97d 100644 --- a/target/s390x/mmu_helper.c +++ b/target/s390x/mmu_helper.c @@ -541,9 +541,7 @@ int s390_cpu_virt_mem_rw(S390CPU *cpu, vaddr laddr, uint8_t ar, void *hostbuf, pages = g_malloc(nr_pages * sizeof(*pages)); ret = translate_pages(cpu, laddr, nr_pages, pages, is_write, &tec); - if (ret) { - trigger_access_exception(&cpu->env, ret, tec); - } else if (hostbuf != NULL) { + if (ret == 0 && hostbuf != NULL) { AddressSpace *as = CPU(cpu)->as; /* Copy data by stepping through the area page by page */ @@ -556,6 +554,9 @@ int s390_cpu_virt_mem_rw(S390CPU *cpu, vaddr laddr, uint8_t ar, void *hostbuf, len -= currlen; } } + if (ret) { + trigger_access_exception(&cpu->env, ret, tec); + } g_free(pages); return ret; From d6f7f9254e333c56226bb7051e74ea57daea2fff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 8 Oct 2025 16:14:09 +0200 Subject: [PATCH 10/10] target/s390x/mmu_helper: Do not ignore address_space_rw() errors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If a address_space_rw() call ever fails, break the loop and return the PGM_ADDRESSING error (after triggering an access exception). Signed-off-by: Philippe Mathieu-Daudé Message-ID: <20251008141410.99865-3-philmd@linaro.org> Reviewed-by: Thomas Huth Signed-off-by: Thomas Huth --- target/s390x/mmu_helper.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/target/s390x/mmu_helper.c b/target/s390x/mmu_helper.c index 22d3d4a97d..3b1e75f783 100644 --- a/target/s390x/mmu_helper.c +++ b/target/s390x/mmu_helper.c @@ -546,9 +546,15 @@ int s390_cpu_virt_mem_rw(S390CPU *cpu, vaddr laddr, uint8_t ar, void *hostbuf, /* Copy data by stepping through the area page by page */ for (i = 0; i < nr_pages; i++) { + MemTxResult res; + currlen = MIN(len, TARGET_PAGE_SIZE - (laddr % TARGET_PAGE_SIZE)); - address_space_rw(as, pages[i] | (laddr & ~TARGET_PAGE_MASK), - attrs, hostbuf, currlen, is_write); + res = address_space_rw(as, pages[i] | (laddr & ~TARGET_PAGE_MASK), + attrs, hostbuf, currlen, is_write); + if (res != MEMTX_OK) { + ret = PGM_ADDRESSING; + break; + } laddr += currlen; hostbuf += currlen; len -= currlen;