From 27535e9ccae89db5856bfb5e3357f44645812143 Mon Sep 17 00:00:00 2001 From: Xin Wang Date: Tue, 19 Aug 2025 22:58:34 +0800 Subject: [PATCH 0001/2396] target/i386: Add support for save/load of exception error code For now, qemu save/load CPU exception info(such as exception_nr and has_error_code), while the exception error_code is ignored. This will cause the dest hypervisor reinject a vCPU exception with error_code(0), potentially causing a guest kernel panic. For instance, if src VM stopped with an user-mode write #PF (error_code 6), the dest hypervisor will reinject an #PF with error_code(0) when vCPU resume, then guest kernel panic as: BUG: unable to handle page fault for address: 00007f80319cb010 #PF: supervisor read access in user mode #PF: error_code(0x0000) - not-present page RIP: 0033:0x40115d To fix it, support save/load exception error_code. Signed-off-by: Xin Wang Link: https://lore.kernel.org/r/20250819145834.3998-1-wangxinxin.wang@huawei.com Signed-off-by: Paolo Bonzini --- target/i386/machine.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/target/i386/machine.c b/target/i386/machine.c index dd2dac1d44..45b7cea80a 100644 --- a/target/i386/machine.c +++ b/target/i386/machine.c @@ -462,6 +462,24 @@ static const VMStateDescription vmstate_exception_info = { } }; +static bool cpu_errcode_needed(void *opaque) +{ + X86CPU *cpu = opaque; + + return cpu->env.has_error_code != 0; +} + +static const VMStateDescription vmstate_error_code = { + .name = "cpu/error_code", + .version_id = 1, + .minimum_version_id = 1, + .needed = cpu_errcode_needed, + .fields = (const VMStateField[]) { + VMSTATE_INT32(env.error_code, X86CPU), + VMSTATE_END_OF_LIST() + } +}; + /* Poll control MSR enabled by default */ static bool poll_control_msr_needed(void *opaque) { @@ -1746,6 +1764,7 @@ const VMStateDescription vmstate_x86_cpu = { }, .subsections = (const VMStateDescription * const []) { &vmstate_exception_info, + &vmstate_error_code, &vmstate_async_pf_msr, &vmstate_async_pf_int_msr, &vmstate_pv_eoi_msr, From a9292b24c35ca40da5bc4b2fd7fcf898b08dcce9 Mon Sep 17 00:00:00 2001 From: Manos Pitsidianakis Date: Wed, 20 Aug 2025 23:41:56 +0300 Subject: [PATCH 0002/2396] scripts/minikconf.py: fix invalid attribute access Fix parse method to use `defconfig` global variable instead of the non-existent KconfigParser class attribute Fixes: f349474920d80838ecea3d421531fdb0660b8740 ("minikconfig: implement allnoconfig and defconfig modes") Signed-off-by: Manos Pitsidianakis Link: https://lore.kernel.org/r/20250820-scripts-minikconf-fixes-v1-1-252041a9125e@linaro.org Signed-off-by: Paolo Bonzini --- scripts/minikconf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/minikconf.py b/scripts/minikconf.py index 6f7f43b291..2a4694fb6a 100644 --- a/scripts/minikconf.py +++ b/scripts/minikconf.py @@ -340,7 +340,7 @@ class KconfigParser: @classmethod def parse(self, fp, mode=None): - data = KconfigData(mode or KconfigParser.defconfig) + data = KconfigData(mode or defconfig) parser = KconfigParser(data) parser.parse_file(fp) return data From d84082cc1a7a7cac361094fc9b3165df7c697a01 Mon Sep 17 00:00:00 2001 From: Manos Pitsidianakis Date: Wed, 20 Aug 2025 23:41:57 +0300 Subject: [PATCH 0003/2396] scripts/minikconf.py: s/Error/KconfigParserError Error is not defined in this script, raise KconfigParserError instead. Fixes: 82f5181777ebe04b550fd94a1d04c49dd3f012dc ("kconfig: introduce kconfig files") Signed-off-by: Manos Pitsidianakis Link: https://lore.kernel.org/r/20250820-scripts-minikconf-fixes-v1-2-252041a9125e@linaro.org Signed-off-by: Paolo Bonzini --- scripts/minikconf.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/scripts/minikconf.py b/scripts/minikconf.py index 2a4694fb6a..4de5aeed11 100644 --- a/scripts/minikconf.py +++ b/scripts/minikconf.py @@ -363,7 +363,9 @@ class KconfigParser: def do_assignment(self, var, val): if not var.startswith("CONFIG_"): - raise Error('assigned variable should start with CONFIG_') + raise KconfigParserError( + self, "assigned variable should start with CONFIG_" + ) var = self.data.do_var(var[7:]) self.data.do_assignment(var, val) From ab85146ac4c6527d6d975afbd3157488cb42147f Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 22 Aug 2025 10:46:05 +0200 Subject: [PATCH 0004/2396] python: mkvenv: fix messages printed by mkvenv MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The new Matcher class does not have a __str__ implementation, and therefore it prints the debugging representation of the internal object: $ ../configure --enable-rust && make qemu-system-arm --enable-download python determined to be '/usr/bin/python3' python version: Python 3.13.6 mkvenv: Creating non-isolated virtual environment at 'pyvenv' mkvenv: checking for LegacyMatcher('meson>=1.5.0') mkvenv: checking for LegacyMatcher('pycotap>=1.1.0') Add the method to print the nicer mkvenv: checking for meson>=1.5.0 mkvenv: checking for pycotap>=1.1.0 Cc: qemu-stable@nongnu.org Cc: John Snow Reviewed-by: Manos Pitsidianakis Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Paolo Bonzini --- python/scripts/mkvenv.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/python/scripts/mkvenv.py b/python/scripts/mkvenv.py index f102527c4d..9aed266df1 100644 --- a/python/scripts/mkvenv.py +++ b/python/scripts/mkvenv.py @@ -184,6 +184,10 @@ class Matcher: ) ) + def __str__(self) -> str: + """String representation delegated to the backend.""" + return str(self._m) + def __repr__(self) -> str: """Stable debug representation delegated to the backend.""" return repr(self._m) From 6f8924163ff1fb4bd19e0cd9dc7910bb540486f3 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 19 Aug 2025 17:42:16 +0200 Subject: [PATCH 0005/2396] MAINTAINERS: add a few more files to "Top Level Makefile and configure" A few files in scripts, and the list of packages in pythondeps.toml, are strictly related to the toplevel build scripts. Add them to the MAINTAINERS file stanza. Signed-off-by: Paolo Bonzini --- MAINTAINERS | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index a07086ed76..0f3e55b51e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4392,7 +4392,6 @@ R: Philippe Mathieu-Daudé S: Maintained F: meson.build F: meson_options.txt -F: scripts/meson-buildoptions.* F: scripts/check_sparse.py F: scripts/symlink-install-tree.py @@ -4403,6 +4402,9 @@ R: Thomas Huth S: Maintained F: Makefile F: configure +F: pythondeps.toml +F: scripts/git-submodule.sh +F: scripts/meson-buildoptions.* F: scripts/mtest2make.py F: tests/Makefile.include From e771ba98de25c9f43959f79fc7099cf7fbba44cc Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Tue, 26 Aug 2025 14:10:25 -0400 Subject: [PATCH 0006/2396] Open 10.2 development tree Signed-off-by: Stefan Hajnoczi --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 4149c39eec..9856be5dd9 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -10.1.0 +10.1.50 From d96519c6bd1e197f24a157ec761ff69cf54779d3 Mon Sep 17 00:00:00 2001 From: Cornelia Huck Date: Tue, 5 Aug 2025 11:56:16 +0200 Subject: [PATCH 0007/2396] hw: add compat machines for 10.2 Add 10.2 machine types for arm/i440fx/m68k/q35/s390x/spapr. Signed-off-by: Cornelia Huck Reviewed-by: Thomas Huth Acked-by: Michael S. Tsirkin Message-ID: <20250805095616.1168905-1-cohuck@redhat.com> Signed-off-by: Thomas Huth --- hw/arm/virt.c | 11 +++++++++-- hw/core/machine.c | 3 +++ hw/i386/pc.c | 3 +++ hw/i386/pc_piix.c | 13 +++++++++++-- hw/i386/pc_q35.c | 13 +++++++++++-- hw/m68k/virt.c | 11 +++++++++-- hw/ppc/spapr.c | 17 ++++++++++++++--- hw/s390x/s390-virtio-ccw.c | 14 +++++++++++++- include/hw/boards.h | 3 +++ include/hw/i386/pc.h | 3 +++ 10 files changed, 79 insertions(+), 12 deletions(-) diff --git a/hw/arm/virt.c b/hw/arm/virt.c index ef6be3660f..9326cfc895 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -3455,10 +3455,17 @@ static void machvirt_machine_init(void) } type_init(machvirt_machine_init); -static void virt_machine_10_1_options(MachineClass *mc) +static void virt_machine_10_2_options(MachineClass *mc) { } -DEFINE_VIRT_MACHINE_AS_LATEST(10, 1) +DEFINE_VIRT_MACHINE_AS_LATEST(10, 2) + +static void virt_machine_10_1_options(MachineClass *mc) +{ + virt_machine_10_2_options(mc); + compat_props_add(mc->compat_props, hw_compat_10_1, hw_compat_10_1_len); +} +DEFINE_VIRT_MACHINE(10, 1) static void virt_machine_10_0_options(MachineClass *mc) { diff --git a/hw/core/machine.c b/hw/core/machine.c index bd47527479..38c949c4f2 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -37,6 +37,9 @@ #include "hw/virtio/virtio-iommu.h" #include "audio/audio.h" +GlobalProperty hw_compat_10_1[] = {}; +const size_t hw_compat_10_1_len = G_N_ELEMENTS(hw_compat_10_1); + GlobalProperty hw_compat_10_0[] = { { "scsi-hd", "dpofua", "off" }, { "vfio-pci", "x-migration-load-config-after-iter", "off" }, diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 2f58e73d33..bc048a6d13 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -81,6 +81,9 @@ { "qemu64-" TYPE_X86_CPU, "model-id", "QEMU Virtual CPU version " v, },\ { "athlon-" TYPE_X86_CPU, "model-id", "QEMU Virtual CPU version " v, }, +GlobalProperty pc_compat_10_1[] = {}; +const size_t pc_compat_10_1_len = G_N_ELEMENTS(pc_compat_10_1); + GlobalProperty pc_compat_10_0[] = { { TYPE_X86_CPU, "x-consistent-cache", "false" }, { TYPE_X86_CPU, "x-vendor-cpuid-only-v2", "false" }, diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index c03324281b..d165ac72ed 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -504,12 +504,21 @@ static void pc_i440fx_machine_options(MachineClass *m) pc_piix_compat_defaults, pc_piix_compat_defaults_len); } -static void pc_i440fx_machine_10_1_options(MachineClass *m) +static void pc_i440fx_machine_10_2_options(MachineClass *m) { pc_i440fx_machine_options(m); } -DEFINE_I440FX_MACHINE_AS_LATEST(10, 1); +DEFINE_I440FX_MACHINE_AS_LATEST(10, 2); + +static void pc_i440fx_machine_10_1_options(MachineClass *m) +{ + pc_i440fx_machine_10_2_options(m); + compat_props_add(m->compat_props, hw_compat_10_1, hw_compat_10_1_len); + compat_props_add(m->compat_props, pc_compat_10_1, pc_compat_10_1_len); +} + +DEFINE_I440FX_MACHINE(10, 1); static void pc_i440fx_machine_10_0_options(MachineClass *m) { diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index b309b2b378..e89951285e 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -374,12 +374,21 @@ static void pc_q35_machine_options(MachineClass *m) pc_q35_compat_defaults, pc_q35_compat_defaults_len); } -static void pc_q35_machine_10_1_options(MachineClass *m) +static void pc_q35_machine_10_2_options(MachineClass *m) { pc_q35_machine_options(m); } -DEFINE_Q35_MACHINE_AS_LATEST(10, 1); +DEFINE_Q35_MACHINE_AS_LATEST(10, 2); + +static void pc_q35_machine_10_1_options(MachineClass *m) +{ + pc_q35_machine_10_2_options(m); + compat_props_add(m->compat_props, hw_compat_10_1, hw_compat_10_1_len); + compat_props_add(m->compat_props, pc_compat_10_1, pc_compat_10_1_len); +} + +DEFINE_Q35_MACHINE(10, 1); static void pc_q35_machine_10_0_options(MachineClass *m) { diff --git a/hw/m68k/virt.c b/hw/m68k/virt.c index 875fd00ef8..98cfe43c73 100644 --- a/hw/m68k/virt.c +++ b/hw/m68k/virt.c @@ -367,10 +367,17 @@ type_init(virt_machine_register_types) #define DEFINE_VIRT_MACHINE(major, minor) \ DEFINE_VIRT_MACHINE_IMPL(false, major, minor) -static void virt_machine_10_1_options(MachineClass *mc) +static void virt_machine_10_2_options(MachineClass *mc) { } -DEFINE_VIRT_MACHINE_AS_LATEST(10, 1) +DEFINE_VIRT_MACHINE_AS_LATEST(10, 2) + +static void virt_machine_10_1_options(MachineClass *mc) +{ + virt_machine_10_2_options(mc); + compat_props_add(mc->compat_props, hw_compat_10_1, hw_compat_10_1_len); +} +DEFINE_VIRT_MACHINE(10, 1) static void virt_machine_10_0_options(MachineClass *mc) { diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 1855a3cd8d..eb22333404 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -4762,14 +4762,25 @@ static void spapr_machine_latest_class_options(MachineClass *mc) DEFINE_SPAPR_MACHINE_IMPL(false, major, minor) /* - * pseries-10.1 + * pseries-10.2 */ -static void spapr_machine_10_1_class_options(MachineClass *mc) +static void spapr_machine_10_2_class_options(MachineClass *mc) { /* Defaults for the latest behaviour inherited from the base class */ } -DEFINE_SPAPR_MACHINE_AS_LATEST(10, 1); +DEFINE_SPAPR_MACHINE_AS_LATEST(10, 2); + +/* + * pseries-10.1 + */ +static void spapr_machine_10_1_class_options(MachineClass *mc) +{ + spapr_machine_10_2_class_options(mc); + compat_props_add(mc->compat_props, hw_compat_10_1, hw_compat_10_1_len); +} + +DEFINE_SPAPR_MACHINE(10, 1); /* * pseries-10.0 diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index a79bd13275..d0c6e80cb0 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -911,14 +911,26 @@ static const TypeInfo ccw_machine_info = { DEFINE_CCW_MACHINE_IMPL(false, major, minor) +static void ccw_machine_10_2_instance_options(MachineState *machine) +{ +} + +static void ccw_machine_10_2_class_options(MachineClass *mc) +{ +} +DEFINE_CCW_MACHINE_AS_LATEST(10, 2); + static void ccw_machine_10_1_instance_options(MachineState *machine) { + ccw_machine_10_2_instance_options(machine); } static void ccw_machine_10_1_class_options(MachineClass *mc) { + ccw_machine_10_2_class_options(mc); + compat_props_add(mc->compat_props, hw_compat_10_1, hw_compat_10_1_len); } -DEFINE_CCW_MACHINE_AS_LATEST(10, 1); +DEFINE_CCW_MACHINE(10, 1); static void ccw_machine_10_0_instance_options(MachineState *machine) { diff --git a/include/hw/boards.h b/include/hw/boards.h index f94713e6e2..665b620121 100644 --- a/include/hw/boards.h +++ b/include/hw/boards.h @@ -779,6 +779,9 @@ struct MachineState { } \ type_init(machine_initfn##_register_types) +extern GlobalProperty hw_compat_10_1[]; +extern const size_t hw_compat_10_1_len; + extern GlobalProperty hw_compat_10_0[]; extern const size_t hw_compat_10_0_len; diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index 79b72c54dd..e83157ab35 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -214,6 +214,9 @@ void pc_system_parse_ovmf_flash(uint8_t *flash_ptr, size_t flash_size); /* sgx.c */ void pc_machine_init_sgx_epc(PCMachineState *pcms); +extern GlobalProperty pc_compat_10_1[]; +extern const size_t pc_compat_10_1_len; + extern GlobalProperty pc_compat_10_0[]; extern const size_t pc_compat_10_0_len; From fe26463d7b3e0f65de4b11377d762d5dcc03bd24 Mon Sep 17 00:00:00 2001 From: Stefan Weil Date: Wed, 6 Aug 2025 22:09:09 +0200 Subject: [PATCH 0008/2396] CI: Use mingw-w64-x86_64-curl-winssl instead of mingw-w64-x86_64-curl for Windows build mingw-w64-x86_64-curl-winssl is required for https connections. Signed-off-by: Stefan Weil Reviewed-by: Pierrick Bouvier Message-ID: <20250806200909.507803-1-sw@weilnetz.de> Signed-off-by: Thomas Huth --- .gitlab-ci.d/windows.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.d/windows.yml b/.gitlab-ci.d/windows.yml index 45ed0c96fe..beac39e5bd 100644 --- a/.gitlab-ci.d/windows.yml +++ b/.gitlab-ci.d/windows.yml @@ -77,7 +77,7 @@ msys2-64bit: git grep make sed mingw-w64-x86_64-binutils mingw-w64-x86_64-ccache - mingw-w64-x86_64-curl + mingw-w64-x86_64-curl-winssl mingw-w64-x86_64-gcc mingw-w64-x86_64-glib2 mingw-w64-x86_64-libnfs From 19ae064832dd915e14306c03b94a505abc13b873 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 26 Aug 2025 13:34:55 +0100 Subject: [PATCH 0009/2396] tests/functional/test_aarch64_virt_gpu: Skip test if EGL won't initialize MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If you are using the Nvidia drivers and have installed new versions of those packages but have not yet rebooted the host kernel, attempting to use the egl-headless display will cause QEMU to fail to start with $ qemu-system-aarch64 -M virt -display egl-headless qemu-system-aarch64: egl: eglInitialize failed: EGL_NOT_INITIALIZED qemu-system-aarch64: egl: render node init failed together with this complaint in the host kernel dmesg: [7874777.555649] NVRM: API mismatch: the client has the version 535.247.01, but NVRM: this kernel module has the version 535.230.02. Please NVRM: make sure that this kernel module and all NVIDIA driver NVRM: components have the same version. This isn't a problem with QEMU itself, so reporting this as a test failure is misleading. Instead skip the tests, as we already do for various other kinds of "host system can't actually run the EGL display" situation. Signed-off-by: Peter Maydell Message-ID: <20250826123455.2856988-1-peter.maydell@linaro.org> Reviewed-by: Manos Pitsidianakis Acked-by: Alex Bennée Acked-by: Dmitry Osipenko Signed-off-by: Thomas Huth --- tests/functional/test_aarch64_virt_gpu.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/functional/test_aarch64_virt_gpu.py b/tests/functional/test_aarch64_virt_gpu.py index 3844727857..4e50887c3e 100755 --- a/tests/functional/test_aarch64_virt_gpu.py +++ b/tests/functional/test_aarch64_virt_gpu.py @@ -76,6 +76,8 @@ class Aarch64VirtGPUMachine(LinuxKernelTest): 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") + elif "eglInitialize failed: EGL_NOT_INITIALIZED" in excp.output: + self.skipTest("EGL failed to initialize on this host") else: self.log.info("unhandled launch failure: %s", excp.output) raise excp From 36fb9796662e8d1f8626b1cacb1a6d5e35a8bd00 Mon Sep 17 00:00:00 2001 From: Gustavo Romero Date: Wed, 27 Aug 2025 00:10:08 +0000 Subject: [PATCH 0010/2396] tests/functional: Fix reverse_debugging asset precaching This commit fixes the asset precaching in the reverse_debugging test on aarch64. QemuBaseTest.main() precaches assets (kernel, rootfs, DT blobs, etc.) that are defined in variables with the ASSET_ prefix. This works because it ultimately calls Asset.precache_test(), which relies on introspection to locate these variables. If an asset variable is not named with the ASSET_ prefix, precache_test cannot find the asset and precaching silently fails. Hence, fix the asset precaching by fixing the asset variable name. Signed-off-by: Gustavo Romero Reviewed-by: Richard Henderson Reviewed-by: Manos Pitsidianakis Message-ID: <20250827001008.22112-1-gustavo.romero@linaro.org> Signed-off-by: Thomas Huth --- tests/functional/test_aarch64_reverse_debug.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/functional/test_aarch64_reverse_debug.py b/tests/functional/test_aarch64_reverse_debug.py index 58d4532835..8bc91ccfde 100755 --- a/tests/functional/test_aarch64_reverse_debug.py +++ b/tests/functional/test_aarch64_reverse_debug.py @@ -21,7 +21,7 @@ class ReverseDebugging_AArch64(ReverseDebugging): REG_PC = 32 - KERNEL_ASSET = Asset( + ASSET_KERNEL = Asset( ('https://archives.fedoraproject.org/pub/archive/fedora/linux/' 'releases/29/Everything/aarch64/os/images/pxeboot/vmlinuz'), '7e1430b81c26bdd0da025eeb8fbd77b5dc961da4364af26e771bd39f379cbbf7') @@ -30,7 +30,7 @@ class ReverseDebugging_AArch64(ReverseDebugging): def test_aarch64_virt(self): self.set_machine('virt') self.cpu = 'cortex-a53' - kernel_path = self.KERNEL_ASSET.fetch() + kernel_path = self.ASSET_KERNEL.fetch() self.reverse_debugging(args=('-kernel', kernel_path)) From 0cdabf8adbaca4b370301366faab2d01121289c7 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Thu, 21 Aug 2025 11:47:35 +0200 Subject: [PATCH 0011/2396] tests/functional: Use more fine-grained locking when looking for free ports MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, we have one lock that is held while a test is looking for free ports. However, we are also using different ranges for looking for free ports nowadays (PORTS_START is based on the PID of the process), so instead of using only one lock, we should rather use a lock per range instead. This should help to allow running more tests in parallel. While we're at it, also create the lock files without executable bit (mode is 0o777 by default). Reviewed-by: Daniel P. Berrangé Signed-off-by: Thomas Huth Message-ID: <20250821094735.804210-1-thuth@redhat.com> --- tests/functional/qemu_test/ports.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/functional/qemu_test/ports.py b/tests/functional/qemu_test/ports.py index 631b77abf6..81174a6153 100644 --- a/tests/functional/qemu_test/ports.py +++ b/tests/functional/qemu_test/ports.py @@ -23,8 +23,9 @@ class Ports(): 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) + lock_file = os.path.join(BUILD_DIR, "tests", "functional", + f".port_lock.{self.PORTS_START}") + self.lock_fh = os.open(lock_file, os.O_CREAT, mode=0o666) fcntl.flock(self.lock_fh, fcntl.LOCK_EX) return self From 9845740a162f2d9a4c852068155e94f1a3c487f9 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 19 Aug 2025 13:23:38 +0200 Subject: [PATCH 0012/2396] tests/functional: Rework the migration test to have target-specific files We are going to move the tests for each target into separate subdirectories. The migration test does not fit quite into this scheme, since it works for multiple targets, but not all. Rework the test to have a common test class, and target specific files with a target specific class, so that this will fit better into the new scheme. Reviewed-by: Pierrick Bouvier Signed-off-by: Thomas Huth Message-ID: <20250819112403.432587-2-thuth@redhat.com> --- MAINTAINERS | 2 +- tests/functional/meson.build | 22 ++++++------ .../{test_migration.py => migration.py} | 35 +++++-------------- tests/functional/test_aarch64_migration.py | 26 ++++++++++++++ tests/functional/test_alpha_migration.py | 26 ++++++++++++++ tests/functional/test_arm_migration.py | 26 ++++++++++++++ tests/functional/test_i386_migration.py | 26 ++++++++++++++ tests/functional/test_ppc64_migration.py | 26 ++++++++++++++ tests/functional/test_ppc_migration.py | 26 ++++++++++++++ tests/functional/test_riscv32_migration.py | 26 ++++++++++++++ tests/functional/test_riscv64_migration.py | 26 ++++++++++++++ tests/functional/test_sparc64_migration.py | 26 ++++++++++++++ tests/functional/test_sparc_migration.py | 26 ++++++++++++++ tests/functional/test_x86_64_migration.py | 26 ++++++++++++++ 14 files changed, 306 insertions(+), 39 deletions(-) rename tests/functional/{test_migration.py => migration.py} (74%) mode change 100755 => 100644 create mode 100755 tests/functional/test_aarch64_migration.py create mode 100755 tests/functional/test_alpha_migration.py create mode 100755 tests/functional/test_arm_migration.py create mode 100755 tests/functional/test_i386_migration.py create mode 100755 tests/functional/test_ppc64_migration.py create mode 100755 tests/functional/test_ppc_migration.py create mode 100755 tests/functional/test_riscv32_migration.py create mode 100755 tests/functional/test_riscv64_migration.py create mode 100755 tests/functional/test_sparc64_migration.py create mode 100755 tests/functional/test_sparc_migration.py create mode 100755 tests/functional/test_x86_64_migration.py diff --git a/MAINTAINERS b/MAINTAINERS index a07086ed76..56c1fe6769 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3597,7 +3597,7 @@ F: include/migration/ F: include/qemu/userfaultfd.h F: migration/ F: scripts/vmstate-static-checker.py -F: tests/functional/test_migration.py +F: tests/functional/*migration.py F: tests/vmstate-static-checker-data/ F: tests/qtest/migration/ F: tests/qtest/migration-* diff --git a/tests/functional/meson.build b/tests/functional/meson.build index 311c6f1806..c32436d99a 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -80,7 +80,7 @@ tests_generic_bsduser = [ ] tests_aarch64_system_quick = [ - 'migration', + 'aarch64_migration', ] tests_aarch64_system_thorough = [ @@ -110,7 +110,7 @@ tests_aarch64_system_thorough = [ ] tests_alpha_system_quick = [ - 'migration', + 'alpha_migration', ] tests_alpha_system_thorough = [ @@ -119,7 +119,7 @@ tests_alpha_system_thorough = [ ] tests_arm_system_quick = [ - 'migration', + 'arm_migration', ] tests_arm_system_thorough = [ @@ -168,7 +168,7 @@ tests_hppa_system_quick = [ ] tests_i386_system_quick = [ - 'migration', + 'i386_migration', ] tests_i386_system_thorough = [ @@ -228,7 +228,7 @@ tests_or1k_system_thorough = [ ] tests_ppc_system_quick = [ - 'migration', + 'ppc_migration', 'ppc_74xx', ] @@ -245,7 +245,7 @@ tests_ppc_system_thorough = [ ] tests_ppc64_system_quick = [ - 'migration', + 'ppc64_migration', ] tests_ppc64_system_thorough = [ @@ -260,7 +260,7 @@ tests_ppc64_system_thorough = [ ] tests_riscv32_system_quick = [ - 'migration', + 'riscv32_migration', 'riscv_opensbi', ] @@ -269,7 +269,7 @@ tests_riscv32_system_thorough = [ ] tests_riscv64_system_quick = [ - 'migration', + 'riscv64_migration', 'riscv_opensbi', ] @@ -300,7 +300,7 @@ tests_sh4eb_system_thorough = [ ] tests_sparc_system_quick = [ - 'migration', + 'sparc_migration', ] tests_sparc_system_thorough = [ @@ -309,7 +309,7 @@ tests_sparc_system_thorough = [ ] tests_sparc64_system_quick = [ - 'migration', + 'sparc64_migration', ] tests_sparc64_system_thorough = [ @@ -320,7 +320,7 @@ tests_sparc64_system_thorough = [ tests_x86_64_system_quick = [ 'cpu_queries', 'mem_addr_space', - 'migration', + 'x86_64_migration', 'pc_cpu_hotplug_props', 'virtio_version', 'x86_cpu_model_versions', diff --git a/tests/functional/test_migration.py b/tests/functional/migration.py old mode 100755 new mode 100644 similarity index 74% rename from tests/functional/test_migration.py rename to tests/functional/migration.py index c4393c3543..0739554483 --- a/tests/functional/test_migration.py +++ b/tests/functional/migration.py @@ -1,6 +1,6 @@ -#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0-or-later # -# Migration test +# Migration test base class # # Copyright (c) 2019 Red Hat, Inc. # @@ -14,7 +14,7 @@ import tempfile import time -from qemu_test import QemuSystemTest, skipIfMissingCommands +from qemu_test import QemuSystemTest, which from qemu_test.ports import Ports @@ -41,24 +41,7 @@ class MigrationTest(QemuSystemTest): 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() @@ -76,23 +59,21 @@ class MigrationTest(QemuSystemTest): self.skipTest('Failed to find a free port') return port - def test_migration_with_tcp_localhost(self): + def 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): + 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) - @skipIfMissingCommands('ncat') - def test_migration_with_exec(self): + def migration_with_exec(self): + if not which('ncat'): + self.skipTest('ncat is not available') 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_aarch64_migration.py b/tests/functional/test_aarch64_migration.py new file mode 100755 index 0000000000..70267e756d --- /dev/null +++ b/tests/functional/test_aarch64_migration.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python3 +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# aarch64 migration test + +from migration import MigrationTest + + +class Aarch64MigrationTest(MigrationTest): + + def test_migration_with_tcp_localhost(self): + self.set_machine('quanta-gsj') + self.migration_with_tcp_localhost() + + def test_migration_with_unix(self): + self.set_machine('quanta-gsj') + self.migration_with_unix() + + def test_migration_with_exec(self): + self.set_machine('quanta-gsj') + self.migration_with_exec() + + +if __name__ == '__main__': + MigrationTest.main() diff --git a/tests/functional/test_alpha_migration.py b/tests/functional/test_alpha_migration.py new file mode 100755 index 0000000000..f11b523ec9 --- /dev/null +++ b/tests/functional/test_alpha_migration.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python3 +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Alpha migration test + +from migration import MigrationTest + + +class AlphaMigrationTest(MigrationTest): + + def test_migration_with_tcp_localhost(self): + self.set_machine('clipper') + self.migration_with_tcp_localhost() + + def test_migration_with_unix(self): + self.set_machine('clipper') + self.migration_with_unix() + + def test_migration_with_exec(self): + self.set_machine('clipper') + self.migration_with_exec() + + +if __name__ == '__main__': + MigrationTest.main() diff --git a/tests/functional/test_arm_migration.py b/tests/functional/test_arm_migration.py new file mode 100755 index 0000000000..0aa89f4f61 --- /dev/null +++ b/tests/functional/test_arm_migration.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python3 +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# arm migration test + +from migration import MigrationTest + + +class ArmMigrationTest(MigrationTest): + + def test_migration_with_tcp_localhost(self): + self.set_machine('npcm750-evb') + self.migration_with_tcp_localhost() + + def test_migration_with_unix(self): + self.set_machine('npcm750-evb') + self.migration_with_unix() + + def test_migration_with_exec(self): + self.set_machine('npcm750-evb') + self.migration_with_exec() + + +if __name__ == '__main__': + MigrationTest.main() diff --git a/tests/functional/test_i386_migration.py b/tests/functional/test_i386_migration.py new file mode 100755 index 0000000000..a57f316404 --- /dev/null +++ b/tests/functional/test_i386_migration.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python3 +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# i386 migration test + +from migration import MigrationTest + + +class I386MigrationTest(MigrationTest): + + def test_migration_with_tcp_localhost(self): + self.set_machine('isapc') + self.migration_with_tcp_localhost() + + def test_migration_with_unix(self): + self.set_machine('isapc') + self.migration_with_unix() + + def test_migration_with_exec(self): + self.set_machine('isapc') + self.migration_with_exec() + + +if __name__ == '__main__': + MigrationTest.main() diff --git a/tests/functional/test_ppc64_migration.py b/tests/functional/test_ppc64_migration.py new file mode 100755 index 0000000000..5dfdaaf709 --- /dev/null +++ b/tests/functional/test_ppc64_migration.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python3 +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# ppc migration test + +from migration import MigrationTest + + +class PpcMigrationTest(MigrationTest): + + def test_migration_with_tcp_localhost(self): + self.set_machine('mac99') + self.migration_with_tcp_localhost() + + def test_migration_with_unix(self): + self.set_machine('mac99') + self.migration_with_unix() + + def test_migration_with_exec(self): + self.set_machine('mac99') + self.migration_with_exec() + + +if __name__ == '__main__': + MigrationTest.main() diff --git a/tests/functional/test_ppc_migration.py b/tests/functional/test_ppc_migration.py new file mode 100755 index 0000000000..a8692826d3 --- /dev/null +++ b/tests/functional/test_ppc_migration.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python3 +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# ppc migration test + +from migration import MigrationTest + + +class PpcMigrationTest(MigrationTest): + + def test_migration_with_tcp_localhost(self): + self.set_machine('sam460ex') + self.migration_with_tcp_localhost() + + def test_migration_with_unix(self): + self.set_machine('sam460ex') + self.migration_with_unix() + + def test_migration_with_exec(self): + self.set_machine('sam460ex') + self.migration_with_exec() + + +if __name__ == '__main__': + MigrationTest.main() diff --git a/tests/functional/test_riscv32_migration.py b/tests/functional/test_riscv32_migration.py new file mode 100755 index 0000000000..30acbbe69f --- /dev/null +++ b/tests/functional/test_riscv32_migration.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python3 +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# riscv32 migration test + +from migration import MigrationTest + + +class Rv32MigrationTest(MigrationTest): + + def test_migration_with_tcp_localhost(self): + self.set_machine('spike') + self.migration_with_tcp_localhost() + + def test_migration_with_unix(self): + self.set_machine('virt') + self.migration_with_unix() + + def test_migration_with_exec(self): + self.set_machine('spike') + self.migration_with_exec() + + +if __name__ == '__main__': + MigrationTest.main() diff --git a/tests/functional/test_riscv64_migration.py b/tests/functional/test_riscv64_migration.py new file mode 100755 index 0000000000..2d613a29ec --- /dev/null +++ b/tests/functional/test_riscv64_migration.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python3 +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# riscv64 migration test + +from migration import MigrationTest + + +class Rv64MigrationTest(MigrationTest): + + def test_migration_with_tcp_localhost(self): + self.set_machine('virt') + self.migration_with_tcp_localhost() + + def test_migration_with_unix(self): + self.set_machine('spike') + self.migration_with_unix() + + def test_migration_with_exec(self): + self.set_machine('virt') + self.migration_with_exec() + + +if __name__ == '__main__': + MigrationTest.main() diff --git a/tests/functional/test_sparc64_migration.py b/tests/functional/test_sparc64_migration.py new file mode 100755 index 0000000000..a8a6c73c35 --- /dev/null +++ b/tests/functional/test_sparc64_migration.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python3 +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Sparc64 migration test + +from migration import MigrationTest + + +class Sparc64MigrationTest(MigrationTest): + + def test_migration_with_tcp_localhost(self): + self.set_machine('sun4u') + self.migration_with_tcp_localhost() + + def test_migration_with_unix(self): + self.set_machine('sun4u') + self.migration_with_unix() + + def test_migration_with_exec(self): + self.set_machine('sun4u') + self.migration_with_exec() + + +if __name__ == '__main__': + MigrationTest.main() diff --git a/tests/functional/test_sparc_migration.py b/tests/functional/test_sparc_migration.py new file mode 100755 index 0000000000..dd6d5783b1 --- /dev/null +++ b/tests/functional/test_sparc_migration.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python3 +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Sparc migration test + +from migration import MigrationTest + + +class SparcMigrationTest(MigrationTest): + + def test_migration_with_tcp_localhost(self): + self.set_machine('SS-4') + self.migration_with_tcp_localhost() + + def test_migration_with_unix(self): + self.set_machine('SS-5') + self.migration_with_unix() + + def test_migration_with_exec(self): + self.set_machine('SS-4') + self.migration_with_exec() + + +if __name__ == '__main__': + MigrationTest.main() diff --git a/tests/functional/test_x86_64_migration.py b/tests/functional/test_x86_64_migration.py new file mode 100755 index 0000000000..f3a517ae1f --- /dev/null +++ b/tests/functional/test_x86_64_migration.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python3 +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# x86_64 migration test + +from migration import MigrationTest + + +class X8664MigrationTest(MigrationTest): + + def test_migration_with_tcp_localhost(self): + self.set_machine('microvm') + self.migration_with_tcp_localhost() + + def test_migration_with_unix(self): + self.set_machine('microvm') + self.migration_with_unix() + + def test_migration_with_exec(self): + self.set_machine('microvm') + self.migration_with_exec() + + +if __name__ == '__main__': + MigrationTest.main() From b19c560e4ac730799f45730020fae63e4727a8e0 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 19 Aug 2025 13:23:39 +0200 Subject: [PATCH 0013/2396] tests/functional: Rework the multiprocess test to have target-specific files We are going to move the tests for each target into separate subdirectories. The multiprocess test currently contains code for both, x86 and aarch64, so it does not quite fit into this scheme. Rework the test to have a common test class, and target specific files with a target specific class, so that this will fit better into the new scheme. Reviewed-by: Pierrick Bouvier Signed-off-by: Thomas Huth Message-ID: <20250819112403.432587-3-thuth@redhat.com> --- MAINTAINERS | 2 +- tests/functional/meson.build | 4 +- .../{test_multiprocess.py => multiprocess.py} | 40 +------------------ tests/functional/test_aarch64_multiprocess.py | 31 ++++++++++++++ tests/functional/test_x86_64_multiprocess.py | 31 ++++++++++++++ 5 files changed, 67 insertions(+), 41 deletions(-) rename tests/functional/{test_multiprocess.py => multiprocess.py} (58%) mode change 100755 => 100644 create mode 100755 tests/functional/test_aarch64_multiprocess.py create mode 100755 tests/functional/test_x86_64_multiprocess.py diff --git a/MAINTAINERS b/MAINTAINERS index 56c1fe6769..adbed9df2f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4286,7 +4286,7 @@ F: hw/remote/vfio-user-obj.c F: include/hw/remote/vfio-user-obj.h F: hw/remote/iommu.c F: include/hw/remote/iommu.h -F: tests/functional/test_multiprocess.py +F: tests/functional/*multiprocess.py VFIO-USER: M: John Levon diff --git a/tests/functional/meson.build b/tests/functional/meson.build index c32436d99a..38ae0d6cd3 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -90,6 +90,7 @@ tests_aarch64_system_thorough = [ 'aarch64_hotplug_pci', 'aarch64_imx8mp_evk', 'aarch64_kvm', + 'aarch64_multiprocess', 'aarch64_raspi3', 'aarch64_raspi4', 'aarch64_replay', @@ -106,7 +107,6 @@ tests_aarch64_system_thorough = [ 'aarch64_virt_gpu', 'aarch64_xen', 'aarch64_xlnx_versal', - 'multiprocess', ] tests_alpha_system_quick = [ @@ -332,7 +332,7 @@ tests_x86_64_system_thorough = [ 'acpi_bits', 'intel_iommu', 'linux_initrd', - 'multiprocess', + 'x86_64_multiprocess', 'netdev_ethtool', 'virtio_balloon', 'virtio_gpu', diff --git a/tests/functional/test_multiprocess.py b/tests/functional/multiprocess.py old mode 100755 new mode 100644 similarity index 58% rename from tests/functional/test_multiprocess.py rename to tests/functional/multiprocess.py index 92d5207b0e..6a06c1eda1 --- a/tests/functional/test_multiprocess.py +++ b/tests/functional/multiprocess.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0-or-later # # Test for multiprocess qemu # @@ -9,33 +9,13 @@ import os import socket -from qemu_test import QemuSystemTest, Asset, wait_for_console_pattern +from qemu_test import QemuSystemTest, 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""" @@ -85,19 +65,3 @@ class Multiprocess(QemuSystemTest): proxy_sock.close() remote_sock.close() - - 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_aarch64_multiprocess.py b/tests/functional/test_aarch64_multiprocess.py new file mode 100755 index 0000000000..1c6e45ecb6 --- /dev/null +++ b/tests/functional/test_aarch64_multiprocess.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python3 +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Test for multiprocess qemu on aarch64 + +from multiprocess import Multiprocess +from qemu_test import Asset + + +class Aarch64Multiprocess(Multiprocess): + + 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 test_multiprocess(self): + kernel_command_line = (self.KERNEL_COMMON_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') + + +if __name__ == '__main__': + Multiprocess.main() diff --git a/tests/functional/test_x86_64_multiprocess.py b/tests/functional/test_x86_64_multiprocess.py new file mode 100755 index 0000000000..756629dd44 --- /dev/null +++ b/tests/functional/test_x86_64_multiprocess.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python3 +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Test for multiprocess qemu on x86 + +from multiprocess import Multiprocess +from qemu_test import Asset + + +class X86Multiprocess(Multiprocess): + + 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') + + def test_multiprocess(self): + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'console=ttyS0 rdinit=/bin/bash') + self.do_test(self.ASSET_KERNEL_X86, self.ASSET_INITRD_X86, + kernel_command_line, 'pc') + + +if __name__ == '__main__': + Multiprocess.main() From b8103f56d97c2e6554af2c365b366989005a666c Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 19 Aug 2025 13:23:40 +0200 Subject: [PATCH 0014/2396] tests/functional/meson.build: Split timeout settings by target We are going to move these settings into target-specific subfolders. As a first step, split the big test_timeouts array up into individual ones. Reviewed-by: Pierrick Bouvier Signed-off-by: Thomas Huth Message-ID: <20250819112403.432587-4-thuth@redhat.com> --- tests/functional/meson.build | 50 +++++++++++++++++++++++++++++++----- 1 file changed, 44 insertions(+), 6 deletions(-) diff --git a/tests/functional/meson.build b/tests/functional/meson.build index 38ae0d6cd3..356aad12de 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -10,7 +10,7 @@ if get_option('tcg_interpreter') endif # Timeouts for individual tests that can be slow e.g. with debugging enabled -test_timeouts = { +test_aarch64_timeouts = { 'aarch64_aspeed_ast2700' : 600, 'aarch64_aspeed_ast2700fc' : 600, 'aarch64_device_passthrough' : 720, @@ -25,7 +25,9 @@ test_timeouts = { 'aarch64_tuxrun' : 240, 'aarch64_virt' : 360, 'aarch64_virt_gpu' : 480, - 'acpi_bits' : 420, +} + +test_arm_timeouts = { 'arm_aspeed_palmetto' : 120, 'arm_aspeed_romulus' : 120, 'arm_aspeed_witherspoon' : 120, @@ -44,24 +46,55 @@ test_timeouts = { 'arm_replay' : 240, 'arm_tuxrun' : 240, 'arm_sx1' : 360, - 'intel_iommu': 300, +} + +test_mips_timeouts = { 'mips_malta' : 480, +} + +test_mipsel_timeouts = { 'mipsel_malta' : 420, 'mipsel_replay' : 480, +} + +test_mips64_timeouts = { 'mips64_malta' : 240, +} + +test_mips64el_timeouts = { 'mips64el_malta' : 420, 'mips64el_replay' : 180, - 'netdev_ethtool' : 180, +} + +test_ppc_timeouts = { 'ppc_40p' : 240, +} + +test_ppc64_timeouts = { 'ppc64_hv' : 1000, 'ppc64_powernv' : 480, 'ppc64_pseries' : 480, 'ppc64_replay' : 210, 'ppc64_tuxrun' : 420, 'ppc64_mac99' : 120, +} + +test_riscv64_timeouts = { 'riscv64_tuxrun' : 120, +} + +test_s390x_timeouts = { 's390x_ccw_virtio' : 420, +} + +test_sh4_timeouts = { 'sh4_tuxrun' : 240, +} + +test_x86_64_timeouts = { + 'acpi_bits' : 420, + 'intel_iommu': 300, + 'netdev_ethtool' : 180, 'virtio_balloon': 120, 'x86_64_kvm_xen' : 180, 'x86_64_replay' : 480, @@ -404,6 +437,11 @@ foreach speed : ['quick', 'thorough'] build_by_default: false, env: test_precache_env) precache_all += precache + if is_variable('test_' + target_base + '_timeouts') + time_out = get_variable('test_' + target_base + '_timeouts').get(test, 90) + else + time_out = 90 + endif # Ideally we would add 'precache' to 'depends' here, such that # 'build_by_default: false' lets the pre-caching automatically @@ -419,8 +457,8 @@ foreach speed : ['quick', 'thorough'] env: test_env, args: [testpath], protocol: 'tap', - timeout: test_timeouts.get(test, 90), - priority: test_timeouts.get(test, 90), + timeout: time_out, + priority: time_out, suite: suites) endforeach endforeach From 9c9efb1e36ddb2e636c7f6aafda49cddaee88a0c Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 19 Aug 2025 13:23:41 +0200 Subject: [PATCH 0015/2396] tests/functional/meson.build: Allow tests to reside in subfolders We are going to move target-specific tests to subfolders that are named after the target (and generic tests will be put into a "generic" folder), so prepare the meson.build file to allow such locations, too. Reviewed-by: Pierrick Bouvier Signed-off-by: Thomas Huth Message-ID: <20250819112403.432587-5-thuth@redhat.com> --- tests/functional/meson.build | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tests/functional/meson.build b/tests/functional/meson.build index 356aad12de..8c24ac1cc2 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -423,7 +423,13 @@ foreach speed : ['quick', 'thorough'] foreach test : target_tests testname = '@0@-@1@'.format(target_base, test) - testfile = 'test_' + test + '.py' + if fs.exists('test_' + test + '.py') + testfile = 'test_' + test + '.py' + elif fs.exists('generic' / 'test_' + test + '.py') + testfile = 'generic' / 'test_' + test + '.py' + else + testfile = target_base / 'test_' + test + '.py' + endif testpath = meson.current_source_dir() / testfile teststamp = testname + '.tstamp' test_precache_env = environment() From 96ced85b0c5664426518ad43f0e423092fbee933 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 19 Aug 2025 13:23:42 +0200 Subject: [PATCH 0016/2396] tests/functional: Move aarch64 tests into architecture specific folder The tests/functional folder has become quite crowded already, some restructuring would be helpful here. Thus move the aarch64 tests into a target-specific subfolder. Reviewed-by: Pierrick Bouvier Signed-off-by: Thomas Huth Message-ID: <20250819112403.432587-6-thuth@redhat.com> --- MAINTAINERS | 24 +++++----- tests/functional/aarch64/meson.build | 48 +++++++++++++++++++ .../test_aspeed_ast2700.py} | 0 .../test_aspeed_ast2700fc.py} | 0 .../test_device_passthrough.py} | 0 .../test_hotplug_pci.py} | 0 .../test_imx8mp_evk.py} | 0 .../test_kvm.py} | 0 .../test_migration.py} | 0 .../test_multiprocess.py} | 0 .../test_raspi3.py} | 0 .../test_raspi4.py} | 0 .../test_replay.py} | 0 .../test_reverse_debug.py} | 0 .../test_rme_sbsaref.py} | 2 +- .../test_rme_virt.py} | 0 .../test_sbsaref.py} | 0 .../test_sbsaref_alpine.py} | 2 +- .../test_sbsaref_freebsd.py} | 2 +- .../test_smmu.py} | 0 .../test_tcg_plugins.py} | 0 .../test_tuxrun.py} | 0 .../test_virt.py} | 0 .../test_virt_gpu.py} | 0 .../test_xen.py} | 0 .../test_xlnx_versal.py} | 0 tests/functional/meson.build | 48 +------------------ 27 files changed, 64 insertions(+), 62 deletions(-) create mode 100644 tests/functional/aarch64/meson.build rename tests/functional/{test_aarch64_aspeed_ast2700.py => aarch64/test_aspeed_ast2700.py} (100%) rename tests/functional/{test_aarch64_aspeed_ast2700fc.py => aarch64/test_aspeed_ast2700fc.py} (100%) rename tests/functional/{test_aarch64_device_passthrough.py => aarch64/test_device_passthrough.py} (100%) rename tests/functional/{test_aarch64_hotplug_pci.py => aarch64/test_hotplug_pci.py} (100%) rename tests/functional/{test_aarch64_imx8mp_evk.py => aarch64/test_imx8mp_evk.py} (100%) rename tests/functional/{test_aarch64_kvm.py => aarch64/test_kvm.py} (100%) rename tests/functional/{test_aarch64_migration.py => aarch64/test_migration.py} (100%) rename tests/functional/{test_aarch64_multiprocess.py => aarch64/test_multiprocess.py} (100%) rename tests/functional/{test_aarch64_raspi3.py => aarch64/test_raspi3.py} (100%) rename tests/functional/{test_aarch64_raspi4.py => aarch64/test_raspi4.py} (100%) rename tests/functional/{test_aarch64_replay.py => aarch64/test_replay.py} (100%) rename tests/functional/{test_aarch64_reverse_debug.py => aarch64/test_reverse_debug.py} (100%) rename tests/functional/{test_aarch64_rme_sbsaref.py => aarch64/test_rme_sbsaref.py} (98%) rename tests/functional/{test_aarch64_rme_virt.py => aarch64/test_rme_virt.py} (100%) rename tests/functional/{test_aarch64_sbsaref.py => aarch64/test_sbsaref.py} (100%) rename tests/functional/{test_aarch64_sbsaref_alpine.py => aarch64/test_sbsaref_alpine.py} (97%) rename tests/functional/{test_aarch64_sbsaref_freebsd.py => aarch64/test_sbsaref_freebsd.py} (97%) rename tests/functional/{test_aarch64_smmu.py => aarch64/test_smmu.py} (100%) rename tests/functional/{test_aarch64_tcg_plugins.py => aarch64/test_tcg_plugins.py} (100%) rename tests/functional/{test_aarch64_tuxrun.py => aarch64/test_tuxrun.py} (100%) rename tests/functional/{test_aarch64_virt.py => aarch64/test_virt.py} (100%) rename tests/functional/{test_aarch64_virt_gpu.py => aarch64/test_virt_gpu.py} (100%) rename tests/functional/{test_aarch64_xen.py => aarch64/test_xen.py} (100%) rename tests/functional/{test_aarch64_xlnx_versal.py => aarch64/test_xlnx_versal.py} (100%) diff --git a/MAINTAINERS b/MAINTAINERS index adbed9df2f..a2a5ccea7b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -212,7 +212,7 @@ L: qemu-arm@nongnu.org S: Maintained F: hw/arm/smmu* F: include/hw/arm/smmu* -F: tests/functional/test_aarch64_smmu.py +F: tests/functional/aarch64/test_smmu.py AVR TCG CPUs M: Michael Rolnik @@ -874,7 +874,7 @@ F: include/hw/arm/fsl-imx8mp.h F: include/hw/misc/imx8mp_*.h F: include/hw/pci-host/fsl_imx8m_phy.h F: docs/system/arm/imx8mp-evk.rst -F: tests/functional/test_aarch64_imx8mp_evk.py +F: tests/functional/aarch64/test_imx8mp_evk.py F: tests/qtest/rs5c372-test.c MPS2 / MPS3 @@ -952,8 +952,7 @@ F: include/hw/arm/rasp* F: include/hw/*/bcm283* F: docs/system/arm/raspi.rst F: tests/functional/test_arm_raspi2.py -F: tests/functional/test_aarch64_raspi3.py -F: tests/functional/test_aarch64_raspi4.py +F: tests/functional/aarch64/test_raspi*.py Real View M: Peter Maydell @@ -993,7 +992,7 @@ F: hw/misc/sbsa_ec.c F: hw/watchdog/sbsa_gwdt.c F: include/hw/watchdog/sbsa_gwdt.h F: docs/system/arm/sbsa.rst -F: tests/functional/test_aarch64_*sbsaref*.py +F: tests/functional/aarch64/test_*sbsaref*.py Sharp SL-5500 (Collie) PDA M: Peter Maydell @@ -1063,8 +1062,8 @@ S: Maintained F: hw/arm/virt* F: include/hw/arm/virt.h F: docs/system/arm/virt.rst -F: tests/functional/test_aarch64_*virt*.py -F: tests/functional/test_aarch64_tuxrun.py +F: tests/functional/aarch64/test_*virt*.py +F: tests/functional/aarch64/test_tuxrun.py F: tests/functional/test_arm_tuxrun.py F: tests/functional/test_arm_virt.py @@ -1096,7 +1095,7 @@ F: hw/display/dpcd.c F: include/hw/display/dpcd.h F: docs/system/arm/xlnx-versal-virt.rst F: docs/system/arm/xlnx-zcu102.rst -F: tests/functional/test_aarch64_xlnx_versal.py +F: tests/functional/aarch64/test_xlnx_versal.py Xilinx Versal OSPI M: Francisco Iglesias @@ -2109,7 +2108,7 @@ ARM PCI Hotplug M: Gustavo Romero L: qemu-arm@nongnu.org S: Supported -F: tests/functional/test_aarch64_hotplug_pci.py +F: tests/functional/aarch64/test_hotplug_pci.py ACPI/SMBIOS M: Michael S. Tsirkin @@ -2263,6 +2262,7 @@ F: util/vfio-helpers.c F: include/hw/vfio/ F: docs/devel/migration/vfio.rst F: qapi/vfio.json +F: tests/functional/aarch64/test_device_passthrough.py vfio-igd M: Alex Williamson @@ -2638,7 +2638,7 @@ M: Alex Bennée S: Maintained F: hw/core/guest-loader.c F: docs/system/guest-loader.rst -F: tests/functional/test_aarch64_xen.py +F: tests/functional/aarch64/test_xen.py Intel Hexadecimal Object File Loader M: Su Hang @@ -2707,7 +2707,7 @@ F: hw/display/virtio-gpu* F: hw/display/virtio-vga.* F: include/hw/virtio/virtio-gpu.h F: docs/system/devices/virtio-gpu.rst -F: tests/functional/test_aarch64_virt_gpu.py +F: tests/functional/aarch64/test_virt_gpu.py vhost-user-blk M: Raphael Norwitz @@ -3933,7 +3933,7 @@ S: Maintained F: docs/devel/tcg-plugins.rst F: plugins/ F: tests/tcg/plugins/ -F: tests/functional/test_aarch64_tcg_plugins.py +F: tests/functional/aarch64/test_tcg_plugins.py F: contrib/plugins/ F: scripts/qemu-plugin-symbols.py diff --git a/tests/functional/aarch64/meson.build b/tests/functional/aarch64/meson.build new file mode 100644 index 0000000000..04846c6eb1 --- /dev/null +++ b/tests/functional/aarch64/meson.build @@ -0,0 +1,48 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +test_aarch64_timeouts = { + 'aspeed_ast2700' : 600, + 'aspeed_ast2700fc' : 600, + 'device_passthrough' : 720, + 'imx8mp_evk' : 240, + 'raspi4' : 480, + 'reverse_debug' : 180, + 'rme_virt' : 1200, + 'rme_sbsaref' : 1200, + 'sbsaref_alpine' : 1200, + 'sbsaref_freebsd' : 720, + 'smmu' : 720, + 'tuxrun' : 240, + 'virt' : 360, + 'virt_gpu' : 480, +} + +tests_aarch64_system_quick = [ + 'migration', +] + +tests_aarch64_system_thorough = [ + 'aspeed_ast2700', + 'aspeed_ast2700fc', + 'device_passthrough', + 'hotplug_pci', + 'imx8mp_evk', + 'kvm', + 'multiprocess', + 'raspi3', + 'raspi4', + 'replay', + 'reverse_debug', + 'rme_virt', + 'rme_sbsaref', + 'sbsaref', + 'sbsaref_alpine', + 'sbsaref_freebsd', + 'smmu', + 'tcg_plugins', + 'tuxrun', + 'virt', + 'virt_gpu', + 'xen', + 'xlnx_versal', +] diff --git a/tests/functional/test_aarch64_aspeed_ast2700.py b/tests/functional/aarch64/test_aspeed_ast2700.py similarity index 100% rename from tests/functional/test_aarch64_aspeed_ast2700.py rename to tests/functional/aarch64/test_aspeed_ast2700.py diff --git a/tests/functional/test_aarch64_aspeed_ast2700fc.py b/tests/functional/aarch64/test_aspeed_ast2700fc.py similarity index 100% rename from tests/functional/test_aarch64_aspeed_ast2700fc.py rename to tests/functional/aarch64/test_aspeed_ast2700fc.py diff --git a/tests/functional/test_aarch64_device_passthrough.py b/tests/functional/aarch64/test_device_passthrough.py similarity index 100% rename from tests/functional/test_aarch64_device_passthrough.py rename to tests/functional/aarch64/test_device_passthrough.py diff --git a/tests/functional/test_aarch64_hotplug_pci.py b/tests/functional/aarch64/test_hotplug_pci.py similarity index 100% rename from tests/functional/test_aarch64_hotplug_pci.py rename to tests/functional/aarch64/test_hotplug_pci.py diff --git a/tests/functional/test_aarch64_imx8mp_evk.py b/tests/functional/aarch64/test_imx8mp_evk.py similarity index 100% rename from tests/functional/test_aarch64_imx8mp_evk.py rename to tests/functional/aarch64/test_imx8mp_evk.py diff --git a/tests/functional/test_aarch64_kvm.py b/tests/functional/aarch64/test_kvm.py similarity index 100% rename from tests/functional/test_aarch64_kvm.py rename to tests/functional/aarch64/test_kvm.py diff --git a/tests/functional/test_aarch64_migration.py b/tests/functional/aarch64/test_migration.py similarity index 100% rename from tests/functional/test_aarch64_migration.py rename to tests/functional/aarch64/test_migration.py diff --git a/tests/functional/test_aarch64_multiprocess.py b/tests/functional/aarch64/test_multiprocess.py similarity index 100% rename from tests/functional/test_aarch64_multiprocess.py rename to tests/functional/aarch64/test_multiprocess.py diff --git a/tests/functional/test_aarch64_raspi3.py b/tests/functional/aarch64/test_raspi3.py similarity index 100% rename from tests/functional/test_aarch64_raspi3.py rename to tests/functional/aarch64/test_raspi3.py diff --git a/tests/functional/test_aarch64_raspi4.py b/tests/functional/aarch64/test_raspi4.py similarity index 100% rename from tests/functional/test_aarch64_raspi4.py rename to tests/functional/aarch64/test_raspi4.py diff --git a/tests/functional/test_aarch64_replay.py b/tests/functional/aarch64/test_replay.py similarity index 100% rename from tests/functional/test_aarch64_replay.py rename to tests/functional/aarch64/test_replay.py diff --git a/tests/functional/test_aarch64_reverse_debug.py b/tests/functional/aarch64/test_reverse_debug.py similarity index 100% rename from tests/functional/test_aarch64_reverse_debug.py rename to tests/functional/aarch64/test_reverse_debug.py diff --git a/tests/functional/test_aarch64_rme_sbsaref.py b/tests/functional/aarch64/test_rme_sbsaref.py similarity index 98% rename from tests/functional/test_aarch64_rme_sbsaref.py rename to tests/functional/aarch64/test_rme_sbsaref.py index 746770e776..100f1c7738 100755 --- a/tests/functional/test_aarch64_rme_sbsaref.py +++ b/tests/functional/aarch64/test_rme_sbsaref.py @@ -13,7 +13,7 @@ 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 +from test_rme_virt import test_realms_guest class Aarch64RMESbsaRefMachine(QemuSystemTest): diff --git a/tests/functional/test_aarch64_rme_virt.py b/tests/functional/aarch64/test_rme_virt.py similarity index 100% rename from tests/functional/test_aarch64_rme_virt.py rename to tests/functional/aarch64/test_rme_virt.py diff --git a/tests/functional/test_aarch64_sbsaref.py b/tests/functional/aarch64/test_sbsaref.py similarity index 100% rename from tests/functional/test_aarch64_sbsaref.py rename to tests/functional/aarch64/test_sbsaref.py diff --git a/tests/functional/test_aarch64_sbsaref_alpine.py b/tests/functional/aarch64/test_sbsaref_alpine.py similarity index 97% rename from tests/functional/test_aarch64_sbsaref_alpine.py rename to tests/functional/aarch64/test_sbsaref_alpine.py index 8776999383..abb8f5114b 100755 --- a/tests/functional/test_aarch64_sbsaref_alpine.py +++ b/tests/functional/aarch64/test_sbsaref_alpine.py @@ -12,7 +12,7 @@ from qemu_test import QemuSystemTest, Asset, skipSlowTest from qemu_test import wait_for_console_pattern -from test_aarch64_sbsaref import fetch_firmware +from test_sbsaref import fetch_firmware class Aarch64SbsarefAlpine(QemuSystemTest): diff --git a/tests/functional/test_aarch64_sbsaref_freebsd.py b/tests/functional/aarch64/test_sbsaref_freebsd.py similarity index 97% rename from tests/functional/test_aarch64_sbsaref_freebsd.py rename to tests/functional/aarch64/test_sbsaref_freebsd.py index 7ef016fba6..3b942f7795 100755 --- a/tests/functional/test_aarch64_sbsaref_freebsd.py +++ b/tests/functional/aarch64/test_sbsaref_freebsd.py @@ -12,7 +12,7 @@ from qemu_test import QemuSystemTest, Asset, skipSlowTest from qemu_test import wait_for_console_pattern -from test_aarch64_sbsaref import fetch_firmware +from test_sbsaref import fetch_firmware class Aarch64SbsarefFreeBSD(QemuSystemTest): diff --git a/tests/functional/test_aarch64_smmu.py b/tests/functional/aarch64/test_smmu.py similarity index 100% rename from tests/functional/test_aarch64_smmu.py rename to tests/functional/aarch64/test_smmu.py diff --git a/tests/functional/test_aarch64_tcg_plugins.py b/tests/functional/aarch64/test_tcg_plugins.py similarity index 100% rename from tests/functional/test_aarch64_tcg_plugins.py rename to tests/functional/aarch64/test_tcg_plugins.py diff --git a/tests/functional/test_aarch64_tuxrun.py b/tests/functional/aarch64/test_tuxrun.py similarity index 100% rename from tests/functional/test_aarch64_tuxrun.py rename to tests/functional/aarch64/test_tuxrun.py diff --git a/tests/functional/test_aarch64_virt.py b/tests/functional/aarch64/test_virt.py similarity index 100% rename from tests/functional/test_aarch64_virt.py rename to tests/functional/aarch64/test_virt.py diff --git a/tests/functional/test_aarch64_virt_gpu.py b/tests/functional/aarch64/test_virt_gpu.py similarity index 100% rename from tests/functional/test_aarch64_virt_gpu.py rename to tests/functional/aarch64/test_virt_gpu.py diff --git a/tests/functional/test_aarch64_xen.py b/tests/functional/aarch64/test_xen.py similarity index 100% rename from tests/functional/test_aarch64_xen.py rename to tests/functional/aarch64/test_xen.py diff --git a/tests/functional/test_aarch64_xlnx_versal.py b/tests/functional/aarch64/test_xlnx_versal.py similarity index 100% rename from tests/functional/test_aarch64_xlnx_versal.py rename to tests/functional/aarch64/test_xlnx_versal.py diff --git a/tests/functional/meson.build b/tests/functional/meson.build index 8c24ac1cc2..9cb6325360 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -9,23 +9,7 @@ if get_option('tcg_interpreter') subdir_done() endif -# Timeouts for individual tests that can be slow e.g. with debugging enabled -test_aarch64_timeouts = { - '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, - 'aarch64_virt_gpu' : 480, -} +subdir('aarch64') test_arm_timeouts = { 'arm_aspeed_palmetto' : 120, @@ -112,36 +96,6 @@ tests_generic_linuxuser = [ tests_generic_bsduser = [ ] -tests_aarch64_system_quick = [ - 'aarch64_migration', -] - -tests_aarch64_system_thorough = [ - 'aarch64_aspeed_ast2700', - 'aarch64_aspeed_ast2700fc', - 'aarch64_device_passthrough', - 'aarch64_hotplug_pci', - 'aarch64_imx8mp_evk', - 'aarch64_kvm', - 'aarch64_multiprocess', - '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', -] - tests_alpha_system_quick = [ 'alpha_migration', ] From e534eee5ba6aefd905c16bcb714d1ff8156a0c45 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 19 Aug 2025 13:23:43 +0200 Subject: [PATCH 0017/2396] tests/functional: Move alpha tests into architecture specific folder MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The tests/functional folder has become quite crowded already, some restructuring would be helpful here. Thus move the alpha tests into a target-specific subfolder. Reviewed-by: Pierrick Bouvier Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Thomas Huth Message-ID: <20250819112403.432587-7-thuth@redhat.com> --- MAINTAINERS | 3 ++- tests/functional/alpha/meson.build | 10 ++++++++++ .../{test_alpha_clipper.py => alpha/test_clipper.py} | 0 .../test_migration.py} | 0 .../{test_alpha_replay.py => alpha/test_replay.py} | 0 tests/functional/meson.build | 10 +--------- 6 files changed, 13 insertions(+), 10 deletions(-) create mode 100644 tests/functional/alpha/meson.build rename tests/functional/{test_alpha_clipper.py => alpha/test_clipper.py} (100%) rename tests/functional/{test_alpha_migration.py => alpha/test_migration.py} (100%) rename tests/functional/{test_alpha_replay.py => alpha/test_replay.py} (100%) diff --git a/MAINTAINERS b/MAINTAINERS index a2a5ccea7b..8115aae618 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -189,6 +189,7 @@ M: Richard Henderson S: Maintained F: target/alpha/ F: tests/tcg/alpha/ +F: tests/functional/alpha/ F: disas/alpha.c ARM TCG CPUs @@ -656,7 +657,7 @@ S: Maintained F: hw/alpha/ F: hw/isa/smc37c669-superio.c F: tests/tcg/alpha/system/ -F: tests/functional/test_alpha_clipper.py +F: tests/functional/alpha/test_clipper.py ARM Machines ------------ diff --git a/tests/functional/alpha/meson.build b/tests/functional/alpha/meson.build new file mode 100644 index 0000000000..26a5b3f2e4 --- /dev/null +++ b/tests/functional/alpha/meson.build @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +tests_alpha_system_quick = [ + 'migration', +] + +tests_alpha_system_thorough = [ + 'clipper', + 'replay', +] diff --git a/tests/functional/test_alpha_clipper.py b/tests/functional/alpha/test_clipper.py similarity index 100% rename from tests/functional/test_alpha_clipper.py rename to tests/functional/alpha/test_clipper.py diff --git a/tests/functional/test_alpha_migration.py b/tests/functional/alpha/test_migration.py similarity index 100% rename from tests/functional/test_alpha_migration.py rename to tests/functional/alpha/test_migration.py diff --git a/tests/functional/test_alpha_replay.py b/tests/functional/alpha/test_replay.py similarity index 100% rename from tests/functional/test_alpha_replay.py rename to tests/functional/alpha/test_replay.py diff --git a/tests/functional/meson.build b/tests/functional/meson.build index 9cb6325360..a7f8c88a07 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -10,6 +10,7 @@ if get_option('tcg_interpreter') endif subdir('aarch64') +subdir('alpha') test_arm_timeouts = { 'arm_aspeed_palmetto' : 120, @@ -96,15 +97,6 @@ tests_generic_linuxuser = [ tests_generic_bsduser = [ ] -tests_alpha_system_quick = [ - 'alpha_migration', -] - -tests_alpha_system_thorough = [ - 'alpha_clipper', - 'alpha_replay', -] - tests_arm_system_quick = [ 'arm_migration', ] From 8de42cb748287d8dcc521d057b107f32d98e5edd Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 19 Aug 2025 13:23:44 +0200 Subject: [PATCH 0018/2396] tests/functional: Move arm tests into architecture specific folder The tests/functional folder has become quite crowded, thus move the arm tests into a target-specific subfolder. Reviewed-by: Pierrick Bouvier Signed-off-by: Thomas Huth Message-ID: <20250819112403.432587-8-thuth@redhat.com> --- MAINTAINERS | 38 ++++++------ tests/functional/arm/meson.build | 62 +++++++++++++++++++ .../test_aspeed_ast1030.py} | 0 .../test_aspeed_ast2500.py} | 0 .../test_aspeed_ast2600.py} | 0 .../test_aspeed_bletchley.py} | 0 .../test_aspeed_catalina.py} | 0 .../test_aspeed_gb200nvl_bmc.py} | 0 .../test_aspeed_palmetto.py} | 0 .../test_aspeed_rainier.py} | 0 .../test_aspeed_romulus.py} | 0 .../test_aspeed_witherspoon.py} | 0 .../{test_arm_bflt.py => arm/test_bflt.py} | 0 .../test_bpim2u.py} | 0 .../test_canona1100.py} | 0 .../test_collie.py} | 0 .../test_cubieboard.py} | 0 .../test_emcraft_sf2.py} | 0 .../test_integratorcp.py} | 0 .../test_max78000fthr.py} | 0 .../test_microbit.py} | 0 .../test_migration.py} | 0 .../test_orangepi.py} | 0 .../test_quanta_gsj.py} | 0 .../test_raspi2.py} | 0 .../test_realview.py} | 0 .../test_replay.py} | 0 .../test_smdkc210.py} | 0 .../test_stellaris.py} | 0 .../{test_arm_sx1.py => arm/test_sx1.py} | 0 .../test_tuxrun.py} | 0 .../test_vexpress.py} | 0 .../{test_arm_virt.py => arm/test_virt.py} | 0 tests/functional/meson.build | 62 +------------------ 34 files changed, 83 insertions(+), 79 deletions(-) create mode 100644 tests/functional/arm/meson.build rename tests/functional/{test_arm_aspeed_ast1030.py => arm/test_aspeed_ast1030.py} (100%) rename tests/functional/{test_arm_aspeed_ast2500.py => arm/test_aspeed_ast2500.py} (100%) rename tests/functional/{test_arm_aspeed_ast2600.py => arm/test_aspeed_ast2600.py} (100%) rename tests/functional/{test_arm_aspeed_bletchley.py => arm/test_aspeed_bletchley.py} (100%) mode change 100644 => 100755 rename tests/functional/{test_arm_aspeed_catalina.py => arm/test_aspeed_catalina.py} (100%) rename tests/functional/{test_arm_aspeed_gb200nvl_bmc.py => arm/test_aspeed_gb200nvl_bmc.py} (100%) mode change 100644 => 100755 rename tests/functional/{test_arm_aspeed_palmetto.py => arm/test_aspeed_palmetto.py} (100%) rename tests/functional/{test_arm_aspeed_rainier.py => arm/test_aspeed_rainier.py} (100%) rename tests/functional/{test_arm_aspeed_romulus.py => arm/test_aspeed_romulus.py} (100%) rename tests/functional/{test_arm_aspeed_witherspoon.py => arm/test_aspeed_witherspoon.py} (100%) mode change 100644 => 100755 rename tests/functional/{test_arm_bflt.py => arm/test_bflt.py} (100%) rename tests/functional/{test_arm_bpim2u.py => arm/test_bpim2u.py} (100%) rename tests/functional/{test_arm_canona1100.py => arm/test_canona1100.py} (100%) rename tests/functional/{test_arm_collie.py => arm/test_collie.py} (100%) rename tests/functional/{test_arm_cubieboard.py => arm/test_cubieboard.py} (100%) rename tests/functional/{test_arm_emcraft_sf2.py => arm/test_emcraft_sf2.py} (100%) rename tests/functional/{test_arm_integratorcp.py => arm/test_integratorcp.py} (100%) rename tests/functional/{test_arm_max78000fthr.py => arm/test_max78000fthr.py} (100%) rename tests/functional/{test_arm_microbit.py => arm/test_microbit.py} (100%) rename tests/functional/{test_arm_migration.py => arm/test_migration.py} (100%) rename tests/functional/{test_arm_orangepi.py => arm/test_orangepi.py} (100%) rename tests/functional/{test_arm_quanta_gsj.py => arm/test_quanta_gsj.py} (100%) rename tests/functional/{test_arm_raspi2.py => arm/test_raspi2.py} (100%) rename tests/functional/{test_arm_realview.py => arm/test_realview.py} (100%) rename tests/functional/{test_arm_replay.py => arm/test_replay.py} (100%) rename tests/functional/{test_arm_smdkc210.py => arm/test_smdkc210.py} (100%) rename tests/functional/{test_arm_stellaris.py => arm/test_stellaris.py} (100%) rename tests/functional/{test_arm_sx1.py => arm/test_sx1.py} (100%) rename tests/functional/{test_arm_tuxrun.py => arm/test_tuxrun.py} (100%) rename tests/functional/{test_arm_vexpress.py => arm/test_vexpress.py} (100%) rename tests/functional/{test_arm_virt.py => arm/test_virt.py} (100%) diff --git a/MAINTAINERS b/MAINTAINERS index 8115aae618..1eb964feca 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -444,6 +444,7 @@ M: Peter Maydell L: qemu-arm@nongnu.org S: Maintained F: target/arm/kvm.c +F: tests/functional/aarch64/test_kvm.py MIPS KVM CPUs M: Huacai Chen @@ -673,7 +674,7 @@ F: include/hw/*/allwinner* F: hw/arm/cubieboard.c F: docs/system/arm/cubieboard.rst F: hw/misc/axp209.c -F: tests/functional/test_arm_cubieboard.py +F: tests/functional/arm/test_cubieboard.py Allwinner-h3 M: Niek Linnenbank @@ -683,7 +684,7 @@ F: hw/*/allwinner-h3* F: include/hw/*/allwinner-h3* F: hw/arm/orangepi.c F: docs/system/arm/orangepi.rst -F: tests/functional/test_arm_orangepi.py +F: tests/functional/arm/test_orangepi.py ARM PrimeCell and CMSDK devices M: Peter Maydell @@ -753,7 +754,7 @@ F: docs/system/arm/bananapi_m2u.rst F: hw/*/allwinner-r40*.c F: hw/arm/bananapi_m2u.c F: include/hw/*/allwinner-r40*.h -F: tests/functional/test_arm_bpim2u.py +F: tests/functional/arm/test_bpim2u.py B-L475E-IOT01A IoT Node M: Samuel Tardieu @@ -771,7 +772,7 @@ S: Odd Fixes F: hw/*/exynos* F: include/hw/*/exynos* F: docs/system/arm/exynos.rst -F: tests/functional/test_arm_smdkc210.py +F: tests/functional/arm/test_smdkc210.py Calxeda Highbank M: Rob Herring @@ -790,7 +791,7 @@ S: Odd Fixes F: include/hw/arm/digic.h F: hw/*/digic* F: include/hw/*/digic* -F: tests/functional/test_arm_canona1100.py +F: tests/functional/arm/test_canona1100.py F: docs/system/arm/digic.rst Goldfish RTC @@ -833,7 +834,7 @@ S: Odd Fixes F: hw/arm/integratorcp.c F: hw/misc/arm_integrator_debug.c F: include/hw/misc/arm_integrator_debug.h -F: tests/functional/test_arm_integratorcp.py +F: tests/functional/arm/test_integratorcp.py F: docs/system/arm/integratorcp.rst MCIMX6UL EVK / i.MX6ul @@ -939,7 +940,7 @@ F: pc-bios/npcm7xx_bootrom.bin F: pc-bios/npcm8xx_bootrom.bin F: roms/vbootrom F: docs/system/arm/nuvoton.rst -F: tests/functional/test_arm_quanta_gsj.py +F: tests/functional/arm/test_quanta_gsj.py Raspberry Pi M: Peter Maydell @@ -952,7 +953,7 @@ F: hw/*/bcm283* F: include/hw/arm/rasp* F: include/hw/*/bcm283* F: docs/system/arm/raspi.rst -F: tests/functional/test_arm_raspi2.py +F: tests/functional/arm/test_raspi2.py F: tests/functional/aarch64/test_raspi*.py Real View @@ -964,7 +965,7 @@ F: hw/cpu/realview_mpcore.c F: hw/intc/realview_gic.c F: include/hw/intc/realview_gic.h F: docs/system/arm/realview.rst -F: tests/functional/test_arm_realview.py +F: tests/functional/arm/test_realview.py SABRELITE / i.MX6 M: Peter Maydell @@ -1004,7 +1005,7 @@ F: hw/arm/strongarm* F: hw/gpio/zaurus.c F: include/hw/arm/sharpsl.h F: docs/system/arm/collie.rst -F: tests/functional/test_arm_collie.py +F: tests/functional/arm/test_collie.py Stellaris M: Peter Maydell @@ -1015,7 +1016,7 @@ F: hw/display/ssd03* F: include/hw/input/stellaris_gamepad.h F: include/hw/timer/stellaris-gptm.h F: docs/system/arm/stellaris.rst -F: tests/functional/test_arm_stellaris.py +F: tests/functional/arm/test_stellaris.py STM32L4x5 SoC Family M: Samuel Tardieu @@ -1044,7 +1045,7 @@ S: Odd Fixes F: hw/arm/vexpress.c F: hw/display/sii9022.c F: docs/system/arm/vexpress.rst -F: tests/functional/test_arm_vexpress.py +F: tests/functional/arm/test_vexpress.py Versatile PB M: Peter Maydell @@ -1065,8 +1066,8 @@ F: include/hw/arm/virt.h F: docs/system/arm/virt.rst F: tests/functional/aarch64/test_*virt*.py F: tests/functional/aarch64/test_tuxrun.py -F: tests/functional/test_arm_tuxrun.py -F: tests/functional/test_arm_virt.py +F: tests/functional/arm/test_tuxrun.py +F: tests/functional/arm/test_virt.py Xilinx Zynq M: Edgar E. Iglesias @@ -1187,7 +1188,7 @@ L: qemu-arm@nongnu.org S: Maintained F: hw/arm/msf2-som.c F: docs/system/arm/emcraft-sf2.rst -F: tests/functional/test_arm_emcraft_sf2.py +F: tests/functional/arm/test_emcraft_sf2.py ASPEED BMCs M: Cédric Le Goater @@ -1205,6 +1206,7 @@ F: hw/net/ftgmac100.c F: include/hw/net/ftgmac100.h F: docs/system/arm/aspeed.rst F: docs/system/arm/fby35.rst +F: tests/functional/*/*aspeed* F: tests/*/*aspeed* F: tests/*/*ast2700* F: hw/arm/fby35.c @@ -1220,7 +1222,7 @@ F: hw/*/microbit*.c F: include/hw/*/nrf51*.h F: include/hw/*/microbit*.h F: tests/qtest/microbit-test.c -F: tests/functional/test_arm_microbit.py +F: tests/functional/arm/test_microbit.py F: docs/system/arm/nrf.rst ARM PL011 Rust device @@ -2077,7 +2079,7 @@ S: Odd Fixes F: hw/*/omap* F: include/hw/arm/omap.h F: docs/system/arm/sx1.rst -F: tests/functional/test_arm_sx1.py +F: tests/functional/arm/test_sx1.py IPack M: Alberto Garcia @@ -3914,7 +3916,7 @@ F: configs/targets/*linux-user.mak F: scripts/qemu-binfmt-conf.sh F: scripts/update-syscalltbl.sh F: scripts/update-mips-syscall-args.sh -F: tests/functional/test_arm_bflt.py +F: tests/functional/arm/test_bflt.py Tiny Code Generator (TCG) ------------------------- diff --git a/tests/functional/arm/meson.build b/tests/functional/arm/meson.build new file mode 100644 index 0000000000..e4e7dba8d0 --- /dev/null +++ b/tests/functional/arm/meson.build @@ -0,0 +1,62 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +test_arm_timeouts = { + 'aspeed_palmetto' : 120, + 'aspeed_romulus' : 120, + 'aspeed_witherspoon' : 120, + 'aspeed_ast2500' : 720, + 'aspeed_ast2600' : 1200, + 'aspeed_bletchley' : 480, + 'aspeed_catalina' : 480, + 'aspeed_gb200nvl_bmc' : 480, + 'aspeed_rainier' : 480, + 'bpim2u' : 500, + 'collie' : 180, + 'cubieboard' : 360, + 'orangepi' : 540, + 'quanta_gsj' : 240, + 'raspi2' : 120, + 'replay' : 240, + 'tuxrun' : 240, + 'sx1' : 360, +} + +tests_arm_system_quick = [ + 'migration', +] + +tests_arm_system_thorough = [ + 'aspeed_ast1030', + 'aspeed_palmetto', + 'aspeed_romulus', + 'aspeed_witherspoon', + 'aspeed_ast2500', + 'aspeed_ast2600', + 'aspeed_bletchley', + 'aspeed_catalina', + 'aspeed_gb200nvl_bmc', + 'aspeed_rainier', + 'bpim2u', + 'canona1100', + 'collie', + 'cubieboard', + 'emcraft_sf2', + 'integratorcp', + 'max78000fthr', + 'microbit', + 'orangepi', + 'quanta_gsj', + 'raspi2', + 'realview', + 'replay', + 'smdkc210', + 'stellaris', + 'sx1', + 'vexpress', + 'virt', + 'tuxrun', +] + +tests_arm_linuxuser_thorough = [ + 'bflt', +] diff --git a/tests/functional/test_arm_aspeed_ast1030.py b/tests/functional/arm/test_aspeed_ast1030.py similarity index 100% rename from tests/functional/test_arm_aspeed_ast1030.py rename to tests/functional/arm/test_aspeed_ast1030.py diff --git a/tests/functional/test_arm_aspeed_ast2500.py b/tests/functional/arm/test_aspeed_ast2500.py similarity index 100% rename from tests/functional/test_arm_aspeed_ast2500.py rename to tests/functional/arm/test_aspeed_ast2500.py diff --git a/tests/functional/test_arm_aspeed_ast2600.py b/tests/functional/arm/test_aspeed_ast2600.py similarity index 100% rename from tests/functional/test_arm_aspeed_ast2600.py rename to tests/functional/arm/test_aspeed_ast2600.py diff --git a/tests/functional/test_arm_aspeed_bletchley.py b/tests/functional/arm/test_aspeed_bletchley.py old mode 100644 new mode 100755 similarity index 100% rename from tests/functional/test_arm_aspeed_bletchley.py rename to tests/functional/arm/test_aspeed_bletchley.py diff --git a/tests/functional/test_arm_aspeed_catalina.py b/tests/functional/arm/test_aspeed_catalina.py similarity index 100% rename from tests/functional/test_arm_aspeed_catalina.py rename to tests/functional/arm/test_aspeed_catalina.py diff --git a/tests/functional/test_arm_aspeed_gb200nvl_bmc.py b/tests/functional/arm/test_aspeed_gb200nvl_bmc.py old mode 100644 new mode 100755 similarity index 100% rename from tests/functional/test_arm_aspeed_gb200nvl_bmc.py rename to tests/functional/arm/test_aspeed_gb200nvl_bmc.py diff --git a/tests/functional/test_arm_aspeed_palmetto.py b/tests/functional/arm/test_aspeed_palmetto.py similarity index 100% rename from tests/functional/test_arm_aspeed_palmetto.py rename to tests/functional/arm/test_aspeed_palmetto.py diff --git a/tests/functional/test_arm_aspeed_rainier.py b/tests/functional/arm/test_aspeed_rainier.py similarity index 100% rename from tests/functional/test_arm_aspeed_rainier.py rename to tests/functional/arm/test_aspeed_rainier.py diff --git a/tests/functional/test_arm_aspeed_romulus.py b/tests/functional/arm/test_aspeed_romulus.py similarity index 100% rename from tests/functional/test_arm_aspeed_romulus.py rename to tests/functional/arm/test_aspeed_romulus.py diff --git a/tests/functional/test_arm_aspeed_witherspoon.py b/tests/functional/arm/test_aspeed_witherspoon.py old mode 100644 new mode 100755 similarity index 100% rename from tests/functional/test_arm_aspeed_witherspoon.py rename to tests/functional/arm/test_aspeed_witherspoon.py diff --git a/tests/functional/test_arm_bflt.py b/tests/functional/arm/test_bflt.py similarity index 100% rename from tests/functional/test_arm_bflt.py rename to tests/functional/arm/test_bflt.py diff --git a/tests/functional/test_arm_bpim2u.py b/tests/functional/arm/test_bpim2u.py similarity index 100% rename from tests/functional/test_arm_bpim2u.py rename to tests/functional/arm/test_bpim2u.py diff --git a/tests/functional/test_arm_canona1100.py b/tests/functional/arm/test_canona1100.py similarity index 100% rename from tests/functional/test_arm_canona1100.py rename to tests/functional/arm/test_canona1100.py diff --git a/tests/functional/test_arm_collie.py b/tests/functional/arm/test_collie.py similarity index 100% rename from tests/functional/test_arm_collie.py rename to tests/functional/arm/test_collie.py diff --git a/tests/functional/test_arm_cubieboard.py b/tests/functional/arm/test_cubieboard.py similarity index 100% rename from tests/functional/test_arm_cubieboard.py rename to tests/functional/arm/test_cubieboard.py diff --git a/tests/functional/test_arm_emcraft_sf2.py b/tests/functional/arm/test_emcraft_sf2.py similarity index 100% rename from tests/functional/test_arm_emcraft_sf2.py rename to tests/functional/arm/test_emcraft_sf2.py diff --git a/tests/functional/test_arm_integratorcp.py b/tests/functional/arm/test_integratorcp.py similarity index 100% rename from tests/functional/test_arm_integratorcp.py rename to tests/functional/arm/test_integratorcp.py diff --git a/tests/functional/test_arm_max78000fthr.py b/tests/functional/arm/test_max78000fthr.py similarity index 100% rename from tests/functional/test_arm_max78000fthr.py rename to tests/functional/arm/test_max78000fthr.py diff --git a/tests/functional/test_arm_microbit.py b/tests/functional/arm/test_microbit.py similarity index 100% rename from tests/functional/test_arm_microbit.py rename to tests/functional/arm/test_microbit.py diff --git a/tests/functional/test_arm_migration.py b/tests/functional/arm/test_migration.py similarity index 100% rename from tests/functional/test_arm_migration.py rename to tests/functional/arm/test_migration.py diff --git a/tests/functional/test_arm_orangepi.py b/tests/functional/arm/test_orangepi.py similarity index 100% rename from tests/functional/test_arm_orangepi.py rename to tests/functional/arm/test_orangepi.py diff --git a/tests/functional/test_arm_quanta_gsj.py b/tests/functional/arm/test_quanta_gsj.py similarity index 100% rename from tests/functional/test_arm_quanta_gsj.py rename to tests/functional/arm/test_quanta_gsj.py diff --git a/tests/functional/test_arm_raspi2.py b/tests/functional/arm/test_raspi2.py similarity index 100% rename from tests/functional/test_arm_raspi2.py rename to tests/functional/arm/test_raspi2.py diff --git a/tests/functional/test_arm_realview.py b/tests/functional/arm/test_realview.py similarity index 100% rename from tests/functional/test_arm_realview.py rename to tests/functional/arm/test_realview.py diff --git a/tests/functional/test_arm_replay.py b/tests/functional/arm/test_replay.py similarity index 100% rename from tests/functional/test_arm_replay.py rename to tests/functional/arm/test_replay.py diff --git a/tests/functional/test_arm_smdkc210.py b/tests/functional/arm/test_smdkc210.py similarity index 100% rename from tests/functional/test_arm_smdkc210.py rename to tests/functional/arm/test_smdkc210.py diff --git a/tests/functional/test_arm_stellaris.py b/tests/functional/arm/test_stellaris.py similarity index 100% rename from tests/functional/test_arm_stellaris.py rename to tests/functional/arm/test_stellaris.py diff --git a/tests/functional/test_arm_sx1.py b/tests/functional/arm/test_sx1.py similarity index 100% rename from tests/functional/test_arm_sx1.py rename to tests/functional/arm/test_sx1.py diff --git a/tests/functional/test_arm_tuxrun.py b/tests/functional/arm/test_tuxrun.py similarity index 100% rename from tests/functional/test_arm_tuxrun.py rename to tests/functional/arm/test_tuxrun.py diff --git a/tests/functional/test_arm_vexpress.py b/tests/functional/arm/test_vexpress.py similarity index 100% rename from tests/functional/test_arm_vexpress.py rename to tests/functional/arm/test_vexpress.py diff --git a/tests/functional/test_arm_virt.py b/tests/functional/arm/test_virt.py similarity index 100% rename from tests/functional/test_arm_virt.py rename to tests/functional/arm/test_virt.py diff --git a/tests/functional/meson.build b/tests/functional/meson.build index a7f8c88a07..6989446d1c 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -11,27 +11,7 @@ endif subdir('aarch64') subdir('alpha') - -test_arm_timeouts = { - '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_catalina' : 480, - 'arm_aspeed_gb200nvl_bmc' : 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, -} +subdir('arm') test_mips_timeouts = { 'mips_malta' : 480, @@ -97,46 +77,6 @@ tests_generic_linuxuser = [ tests_generic_bsduser = [ ] -tests_arm_system_quick = [ - 'arm_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_catalina', - 'arm_aspeed_gb200nvl_bmc', - 'arm_aspeed_rainier', - 'arm_bpim2u', - 'arm_canona1100', - 'arm_collie', - 'arm_cubieboard', - 'arm_emcraft_sf2', - 'arm_integratorcp', - 'arm_max78000fthr', - '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 = [ - 'arm_bflt', -] - tests_avr_system_thorough = [ 'avr_mega2560', 'avr_uno', From dc8f7a1bf724cb90d37c5bdfcdb80a5c74befc11 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 19 Aug 2025 13:23:45 +0200 Subject: [PATCH 0019/2396] tests/functional: Move avr tests into architecture specific folder MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The tests/functional folder has become quite crowded, thus move the avr tests into a target-specific subfolder. Reviewed-by: Pierrick Bouvier Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Thomas Huth Message-ID: <20250819112403.432587-9-thuth@redhat.com> --- MAINTAINERS | 4 ++-- tests/functional/avr/meson.build | 6 ++++++ .../{test_avr_mega2560.py => avr/test_mega2560.py} | 0 tests/functional/{test_avr_uno.py => avr/test_uno.py} | 0 tests/functional/meson.build | 6 +----- 5 files changed, 9 insertions(+), 7 deletions(-) create mode 100644 tests/functional/avr/meson.build rename tests/functional/{test_avr_mega2560.py => avr/test_mega2560.py} (100%) rename tests/functional/{test_avr_uno.py => avr/test_uno.py} (100%) diff --git a/MAINTAINERS b/MAINTAINERS index 1eb964feca..d01afcbea6 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -221,7 +221,7 @@ S: Maintained F: docs/system/target-avr.rst F: gdb-xml/avr-cpu.xml F: target/avr/ -F: tests/functional/test_avr_*.py +F: tests/functional/avr/ Hexagon TCG CPUs M: Brian Cain @@ -1249,7 +1249,7 @@ Arduino M: Philippe Mathieu-Daudé S: Maintained F: hw/avr/arduino.c -F: tests/functional/test_avr_uno.py +F: tests/functional/avr/test_uno.py HP-PARISC Machines ------------------ diff --git a/tests/functional/avr/meson.build b/tests/functional/avr/meson.build new file mode 100644 index 0000000000..7a2cb7099e --- /dev/null +++ b/tests/functional/avr/meson.build @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +tests_avr_system_thorough = [ + 'mega2560', + 'uno', +] diff --git a/tests/functional/test_avr_mega2560.py b/tests/functional/avr/test_mega2560.py similarity index 100% rename from tests/functional/test_avr_mega2560.py rename to tests/functional/avr/test_mega2560.py diff --git a/tests/functional/test_avr_uno.py b/tests/functional/avr/test_uno.py similarity index 100% rename from tests/functional/test_avr_uno.py rename to tests/functional/avr/test_uno.py diff --git a/tests/functional/meson.build b/tests/functional/meson.build index 6989446d1c..81eaa9c218 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -12,6 +12,7 @@ endif subdir('aarch64') subdir('alpha') subdir('arm') +subdir('avr') test_mips_timeouts = { 'mips_malta' : 480, @@ -77,11 +78,6 @@ tests_generic_linuxuser = [ tests_generic_bsduser = [ ] -tests_avr_system_thorough = [ - 'avr_mega2560', - 'avr_uno', -] - tests_hppa_system_quick = [ 'hppa_seabios', ] From e538303d31bb0b537285571b9b56674e7c90b120 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 19 Aug 2025 13:23:46 +0200 Subject: [PATCH 0020/2396] tests/functional: Move hppa tests into architecture specific folder MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The tests/functional folder has become quite crowded, thus move the avr tests into a target-specific subfolder. Reviewed-by: Pierrick Bouvier Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Thomas Huth Message-ID: <20250819112403.432587-10-thuth@redhat.com> --- MAINTAINERS | 2 +- tests/functional/hppa/meson.build | 5 +++++ .../{test_hppa_seabios.py => hppa/test_seabios.py} | 0 tests/functional/meson.build | 5 +---- 4 files changed, 7 insertions(+), 5 deletions(-) create mode 100644 tests/functional/hppa/meson.build rename tests/functional/{test_hppa_seabios.py => hppa/test_seabios.py} (100%) diff --git a/MAINTAINERS b/MAINTAINERS index d01afcbea6..2e1754912f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1273,7 +1273,7 @@ F: include/hw/pci-host/astro.h F: include/hw/pci-host/dino.h F: pc-bios/hppa-firmware.img F: roms/seabios-hppa/ -F: tests/functional/test_hppa_seabios.py +F: tests/functional/hppa/test_seabios.py LoongArch Machines ------------------ diff --git a/tests/functional/hppa/meson.build b/tests/functional/hppa/meson.build new file mode 100644 index 0000000000..a334837088 --- /dev/null +++ b/tests/functional/hppa/meson.build @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +tests_hppa_system_quick = [ + 'seabios', +] diff --git a/tests/functional/test_hppa_seabios.py b/tests/functional/hppa/test_seabios.py similarity index 100% rename from tests/functional/test_hppa_seabios.py rename to tests/functional/hppa/test_seabios.py diff --git a/tests/functional/meson.build b/tests/functional/meson.build index 81eaa9c218..8f85c13d3d 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -13,6 +13,7 @@ subdir('aarch64') subdir('alpha') subdir('arm') subdir('avr') +subdir('hppa') test_mips_timeouts = { 'mips_malta' : 480, @@ -78,10 +79,6 @@ tests_generic_linuxuser = [ tests_generic_bsduser = [ ] -tests_hppa_system_quick = [ - 'hppa_seabios', -] - tests_i386_system_quick = [ 'i386_migration', ] From 84b30c4587719297955e1fe2a22face5cc84587c Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 19 Aug 2025 13:23:47 +0200 Subject: [PATCH 0021/2396] tests/functional: Move i386 tests into architecture specific folder The tests/functional folder has become quite crowded, thus move the i386 tests into a target-specific subfolder. Reviewed-by: Pierrick Bouvier Signed-off-by: Thomas Huth Message-ID: <20250819112403.432587-11-thuth@redhat.com> --- MAINTAINERS | 3 ++- tests/functional/i386/meson.build | 10 ++++++++++ .../{test_i386_migration.py => i386/test_migration.py} | 0 .../{test_i386_replay.py => i386/test_replay.py} | 0 .../{test_i386_tuxrun.py => i386/test_tuxrun.py} | 0 tests/functional/meson.build | 10 +--------- 6 files changed, 13 insertions(+), 10 deletions(-) create mode 100644 tests/functional/i386/meson.build rename tests/functional/{test_i386_migration.py => i386/test_migration.py} (100%) rename tests/functional/{test_i386_replay.py => i386/test_replay.py} (100%) rename tests/functional/{test_i386_tuxrun.py => i386/test_tuxrun.py} (100%) diff --git a/MAINTAINERS b/MAINTAINERS index 2e1754912f..2b109ecc18 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -146,6 +146,7 @@ F: target/i386/*.[ch] F: target/i386/Kconfig F: target/i386/meson.build F: tools/i386/ +F: tests/functional/i386/ Guest CPU cores (TCG) --------------------- @@ -1891,7 +1892,7 @@ F: hw/isa/apm.c F: include/hw/isa/apm.h F: tests/unit/test-x86-topo.c F: tests/qtest/test-x86-cpuid-compat.c -F: tests/functional/test_i386_tuxrun.py +F: tests/functional/i386/test_tuxrun.py F: tests/functional/test_linux_initrd.py F: tests/functional/test_mem_addr_space.py F: tests/functional/test_pc_cpu_hotplug_props.py diff --git a/tests/functional/i386/meson.build b/tests/functional/i386/meson.build new file mode 100644 index 0000000000..23d8c216be --- /dev/null +++ b/tests/functional/i386/meson.build @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +tests_i386_system_quick = [ + 'migration', +] + +tests_i386_system_thorough = [ + 'replay', + 'tuxrun', +] diff --git a/tests/functional/test_i386_migration.py b/tests/functional/i386/test_migration.py similarity index 100% rename from tests/functional/test_i386_migration.py rename to tests/functional/i386/test_migration.py diff --git a/tests/functional/test_i386_replay.py b/tests/functional/i386/test_replay.py similarity index 100% rename from tests/functional/test_i386_replay.py rename to tests/functional/i386/test_replay.py diff --git a/tests/functional/test_i386_tuxrun.py b/tests/functional/i386/test_tuxrun.py similarity index 100% rename from tests/functional/test_i386_tuxrun.py rename to tests/functional/i386/test_tuxrun.py diff --git a/tests/functional/meson.build b/tests/functional/meson.build index 8f85c13d3d..f1fc01717c 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -14,6 +14,7 @@ subdir('alpha') subdir('arm') subdir('avr') subdir('hppa') +subdir('i386') test_mips_timeouts = { 'mips_malta' : 480, @@ -79,15 +80,6 @@ tests_generic_linuxuser = [ tests_generic_bsduser = [ ] -tests_i386_system_quick = [ - 'i386_migration', -] - -tests_i386_system_thorough = [ - 'i386_replay', - 'i386_tuxrun', -] - tests_loongarch64_system_thorough = [ 'loongarch64_virt', ] From 6bd38ef0f9af38364509005385ff2ac897ad3913 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 19 Aug 2025 13:23:48 +0200 Subject: [PATCH 0022/2396] tests/functional: Move loongarch64 tests into architecture specific folder MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The tests/functional folder has become quite crowded, thus move the loongarch64 tests into a target-specific subfolder. Reviewed-by: Pierrick Bouvier Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Thomas Huth Message-ID: <20250819112403.432587-12-thuth@redhat.com> --- MAINTAINERS | 2 +- tests/functional/loongarch64/meson.build | 5 +++++ .../{test_loongarch64_virt.py => loongarch64/test_virt.py} | 0 tests/functional/meson.build | 5 +---- 4 files changed, 7 insertions(+), 5 deletions(-) create mode 100644 tests/functional/loongarch64/meson.build rename tests/functional/{test_loongarch64_virt.py => loongarch64/test_virt.py} (100%) diff --git a/MAINTAINERS b/MAINTAINERS index 2b109ecc18..716127e831 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -258,7 +258,7 @@ M: Song Gao S: Maintained F: target/loongarch/ F: tests/tcg/loongarch64/ -F: tests/functional/test_loongarch64_virt.py +F: tests/functional/loongarch64/test_virt.py M68K TCG CPUs M: Laurent Vivier diff --git a/tests/functional/loongarch64/meson.build b/tests/functional/loongarch64/meson.build new file mode 100644 index 0000000000..d1687176a3 --- /dev/null +++ b/tests/functional/loongarch64/meson.build @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +tests_loongarch64_system_thorough = [ + 'virt', +] diff --git a/tests/functional/test_loongarch64_virt.py b/tests/functional/loongarch64/test_virt.py similarity index 100% rename from tests/functional/test_loongarch64_virt.py rename to tests/functional/loongarch64/test_virt.py diff --git a/tests/functional/meson.build b/tests/functional/meson.build index f1fc01717c..e2e66dcf52 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -15,6 +15,7 @@ subdir('arm') subdir('avr') subdir('hppa') subdir('i386') +subdir('loongarch64') test_mips_timeouts = { 'mips_malta' : 480, @@ -80,10 +81,6 @@ tests_generic_linuxuser = [ tests_generic_bsduser = [ ] -tests_loongarch64_system_thorough = [ - 'loongarch64_virt', -] - tests_m68k_system_thorough = [ 'm68k_mcf5208evb', 'm68k_nextcube', From 0ae63d0e19b22d45cd354b428b4b530223f53d7b Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 19 Aug 2025 13:23:49 +0200 Subject: [PATCH 0023/2396] tests/functional: Move m68k tests into architecture specific folder MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The tests/functional folder has become quite crowded, thus move the m68k tests into a target-specific subfolder. Reviewed-by: Pierrick Bouvier Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Thomas Huth Message-ID: <20250819112403.432587-13-thuth@redhat.com> --- MAINTAINERS | 8 ++++---- tests/functional/m68k/meson.build | 9 +++++++++ .../{test_m68k_mcf5208evb.py => m68k/test_mcf5208evb.py} | 0 .../{test_m68k_nextcube.py => m68k/test_nextcube.py} | 0 .../functional/{test_m68k_q800.py => m68k/test_q800.py} | 0 .../{test_m68k_replay.py => m68k/test_replay.py} | 0 .../{test_m68k_tuxrun.py => m68k/test_tuxrun.py} | 0 tests/functional/meson.build | 9 +-------- 8 files changed, 14 insertions(+), 12 deletions(-) create mode 100644 tests/functional/m68k/meson.build rename tests/functional/{test_m68k_mcf5208evb.py => m68k/test_mcf5208evb.py} (100%) rename tests/functional/{test_m68k_nextcube.py => m68k/test_nextcube.py} (100%) rename tests/functional/{test_m68k_q800.py => m68k/test_q800.py} (100%) rename tests/functional/{test_m68k_replay.py => m68k/test_replay.py} (100%) rename tests/functional/{test_m68k_tuxrun.py => m68k/test_tuxrun.py} (100%) diff --git a/MAINTAINERS b/MAINTAINERS index 716127e831..e188de813f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1312,7 +1312,7 @@ F: hw/m68k/mcf_intc.c F: hw/char/mcf_uart.c F: hw/net/mcf_fec.c F: include/hw/m68k/mcf*.h -F: tests/functional/test_m68k_mcf5208evb.py +F: tests/functional/m68k/test_mcf5208evb.py NeXTcube M: Thomas Huth @@ -1320,7 +1320,7 @@ S: Odd Fixes F: hw/m68k/next-*.c F: hw/display/next-fb.c F: include/hw/m68k/next-cube.h -F: tests/functional/test_m68k_nextcube.py +F: tests/functional/m68k/test_nextcube.py q800 M: Laurent Vivier @@ -1346,7 +1346,7 @@ F: include/hw/m68k/q800-glue.h F: include/hw/misc/djmemc.h F: include/hw/misc/iosb.h F: include/hw/audio/asc.h -F: tests/functional/test_m68k_q800.py +F: tests/functional/m68k/test_q800.py virt M: Laurent Vivier @@ -1361,7 +1361,7 @@ F: include/hw/intc/goldfish_pic.h F: include/hw/intc/m68k_irqc.h F: include/hw/misc/virt_ctrl.h F: docs/specs/virt-ctlr.rst -F: tests/functional/test_m68k_tuxrun.py +F: tests/functional/m68k/test_tuxrun.py MicroBlaze Machines ------------------- diff --git a/tests/functional/m68k/meson.build b/tests/functional/m68k/meson.build new file mode 100644 index 0000000000..e29044a6d7 --- /dev/null +++ b/tests/functional/m68k/meson.build @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +tests_m68k_system_thorough = [ + 'mcf5208evb', + 'nextcube', + 'replay', + 'q800', + 'tuxrun', +] diff --git a/tests/functional/test_m68k_mcf5208evb.py b/tests/functional/m68k/test_mcf5208evb.py similarity index 100% rename from tests/functional/test_m68k_mcf5208evb.py rename to tests/functional/m68k/test_mcf5208evb.py diff --git a/tests/functional/test_m68k_nextcube.py b/tests/functional/m68k/test_nextcube.py similarity index 100% rename from tests/functional/test_m68k_nextcube.py rename to tests/functional/m68k/test_nextcube.py diff --git a/tests/functional/test_m68k_q800.py b/tests/functional/m68k/test_q800.py similarity index 100% rename from tests/functional/test_m68k_q800.py rename to tests/functional/m68k/test_q800.py diff --git a/tests/functional/test_m68k_replay.py b/tests/functional/m68k/test_replay.py similarity index 100% rename from tests/functional/test_m68k_replay.py rename to tests/functional/m68k/test_replay.py diff --git a/tests/functional/test_m68k_tuxrun.py b/tests/functional/m68k/test_tuxrun.py similarity index 100% rename from tests/functional/test_m68k_tuxrun.py rename to tests/functional/m68k/test_tuxrun.py diff --git a/tests/functional/meson.build b/tests/functional/meson.build index e2e66dcf52..d32dd4a371 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -16,6 +16,7 @@ subdir('avr') subdir('hppa') subdir('i386') subdir('loongarch64') +subdir('m68k') test_mips_timeouts = { 'mips_malta' : 480, @@ -81,14 +82,6 @@ tests_generic_linuxuser = [ tests_generic_bsduser = [ ] -tests_m68k_system_thorough = [ - 'm68k_mcf5208evb', - 'm68k_nextcube', - 'm68k_replay', - 'm68k_q800', - 'm68k_tuxrun', -] - tests_microblaze_system_thorough = [ 'microblaze_replay', 'microblaze_s3adsp1800' From 67adbc8b6f40f6a2370d4a0ba479d3778d147cbc Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 19 Aug 2025 13:23:50 +0200 Subject: [PATCH 0024/2396] tests/functional: Move microblaze tests into architecture specific folder MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The tests/functional folder has become quite crowded, thus move the microblaze tests into a target-specific subfolder. Reviewed-by: Pierrick Bouvier Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Thomas Huth Message-ID: <20250819112403.432587-14-thuth@redhat.com> --- MAINTAINERS | 2 +- tests/functional/meson.build | 11 ++--------- tests/functional/microblaze/meson.build | 6 ++++++ .../test_replay.py} | 0 .../test_s3adsp1800.py} | 0 tests/functional/microblazeel/meson.build | 5 +++++ .../test_s3adsp1800.py} | 2 +- 7 files changed, 15 insertions(+), 11 deletions(-) create mode 100644 tests/functional/microblaze/meson.build rename tests/functional/{test_microblaze_replay.py => microblaze/test_replay.py} (100%) rename tests/functional/{test_microblaze_s3adsp1800.py => microblaze/test_s3adsp1800.py} (100%) create mode 100644 tests/functional/microblazeel/meson.build rename tests/functional/{test_microblazeel_s3adsp1800.py => microblazeel/test_s3adsp1800.py} (92%) diff --git a/MAINTAINERS b/MAINTAINERS index e188de813f..b6a835777b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1370,7 +1370,7 @@ M: Edgar E. Iglesias S: Maintained F: hw/microblaze/petalogix_s3adsp1800_mmu.c F: include/hw/char/xilinx_uartlite.h -F: tests/functional/test_microblaze*.py +F: tests/functional/microblaze*/test_s3adsp1800.py petalogix_ml605 M: Edgar E. Iglesias diff --git a/tests/functional/meson.build b/tests/functional/meson.build index d32dd4a371..fee6805614 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -17,6 +17,8 @@ subdir('hppa') subdir('i386') subdir('loongarch64') subdir('m68k') +subdir('microblaze') +subdir('microblazeel') test_mips_timeouts = { 'mips_malta' : 480, @@ -82,15 +84,6 @@ tests_generic_linuxuser = [ tests_generic_bsduser = [ ] -tests_microblaze_system_thorough = [ - 'microblaze_replay', - 'microblaze_s3adsp1800' -] - -tests_microblazeel_system_thorough = [ - 'microblazeel_s3adsp1800' -] - tests_mips_system_thorough = [ 'mips_malta', 'mips_replay', diff --git a/tests/functional/microblaze/meson.build b/tests/functional/microblaze/meson.build new file mode 100644 index 0000000000..8069ca9be6 --- /dev/null +++ b/tests/functional/microblaze/meson.build @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +tests_microblaze_system_thorough = [ + 'replay', + 's3adsp1800' +] diff --git a/tests/functional/test_microblaze_replay.py b/tests/functional/microblaze/test_replay.py similarity index 100% rename from tests/functional/test_microblaze_replay.py rename to tests/functional/microblaze/test_replay.py diff --git a/tests/functional/test_microblaze_s3adsp1800.py b/tests/functional/microblaze/test_s3adsp1800.py similarity index 100% rename from tests/functional/test_microblaze_s3adsp1800.py rename to tests/functional/microblaze/test_s3adsp1800.py diff --git a/tests/functional/microblazeel/meson.build b/tests/functional/microblazeel/meson.build new file mode 100644 index 0000000000..27619dc5a9 --- /dev/null +++ b/tests/functional/microblazeel/meson.build @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +tests_microblazeel_system_thorough = [ + 's3adsp1800' +] diff --git a/tests/functional/test_microblazeel_s3adsp1800.py b/tests/functional/microblazeel/test_s3adsp1800.py similarity index 92% rename from tests/functional/test_microblazeel_s3adsp1800.py rename to tests/functional/microblazeel/test_s3adsp1800.py index 915902d48b..75ce8856ed 100755 --- a/tests/functional/test_microblazeel_s3adsp1800.py +++ b/tests/functional/microblazeel/test_s3adsp1800.py @@ -7,7 +7,7 @@ # 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 test_microblaze_s3adsp1800 import MicroblazeMachine +from microblaze.test_s3adsp1800 import MicroblazeMachine class MicroblazeLittleEndianMachine(MicroblazeMachine): From f227c45e5b6e928a413fcf7176b59aac4e430c82 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 19 Aug 2025 13:23:51 +0200 Subject: [PATCH 0025/2396] tests/functional: Move mips tests into target-specific folders MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The tests/functional folder has become quite crowded, thus move the mips tests into a target-specific subfolder. Reviewed-by: Pierrick Bouvier Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Thomas Huth Message-ID: <20250819112403.432587-15-thuth@redhat.com> --- MAINTAINERS | 8 ++-- tests/functional/meson.build | 47 ++----------------- tests/functional/mips/meson.build | 11 +++++ .../test_malta.py} | 0 .../test_replay.py} | 0 .../test_tuxrun.py} | 0 tests/functional/mips64/meson.build | 10 ++++ .../test_malta.py} | 2 +- .../test_tuxrun.py} | 0 tests/functional/mips64el/meson.build | 14 ++++++ .../test_fuloong2e.py} | 0 .../test_loongson3v.py} | 0 .../test_malta.py} | 4 +- .../test_replay.py} | 0 .../test_tuxrun.py} | 0 tests/functional/mipsel/meson.build | 12 +++++ .../test_malta.py} | 2 +- .../test_replay.py} | 0 .../test_tuxrun.py} | 0 19 files changed, 59 insertions(+), 51 deletions(-) create mode 100644 tests/functional/mips/meson.build rename tests/functional/{test_mips_malta.py => mips/test_malta.py} (100%) rename tests/functional/{test_mips_replay.py => mips/test_replay.py} (100%) rename tests/functional/{test_mips_tuxrun.py => mips/test_tuxrun.py} (100%) create mode 100644 tests/functional/mips64/meson.build rename tests/functional/{test_mips64_malta.py => mips64/test_malta.py} (96%) rename tests/functional/{test_mips64_tuxrun.py => mips64/test_tuxrun.py} (100%) create mode 100644 tests/functional/mips64el/meson.build rename tests/functional/{test_mips64el_fuloong2e.py => mips64el/test_fuloong2e.py} (100%) rename tests/functional/{test_mips64el_loongson3v.py => mips64el/test_loongson3v.py} (100%) rename tests/functional/{test_mips64el_malta.py => mips64el/test_malta.py} (98%) rename tests/functional/{test_mips64el_replay.py => mips64el/test_replay.py} (100%) rename tests/functional/{test_mips64el_tuxrun.py => mips64el/test_tuxrun.py} (100%) create mode 100644 tests/functional/mipsel/meson.build rename tests/functional/{test_mipsel_malta.py => mipsel/test_malta.py} (98%) rename tests/functional/{test_mipsel_replay.py => mipsel/test_replay.py} (100%) mode change 100644 => 100755 rename tests/functional/{test_mipsel_tuxrun.py => mipsel/test_tuxrun.py} (100%) diff --git a/MAINTAINERS b/MAINTAINERS index b6a835777b..b8f0ce3360 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1406,8 +1406,8 @@ F: hw/acpi/piix4.c F: hw/mips/malta.c F: hw/pci-host/gt64120.c F: include/hw/southbridge/piix.h -F: tests/functional/test_mips*_malta.py -F: tests/functional/test_mips*_tuxrun.py +F: tests/functional/mips*/test_malta.py +F: tests/functional/mips*/test_tuxrun.py Mipssim R: Aleksandar Rikalo @@ -1423,7 +1423,7 @@ S: Odd Fixes F: hw/mips/fuloong2e.c F: hw/pci-host/bonito.c F: include/hw/pci-host/bonito.h -F: tests/functional/test_mips64el_fuloong2e.py +F: tests/functional/mips64el/test_fuloong2e.py Loongson-3 virtual platforms M: Huacai Chen @@ -1438,7 +1438,7 @@ F: hw/mips/loongson3_virt.c F: include/hw/intc/loongson_ipi_common.h F: include/hw/intc/loongson_ipi.h F: include/hw/intc/loongson_liointc.h -F: tests/functional/test_mips64el_loongson3v.py +F: tests/functional/mips64el/test_loongson3v.py Boston M: Paul Burton diff --git a/tests/functional/meson.build b/tests/functional/meson.build index fee6805614..52969a3ff8 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -19,24 +19,10 @@ subdir('loongarch64') subdir('m68k') subdir('microblaze') subdir('microblazeel') - -test_mips_timeouts = { - 'mips_malta' : 480, -} - -test_mipsel_timeouts = { - 'mipsel_malta' : 420, - 'mipsel_replay' : 480, -} - -test_mips64_timeouts = { - 'mips64_malta' : 240, -} - -test_mips64el_timeouts = { - 'mips64el_malta' : 420, - 'mips64el_replay' : 180, -} +subdir('mips') +subdir('mipsel') +subdir('mips64') +subdir('mips64el') test_ppc_timeouts = { 'ppc_40p' : 240, @@ -84,31 +70,6 @@ tests_generic_linuxuser = [ tests_generic_bsduser = [ ] -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', diff --git a/tests/functional/mips/meson.build b/tests/functional/mips/meson.build new file mode 100644 index 0000000000..49aaf53b02 --- /dev/null +++ b/tests/functional/mips/meson.build @@ -0,0 +1,11 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +test_mips_timeouts = { + 'malta' : 480, +} + +tests_mips_system_thorough = [ + 'malta', + 'replay', + 'tuxrun', +] diff --git a/tests/functional/test_mips_malta.py b/tests/functional/mips/test_malta.py similarity index 100% rename from tests/functional/test_mips_malta.py rename to tests/functional/mips/test_malta.py diff --git a/tests/functional/test_mips_replay.py b/tests/functional/mips/test_replay.py similarity index 100% rename from tests/functional/test_mips_replay.py rename to tests/functional/mips/test_replay.py diff --git a/tests/functional/test_mips_tuxrun.py b/tests/functional/mips/test_tuxrun.py similarity index 100% rename from tests/functional/test_mips_tuxrun.py rename to tests/functional/mips/test_tuxrun.py diff --git a/tests/functional/mips64/meson.build b/tests/functional/mips64/meson.build new file mode 100644 index 0000000000..3ff2118987 --- /dev/null +++ b/tests/functional/mips64/meson.build @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +test_mips64_timeouts = { + 'malta' : 240, +} + +tests_mips64_system_thorough = [ + 'malta', + 'tuxrun', +] diff --git a/tests/functional/test_mips64_malta.py b/tests/functional/mips64/test_malta.py similarity index 96% rename from tests/functional/test_mips64_malta.py rename to tests/functional/mips64/test_malta.py index 53c3e0c122..a553d3c5bc 100755 --- a/tests/functional/test_mips64_malta.py +++ b/tests/functional/mips64/test_malta.py @@ -5,7 +5,7 @@ # SPDX-License-Identifier: GPL-2.0-or-later from qemu_test import LinuxKernelTest, Asset -from test_mips_malta import mips_check_wheezy +from mips.test_malta import mips_check_wheezy class MaltaMachineConsole(LinuxKernelTest): diff --git a/tests/functional/test_mips64_tuxrun.py b/tests/functional/mips64/test_tuxrun.py similarity index 100% rename from tests/functional/test_mips64_tuxrun.py rename to tests/functional/mips64/test_tuxrun.py diff --git a/tests/functional/mips64el/meson.build b/tests/functional/mips64el/meson.build new file mode 100644 index 0000000000..69ec50174c --- /dev/null +++ b/tests/functional/mips64el/meson.build @@ -0,0 +1,14 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +test_mips64el_timeouts = { + 'malta' : 420, + 'replay' : 180, +} + +tests_mips64el_system_thorough = [ + 'fuloong2e', + 'loongson3v', + 'malta', + 'replay', + 'tuxrun', +] diff --git a/tests/functional/test_mips64el_fuloong2e.py b/tests/functional/mips64el/test_fuloong2e.py similarity index 100% rename from tests/functional/test_mips64el_fuloong2e.py rename to tests/functional/mips64el/test_fuloong2e.py diff --git a/tests/functional/test_mips64el_loongson3v.py b/tests/functional/mips64el/test_loongson3v.py similarity index 100% rename from tests/functional/test_mips64el_loongson3v.py rename to tests/functional/mips64el/test_loongson3v.py diff --git a/tests/functional/test_mips64el_malta.py b/tests/functional/mips64el/test_malta.py similarity index 98% rename from tests/functional/test_mips64el_malta.py rename to tests/functional/mips64el/test_malta.py index 3cc79b74c1..8fdc49b300 100755 --- a/tests/functional/test_mips64el_malta.py +++ b/tests/functional/mips64el/test_malta.py @@ -16,7 +16,7 @@ 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 +from mips.test_malta import mips_check_wheezy class MaltaMachineConsole(LinuxKernelTest): @@ -191,7 +191,7 @@ class MaltaMachineFramebuffer(LinuxKernelTest): self.do_test_i6400_framebuffer_logo(8) -from test_mipsel_malta import MaltaMachineYAMON +from mipsel.test_malta import MaltaMachineYAMON if __name__ == '__main__': LinuxKernelTest.main() diff --git a/tests/functional/test_mips64el_replay.py b/tests/functional/mips64el/test_replay.py similarity index 100% rename from tests/functional/test_mips64el_replay.py rename to tests/functional/mips64el/test_replay.py diff --git a/tests/functional/test_mips64el_tuxrun.py b/tests/functional/mips64el/test_tuxrun.py similarity index 100% rename from tests/functional/test_mips64el_tuxrun.py rename to tests/functional/mips64el/test_tuxrun.py diff --git a/tests/functional/mipsel/meson.build b/tests/functional/mipsel/meson.build new file mode 100644 index 0000000000..8bfdf0649b --- /dev/null +++ b/tests/functional/mipsel/meson.build @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +test_mipsel_timeouts = { + 'malta' : 420, + 'replay' : 480, +} + +tests_mipsel_system_thorough = [ + 'malta', + 'replay', + 'tuxrun', +] diff --git a/tests/functional/test_mipsel_malta.py b/tests/functional/mipsel/test_malta.py similarity index 98% rename from tests/functional/test_mipsel_malta.py rename to tests/functional/mipsel/test_malta.py index 9ee2884da8..427e163d19 100755 --- a/tests/functional/test_mipsel_malta.py +++ b/tests/functional/mipsel/test_malta.py @@ -13,7 +13,7 @@ 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 +from mips.test_malta import mips_check_wheezy class MaltaMachineConsole(LinuxKernelTest): diff --git a/tests/functional/test_mipsel_replay.py b/tests/functional/mipsel/test_replay.py old mode 100644 new mode 100755 similarity index 100% rename from tests/functional/test_mipsel_replay.py rename to tests/functional/mipsel/test_replay.py diff --git a/tests/functional/test_mipsel_tuxrun.py b/tests/functional/mipsel/test_tuxrun.py similarity index 100% rename from tests/functional/test_mipsel_tuxrun.py rename to tests/functional/mipsel/test_tuxrun.py From ff94df87caeecf3fe2b77e06d2ffe483725bcb51 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 19 Aug 2025 13:23:52 +0200 Subject: [PATCH 0026/2396] tests/functional: Move or1k tests into target-specific folders MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The tests/functional folder has become quite crowded, thus move the openrisc tests into a target-specific subfolder. Reviewed-by: Pierrick Bouvier Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Thomas Huth Message-ID: <20250819112403.432587-16-thuth@redhat.com> --- MAINTAINERS | 2 +- tests/functional/meson.build | 6 +----- tests/functional/or1k/meson.build | 6 ++++++ .../functional/{test_or1k_replay.py => or1k/test_replay.py} | 0 tests/functional/{test_or1k_sim.py => or1k/test_sim.py} | 0 5 files changed, 8 insertions(+), 6 deletions(-) create mode 100644 tests/functional/or1k/meson.build rename tests/functional/{test_or1k_replay.py => or1k/test_replay.py} (100%) rename tests/functional/{test_or1k_sim.py => or1k/test_sim.py} (100%) diff --git a/MAINTAINERS b/MAINTAINERS index b8f0ce3360..56ba9b02c3 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1457,7 +1457,7 @@ S: Maintained F: docs/system/openrisc/or1k-sim.rst F: hw/intc/ompic.c F: hw/openrisc/openrisc_sim.c -F: tests/functional/test_or1k_sim.py +F: tests/functional/or1k/test_sim.py PowerPC Machines ---------------- diff --git a/tests/functional/meson.build b/tests/functional/meson.build index 52969a3ff8..397303ec6f 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -23,6 +23,7 @@ subdir('mips') subdir('mipsel') subdir('mips64') subdir('mips64el') +subdir('or1k') test_ppc_timeouts = { 'ppc_40p' : 240, @@ -70,11 +71,6 @@ tests_generic_linuxuser = [ tests_generic_bsduser = [ ] -tests_or1k_system_thorough = [ - 'or1k_replay', - 'or1k_sim', -] - tests_ppc_system_quick = [ 'ppc_migration', 'ppc_74xx', diff --git a/tests/functional/or1k/meson.build b/tests/functional/or1k/meson.build new file mode 100644 index 0000000000..e246e2ab08 --- /dev/null +++ b/tests/functional/or1k/meson.build @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +tests_or1k_system_thorough = [ + 'replay', + 'sim', +] diff --git a/tests/functional/test_or1k_replay.py b/tests/functional/or1k/test_replay.py similarity index 100% rename from tests/functional/test_or1k_replay.py rename to tests/functional/or1k/test_replay.py diff --git a/tests/functional/test_or1k_sim.py b/tests/functional/or1k/test_sim.py similarity index 100% rename from tests/functional/test_or1k_sim.py rename to tests/functional/or1k/test_sim.py From 1cdb63218c975b20699714449131c6745a512a1f Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 19 Aug 2025 13:23:53 +0200 Subject: [PATCH 0027/2396] tests/functional: Move ppc/ppc64 tests into target-specific folders The tests/functional folder has become quite crowded, thus move the ppc and ppc64 tests into a target-specific subfolder. Reviewed-by: Pierrick Bouvier Signed-off-by: Thomas Huth Message-ID: <20250819112403.432587-17-thuth@redhat.com> --- MAINTAINERS | 32 ++++++------- tests/functional/meson.build | 47 +------------------ tests/functional/ppc/meson.build | 22 +++++++++ .../{test_ppc_40p.py => ppc/test_40p.py} | 0 .../{test_ppc_74xx.py => ppc/test_74xx.py} | 0 .../{test_ppc_amiga.py => ppc/test_amiga.py} | 0 .../test_bamboo.py} | 0 .../{test_ppc_mac.py => ppc/test_mac.py} | 0 .../test_migration.py} | 0 .../test_mpc8544ds.py} | 0 .../test_replay.py} | 0 .../test_sam460ex.py} | 0 .../test_tuxrun.py} | 0 .../test_virtex_ml507.py} | 0 tests/functional/ppc64/meson.build | 25 ++++++++++ .../test_e500.py} | 0 .../{test_ppc64_hv.py => ppc64/test_hv.py} | 0 .../test_mac99.py} | 0 .../test_migration.py} | 0 .../test_powernv.py} | 0 .../test_pseries.py} | 0 .../test_replay.py} | 0 .../test_reverse_debug.py} | 0 .../test_tuxrun.py} | 0 24 files changed, 65 insertions(+), 61 deletions(-) create mode 100644 tests/functional/ppc/meson.build rename tests/functional/{test_ppc_40p.py => ppc/test_40p.py} (100%) rename tests/functional/{test_ppc_74xx.py => ppc/test_74xx.py} (100%) rename tests/functional/{test_ppc_amiga.py => ppc/test_amiga.py} (100%) rename tests/functional/{test_ppc_bamboo.py => ppc/test_bamboo.py} (100%) rename tests/functional/{test_ppc_mac.py => ppc/test_mac.py} (100%) rename tests/functional/{test_ppc_migration.py => ppc/test_migration.py} (100%) rename tests/functional/{test_ppc_mpc8544ds.py => ppc/test_mpc8544ds.py} (100%) rename tests/functional/{test_ppc_replay.py => ppc/test_replay.py} (100%) rename tests/functional/{test_ppc_sam460ex.py => ppc/test_sam460ex.py} (100%) mode change 100644 => 100755 rename tests/functional/{test_ppc_tuxrun.py => ppc/test_tuxrun.py} (100%) rename tests/functional/{test_ppc_virtex_ml507.py => ppc/test_virtex_ml507.py} (100%) create mode 100644 tests/functional/ppc64/meson.build rename tests/functional/{test_ppc64_e500.py => ppc64/test_e500.py} (100%) rename tests/functional/{test_ppc64_hv.py => ppc64/test_hv.py} (100%) rename tests/functional/{test_ppc64_mac99.py => ppc64/test_mac99.py} (100%) rename tests/functional/{test_ppc64_migration.py => ppc64/test_migration.py} (100%) rename tests/functional/{test_ppc64_powernv.py => ppc64/test_powernv.py} (100%) rename tests/functional/{test_ppc64_pseries.py => ppc64/test_pseries.py} (100%) rename tests/functional/{test_ppc64_replay.py => ppc64/test_replay.py} (100%) rename tests/functional/{test_ppc64_reverse_debug.py => ppc64/test_reverse_debug.py} (100%) rename tests/functional/{test_ppc64_tuxrun.py => ppc64/test_tuxrun.py} (100%) diff --git a/MAINTAINERS b/MAINTAINERS index 56ba9b02c3..b0d440cf75 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -310,7 +310,7 @@ F: configs/devices/ppc* F: docs/system/ppc/embedded.rst F: docs/system/target-ppc.rst F: tests/tcg/ppc*/* -F: tests/functional/test_ppc_74xx.py +F: tests/functional/ppc/test_74xx.py RISC-V TCG CPUs M: Palmer Dabbelt @@ -1466,7 +1466,7 @@ L: qemu-ppc@nongnu.org S: Orphan F: hw/ppc/ppc440_bamboo.c F: hw/pci-host/ppc4xx_pci.c -F: tests/functional/test_ppc_bamboo.py +F: tests/functional/ppc/test_bamboo.py e500 M: Bernhard Beschow @@ -1484,8 +1484,8 @@ F: pc-bios/u-boot.e500 F: hw/intc/openpic_kvm.c F: include/hw/ppc/openpic_kvm.h F: docs/system/ppc/ppce500.rst -F: tests/functional/test_ppc64_e500.py -F: tests/functional/test_ppc_tuxrun.py +F: tests/functional/ppc64/test_e500.py +F: tests/functional/ppc/test_tuxrun.py mpc8544ds M: Bernhard Beschow @@ -1493,7 +1493,7 @@ L: qemu-ppc@nongnu.org S: Odd Fixes F: hw/ppc/mpc8544ds.c F: hw/ppc/mpc8544_guts.c -F: tests/functional/test_ppc_mpc8544ds.py +F: tests/functional/ppc/test_mpc8544ds.py New World (mac99) M: Mark Cave-Ayland @@ -1515,8 +1515,8 @@ F: include/hw/ppc/mac_dbdma.h F: include/hw/pci-host/uninorth.h F: include/hw/input/adb* F: pc-bios/qemu_vga.ndrv -F: tests/functional/test_ppc_mac.py -F: tests/functional/test_ppc64_mac99.py +F: tests/functional/ppc/test_mac.py +F: tests/functional/ppc64/test_mac99.py Old World (g3beige) M: Mark Cave-Ayland @@ -1532,7 +1532,7 @@ F: include/hw/intc/heathrow_pic.h F: include/hw/input/adb* F: include/hw/pci-host/grackle.h F: pc-bios/qemu_vga.ndrv -F: tests/functional/test_ppc_mac.py +F: tests/functional/ppc/test_mac.py PReP M: Hervé Poussineau @@ -1549,7 +1549,7 @@ F: hw/dma/i82374.c F: hw/rtc/m48t59-isa.c F: include/hw/isa/pc87312.h F: include/hw/rtc/m48t59.h -F: tests/functional/test_ppc_40p.py +F: tests/functional/ppc/test_40p.py sPAPR (pseries) M: Nicholas Piggin @@ -1572,9 +1572,9 @@ F: tests/qtest/spapr* F: tests/qtest/libqos/*spapr* F: tests/qtest/rtas* F: tests/qtest/libqos/rtas* -F: tests/functional/test_ppc64_pseries.py -F: tests/functional/test_ppc64_hv.py -F: tests/functional/test_ppc64_tuxrun.py +F: tests/functional/ppc64/test_pseries.py +F: tests/functional/ppc64/test_hv.py +F: tests/functional/ppc64/test_tuxrun.py PowerNV (Non-Virtualized) M: Nicholas Piggin @@ -1593,7 +1593,7 @@ F: include/hw/ssi/pnv_spi* F: pc-bios/skiboot.lid F: pc-bios/pnv-pnor.bin F: tests/qtest/pnv* -F: tests/functional/test_ppc64_powernv.py +F: tests/functional/ppc64/test_powernv.py pca955x M: Glenn Miles @@ -1608,7 +1608,7 @@ M: Edgar E. Iglesias L: qemu-ppc@nongnu.org S: Odd Fixes F: hw/ppc/virtex_ml507.c -F: tests/functional/test_ppc_virtex_ml507.py +F: tests/functional/ppc/test_virtex_ml507.py sam460ex M: BALATON Zoltan @@ -1624,7 +1624,7 @@ F: pc-bios/dtb/canyonlands.dt[sb] F: pc-bios/u-boot-sam460ex-20100605.bin F: roms/u-boot-sam460ex F: docs/system/ppc/amigang.rst -F: tests/functional/test_ppc_sam460ex.py +F: tests/functional/ppc/test_sam460ex.py pegasos2 M: BALATON Zoltan @@ -1642,7 +1642,7 @@ S: Maintained F: hw/ppc/amigaone.c F: hw/pci-host/articia.c F: include/hw/pci-host/articia.h -F: tests/functional/test_ppc_amiga.py +F: tests/functional/ppc/test_amiga.py Virtual Open Firmware (VOF) M: Alexey Kardashevskiy diff --git a/tests/functional/meson.build b/tests/functional/meson.build index 397303ec6f..3caeea5ebd 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -24,19 +24,8 @@ subdir('mipsel') subdir('mips64') subdir('mips64el') subdir('or1k') - -test_ppc_timeouts = { - 'ppc_40p' : 240, -} - -test_ppc64_timeouts = { - 'ppc64_hv' : 1000, - 'ppc64_powernv' : 480, - 'ppc64_pseries' : 480, - 'ppc64_replay' : 210, - 'ppc64_tuxrun' : 420, - 'ppc64_mac99' : 120, -} +subdir('ppc') +subdir('ppc64') test_riscv64_timeouts = { 'riscv64_tuxrun' : 120, @@ -71,38 +60,6 @@ tests_generic_linuxuser = [ tests_generic_bsduser = [ ] -tests_ppc_system_quick = [ - 'ppc_migration', - 'ppc_74xx', -] - -tests_ppc_system_thorough = [ - 'ppc_40p', - 'ppc_amiga', - 'ppc_bamboo', - 'ppc_mac', - 'ppc_mpc8544ds', - 'ppc_replay', - 'ppc_sam460ex', - 'ppc_tuxrun', - 'ppc_virtex_ml507', -] - -tests_ppc64_system_quick = [ - 'ppc64_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 = [ 'riscv32_migration', 'riscv_opensbi', diff --git a/tests/functional/ppc/meson.build b/tests/functional/ppc/meson.build new file mode 100644 index 0000000000..3d562010d8 --- /dev/null +++ b/tests/functional/ppc/meson.build @@ -0,0 +1,22 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +test_ppc_timeouts = { + '40p' : 240, +} + +tests_ppc_system_quick = [ + 'migration', + '74xx', +] + +tests_ppc_system_thorough = [ + '40p', + 'amiga', + 'bamboo', + 'mac', + 'mpc8544ds', + 'replay', + 'sam460ex', + 'tuxrun', + 'virtex_ml507', +] diff --git a/tests/functional/test_ppc_40p.py b/tests/functional/ppc/test_40p.py similarity index 100% rename from tests/functional/test_ppc_40p.py rename to tests/functional/ppc/test_40p.py diff --git a/tests/functional/test_ppc_74xx.py b/tests/functional/ppc/test_74xx.py similarity index 100% rename from tests/functional/test_ppc_74xx.py rename to tests/functional/ppc/test_74xx.py diff --git a/tests/functional/test_ppc_amiga.py b/tests/functional/ppc/test_amiga.py similarity index 100% rename from tests/functional/test_ppc_amiga.py rename to tests/functional/ppc/test_amiga.py diff --git a/tests/functional/test_ppc_bamboo.py b/tests/functional/ppc/test_bamboo.py similarity index 100% rename from tests/functional/test_ppc_bamboo.py rename to tests/functional/ppc/test_bamboo.py diff --git a/tests/functional/test_ppc_mac.py b/tests/functional/ppc/test_mac.py similarity index 100% rename from tests/functional/test_ppc_mac.py rename to tests/functional/ppc/test_mac.py diff --git a/tests/functional/test_ppc_migration.py b/tests/functional/ppc/test_migration.py similarity index 100% rename from tests/functional/test_ppc_migration.py rename to tests/functional/ppc/test_migration.py diff --git a/tests/functional/test_ppc_mpc8544ds.py b/tests/functional/ppc/test_mpc8544ds.py similarity index 100% rename from tests/functional/test_ppc_mpc8544ds.py rename to tests/functional/ppc/test_mpc8544ds.py diff --git a/tests/functional/test_ppc_replay.py b/tests/functional/ppc/test_replay.py similarity index 100% rename from tests/functional/test_ppc_replay.py rename to tests/functional/ppc/test_replay.py diff --git a/tests/functional/test_ppc_sam460ex.py b/tests/functional/ppc/test_sam460ex.py old mode 100644 new mode 100755 similarity index 100% rename from tests/functional/test_ppc_sam460ex.py rename to tests/functional/ppc/test_sam460ex.py diff --git a/tests/functional/test_ppc_tuxrun.py b/tests/functional/ppc/test_tuxrun.py similarity index 100% rename from tests/functional/test_ppc_tuxrun.py rename to tests/functional/ppc/test_tuxrun.py diff --git a/tests/functional/test_ppc_virtex_ml507.py b/tests/functional/ppc/test_virtex_ml507.py similarity index 100% rename from tests/functional/test_ppc_virtex_ml507.py rename to tests/functional/ppc/test_virtex_ml507.py diff --git a/tests/functional/ppc64/meson.build b/tests/functional/ppc64/meson.build new file mode 100644 index 0000000000..842fe0fc71 --- /dev/null +++ b/tests/functional/ppc64/meson.build @@ -0,0 +1,25 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +test_ppc64_timeouts = { + 'hv' : 1000, + 'mac99' : 120, + 'powernv' : 480, + 'pseries' : 480, + 'replay' : 210, + 'tuxrun' : 420, +} + +tests_ppc64_system_quick = [ + 'migration', +] + +tests_ppc64_system_thorough = [ + 'e500', + 'hv', + 'mac99', + 'powernv', + 'pseries', + 'replay', + 'reverse_debug', + 'tuxrun', +] diff --git a/tests/functional/test_ppc64_e500.py b/tests/functional/ppc64/test_e500.py similarity index 100% rename from tests/functional/test_ppc64_e500.py rename to tests/functional/ppc64/test_e500.py diff --git a/tests/functional/test_ppc64_hv.py b/tests/functional/ppc64/test_hv.py similarity index 100% rename from tests/functional/test_ppc64_hv.py rename to tests/functional/ppc64/test_hv.py diff --git a/tests/functional/test_ppc64_mac99.py b/tests/functional/ppc64/test_mac99.py similarity index 100% rename from tests/functional/test_ppc64_mac99.py rename to tests/functional/ppc64/test_mac99.py diff --git a/tests/functional/test_ppc64_migration.py b/tests/functional/ppc64/test_migration.py similarity index 100% rename from tests/functional/test_ppc64_migration.py rename to tests/functional/ppc64/test_migration.py diff --git a/tests/functional/test_ppc64_powernv.py b/tests/functional/ppc64/test_powernv.py similarity index 100% rename from tests/functional/test_ppc64_powernv.py rename to tests/functional/ppc64/test_powernv.py diff --git a/tests/functional/test_ppc64_pseries.py b/tests/functional/ppc64/test_pseries.py similarity index 100% rename from tests/functional/test_ppc64_pseries.py rename to tests/functional/ppc64/test_pseries.py diff --git a/tests/functional/test_ppc64_replay.py b/tests/functional/ppc64/test_replay.py similarity index 100% rename from tests/functional/test_ppc64_replay.py rename to tests/functional/ppc64/test_replay.py diff --git a/tests/functional/test_ppc64_reverse_debug.py b/tests/functional/ppc64/test_reverse_debug.py similarity index 100% rename from tests/functional/test_ppc64_reverse_debug.py rename to tests/functional/ppc64/test_reverse_debug.py diff --git a/tests/functional/test_ppc64_tuxrun.py b/tests/functional/ppc64/test_tuxrun.py similarity index 100% rename from tests/functional/test_ppc64_tuxrun.py rename to tests/functional/ppc64/test_tuxrun.py From e1a8572a8d7a11627db22366d356d0daf0f6d559 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 19 Aug 2025 13:23:54 +0200 Subject: [PATCH 0028/2396] tests/functional: Move riscv32/riscv64 tests into target-specific folders MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The opensbi test is used for both, riscv32 and riscv64. Copy the main test to the riscv64 folder and add a simple wrapper to the riscv32 folder to be able to run it for that target, too. Reviewed-by: Pierrick Bouvier Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Thomas Huth Message-ID: <20250819112403.432587-18-thuth@redhat.com> --- MAINTAINERS | 3 ++- tests/functional/meson.build | 25 ++----------------- tests/functional/riscv32/meson.build | 10 ++++++++ .../test_migration.py} | 0 tests/functional/riscv32/test_opensbi.py | 10 ++++++++ .../test_tuxrun.py} | 0 tests/functional/riscv64/meson.build | 15 +++++++++++ .../test_migration.py} | 0 .../test_opensbi.py} | 0 .../test_sifive_u.py} | 0 .../test_tuxrun.py} | 0 11 files changed, 39 insertions(+), 24 deletions(-) create mode 100644 tests/functional/riscv32/meson.build rename tests/functional/{test_riscv32_migration.py => riscv32/test_migration.py} (100%) create mode 100755 tests/functional/riscv32/test_opensbi.py rename tests/functional/{test_riscv32_tuxrun.py => riscv32/test_tuxrun.py} (100%) create mode 100644 tests/functional/riscv64/meson.build rename tests/functional/{test_riscv64_migration.py => riscv64/test_migration.py} (100%) rename tests/functional/{test_riscv_opensbi.py => riscv64/test_opensbi.py} (100%) rename tests/functional/{test_riscv64_sifive_u.py => riscv64/test_sifive_u.py} (100%) rename tests/functional/{test_riscv64_tuxrun.py => riscv64/test_tuxrun.py} (100%) diff --git a/MAINTAINERS b/MAINTAINERS index b0d440cf75..81262546c4 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -332,7 +332,8 @@ F: include/hw/riscv/ F: linux-user/host/riscv32/ F: linux-user/host/riscv64/ F: common-user/host/riscv* -F: tests/functional/test_riscv* +F: tests/functional/riscv32 +F: tests/functional/riscv64 F: tests/tcg/riscv64/ RISC-V XThead* extensions diff --git a/tests/functional/meson.build b/tests/functional/meson.build index 3caeea5ebd..2d8f67fd94 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -26,10 +26,8 @@ subdir('mips64el') subdir('or1k') subdir('ppc') subdir('ppc64') - -test_riscv64_timeouts = { - 'riscv64_tuxrun' : 120, -} +subdir('riscv32') +subdir('riscv64') test_s390x_timeouts = { 's390x_ccw_virtio' : 420, @@ -60,25 +58,6 @@ tests_generic_linuxuser = [ tests_generic_bsduser = [ ] -tests_riscv32_system_quick = [ - 'riscv32_migration', - 'riscv_opensbi', -] - -tests_riscv32_system_thorough = [ - 'riscv32_tuxrun', -] - -tests_riscv64_system_quick = [ - 'riscv64_migration', - 'riscv_opensbi', -] - -tests_riscv64_system_thorough = [ - 'riscv64_sifive_u', - 'riscv64_tuxrun', -] - tests_rx_system_thorough = [ 'rx_gdbsim', ] diff --git a/tests/functional/riscv32/meson.build b/tests/functional/riscv32/meson.build new file mode 100644 index 0000000000..f3ebbb8db5 --- /dev/null +++ b/tests/functional/riscv32/meson.build @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +tests_riscv32_system_quick = [ + 'migration', + 'opensbi', +] + +tests_riscv32_system_thorough = [ + 'tuxrun', +] diff --git a/tests/functional/test_riscv32_migration.py b/tests/functional/riscv32/test_migration.py similarity index 100% rename from tests/functional/test_riscv32_migration.py rename to tests/functional/riscv32/test_migration.py diff --git a/tests/functional/riscv32/test_opensbi.py b/tests/functional/riscv32/test_opensbi.py new file mode 100755 index 0000000000..d1ac706f0b --- /dev/null +++ b/tests/functional/riscv32/test_opensbi.py @@ -0,0 +1,10 @@ +#!/usr/bin/env python3 +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Reuse the 64-bit OpenSBI test for RISC-V 32-bit machines + +from riscv64.test_opensbi import RiscvOpenSBI + +if __name__ == '__main__': + RiscvOpenSBI.main() diff --git a/tests/functional/test_riscv32_tuxrun.py b/tests/functional/riscv32/test_tuxrun.py similarity index 100% rename from tests/functional/test_riscv32_tuxrun.py rename to tests/functional/riscv32/test_tuxrun.py diff --git a/tests/functional/riscv64/meson.build b/tests/functional/riscv64/meson.build new file mode 100644 index 0000000000..c1704d9275 --- /dev/null +++ b/tests/functional/riscv64/meson.build @@ -0,0 +1,15 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +test_riscv64_timeouts = { + 'tuxrun' : 120, +} + +tests_riscv64_system_quick = [ + 'migration', + 'opensbi', +] + +tests_riscv64_system_thorough = [ + 'sifive_u', + 'tuxrun', +] diff --git a/tests/functional/test_riscv64_migration.py b/tests/functional/riscv64/test_migration.py similarity index 100% rename from tests/functional/test_riscv64_migration.py rename to tests/functional/riscv64/test_migration.py diff --git a/tests/functional/test_riscv_opensbi.py b/tests/functional/riscv64/test_opensbi.py similarity index 100% rename from tests/functional/test_riscv_opensbi.py rename to tests/functional/riscv64/test_opensbi.py diff --git a/tests/functional/test_riscv64_sifive_u.py b/tests/functional/riscv64/test_sifive_u.py similarity index 100% rename from tests/functional/test_riscv64_sifive_u.py rename to tests/functional/riscv64/test_sifive_u.py diff --git a/tests/functional/test_riscv64_tuxrun.py b/tests/functional/riscv64/test_tuxrun.py similarity index 100% rename from tests/functional/test_riscv64_tuxrun.py rename to tests/functional/riscv64/test_tuxrun.py From bbb43ea3be12f09c284954b7b51ae8fea85ebe33 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 19 Aug 2025 13:23:55 +0200 Subject: [PATCH 0029/2396] tests/functional: Move rx test into target-specific folders MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move the architecture specific test into an architecture specific subdirectory. Reviewed-by: Pierrick Bouvier Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Thomas Huth Message-ID: <20250819112403.432587-19-thuth@redhat.com> --- MAINTAINERS | 2 +- tests/functional/meson.build | 5 +---- tests/functional/rx/meson.build | 5 +++++ tests/functional/{test_rx_gdbsim.py => rx/test_gdbsim.py} | 0 4 files changed, 7 insertions(+), 5 deletions(-) create mode 100644 tests/functional/rx/meson.build rename tests/functional/{test_rx_gdbsim.py => rx/test_gdbsim.py} (100%) diff --git a/MAINTAINERS b/MAINTAINERS index 81262546c4..c6410a5f5f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1721,7 +1721,7 @@ R: Yoshinori Sato S: Orphan F: docs/system/target-rx.rst F: hw/rx/rx-gdbsim.c -F: tests/functional/test_rx_gdbsim.py +F: tests/functional/rx/test_gdbsim.py SH4 Machines ------------ diff --git a/tests/functional/meson.build b/tests/functional/meson.build index 2d8f67fd94..7e7a6aa0c9 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -28,6 +28,7 @@ subdir('ppc') subdir('ppc64') subdir('riscv32') subdir('riscv64') +subdir('rx') test_s390x_timeouts = { 's390x_ccw_virtio' : 420, @@ -58,10 +59,6 @@ tests_generic_linuxuser = [ tests_generic_bsduser = [ ] -tests_rx_system_thorough = [ - 'rx_gdbsim', -] - tests_s390x_system_thorough = [ 's390x_ccw_virtio', 's390x_pxelinux', diff --git a/tests/functional/rx/meson.build b/tests/functional/rx/meson.build new file mode 100644 index 0000000000..6af83a9f23 --- /dev/null +++ b/tests/functional/rx/meson.build @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +tests_rx_system_thorough = [ + 'gdbsim', +] diff --git a/tests/functional/test_rx_gdbsim.py b/tests/functional/rx/test_gdbsim.py similarity index 100% rename from tests/functional/test_rx_gdbsim.py rename to tests/functional/rx/test_gdbsim.py From 5e50a3c9c0f31daf53962914855accdc97a3109b Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 19 Aug 2025 13:23:56 +0200 Subject: [PATCH 0030/2396] tests/functional: Move s390x tests into target-specific folders MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The tests/functional folder has become quite crowded, thus move the s390x tests into a target-specific subfolder. Reviewed-by: Pierrick Bouvier Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Thomas Huth Message-ID: <20250819112403.432587-20-thuth@redhat.com> --- MAINTAINERS | 6 +++--- tests/functional/meson.build | 13 +------------ tests/functional/s390x/meson.build | 13 +++++++++++++ .../test_ccw_virtio.py} | 0 .../test_pxelinux.py} | 0 .../{test_s390x_replay.py => s390x/test_replay.py} | 0 .../test_topology.py} | 0 .../{test_s390x_tuxrun.py => s390x/test_tuxrun.py} | 0 8 files changed, 17 insertions(+), 15 deletions(-) create mode 100644 tests/functional/s390x/meson.build rename tests/functional/{test_s390x_ccw_virtio.py => s390x/test_ccw_virtio.py} (100%) rename tests/functional/{test_s390x_pxelinux.py => s390x/test_pxelinux.py} (100%) rename tests/functional/{test_s390x_replay.py => s390x/test_replay.py} (100%) rename tests/functional/{test_s390x_topology.py => s390x/test_topology.py} (100%) rename tests/functional/{test_s390x_tuxrun.py => s390x/test_tuxrun.py} (100%) diff --git a/MAINTAINERS b/MAINTAINERS index c6410a5f5f..4a55a20f6a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1797,7 +1797,7 @@ S: Supported F: hw/s390x/ F: include/hw/s390x/ F: configs/devices/s390x-softmmu/default.mak -F: tests/functional/test_s390x_* +F: tests/functional/s390x T: git https://github.com/borntraeger/qemu.git s390-next L: qemu-s390x@nongnu.org @@ -1811,7 +1811,7 @@ F: hw/s390x/ipl.* F: pc-bios/s390-ccw/ F: pc-bios/s390-ccw.img F: docs/devel/s390-dasd-ipl.rst -F: tests/functional/test_s390x_pxelinux.py +F: tests/functional/s390x/test_pxelinux.py T: git https://github.com/borntraeger/qemu.git s390-next L: qemu-s390x@nongnu.org @@ -1865,7 +1865,7 @@ F: hw/s390x/cpu-topology.c F: target/s390x/kvm/stsi-topology.c F: docs/devel/s390-cpu-topology.rst F: docs/system/s390x/cpu-topology.rst -F: tests/functional/test_s390x_topology.py +F: tests/functional/s390x/test_topology.py X86 Machines ------------ diff --git a/tests/functional/meson.build b/tests/functional/meson.build index 7e7a6aa0c9..abaa4e00fc 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -29,10 +29,7 @@ subdir('ppc64') subdir('riscv32') subdir('riscv64') subdir('rx') - -test_s390x_timeouts = { - 's390x_ccw_virtio' : 420, -} +subdir('s390x') test_sh4_timeouts = { 'sh4_tuxrun' : 240, @@ -59,14 +56,6 @@ tests_generic_linuxuser = [ tests_generic_bsduser = [ ] -tests_s390x_system_thorough = [ - 's390x_ccw_virtio', - 's390x_pxelinux', - 's390x_replay', - 's390x_topology', - 's390x_tuxrun', -] - tests_sh4_system_thorough = [ 'sh4_r2d', 'sh4_tuxrun', diff --git a/tests/functional/s390x/meson.build b/tests/functional/s390x/meson.build new file mode 100644 index 0000000000..030b116039 --- /dev/null +++ b/tests/functional/s390x/meson.build @@ -0,0 +1,13 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +test_s390x_timeouts = { + 'ccw_virtio' : 420, +} + +tests_s390x_system_thorough = [ + 'ccw_virtio', + 'pxelinux', + 'replay', + 'topology', + 'tuxrun', +] diff --git a/tests/functional/test_s390x_ccw_virtio.py b/tests/functional/s390x/test_ccw_virtio.py similarity index 100% rename from tests/functional/test_s390x_ccw_virtio.py rename to tests/functional/s390x/test_ccw_virtio.py diff --git a/tests/functional/test_s390x_pxelinux.py b/tests/functional/s390x/test_pxelinux.py similarity index 100% rename from tests/functional/test_s390x_pxelinux.py rename to tests/functional/s390x/test_pxelinux.py diff --git a/tests/functional/test_s390x_replay.py b/tests/functional/s390x/test_replay.py similarity index 100% rename from tests/functional/test_s390x_replay.py rename to tests/functional/s390x/test_replay.py diff --git a/tests/functional/test_s390x_topology.py b/tests/functional/s390x/test_topology.py similarity index 100% rename from tests/functional/test_s390x_topology.py rename to tests/functional/s390x/test_topology.py diff --git a/tests/functional/test_s390x_tuxrun.py b/tests/functional/s390x/test_tuxrun.py similarity index 100% rename from tests/functional/test_s390x_tuxrun.py rename to tests/functional/s390x/test_tuxrun.py From 58e95a05dec8bb0c739a2b7192b6d0a96d1a99a5 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 19 Aug 2025 13:23:57 +0200 Subject: [PATCH 0031/2396] tests/functional: Move sh4/sh4eb tests into target-specific folders MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The tests/functional folder has become quite crowded, thus move the sh4 tests into a target-specific subfolder. Reviewed-by: Pierrick Bouvier Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Thomas Huth Message-ID: <20250819112403.432587-21-thuth@redhat.com> --- MAINTAINERS | 4 ++-- tests/functional/meson.build | 15 ++------------- tests/functional/sh4/meson.build | 10 ++++++++++ .../{test_sh4_r2d.py => sh4/test_r2d.py} | 0 .../{test_sh4_tuxrun.py => sh4/test_tuxrun.py} | 0 tests/functional/sh4eb/meson.build | 5 +++++ .../{test_sh4eb_r2d.py => sh4eb/test_r2d.py} | 0 7 files changed, 19 insertions(+), 15 deletions(-) create mode 100644 tests/functional/sh4/meson.build rename tests/functional/{test_sh4_r2d.py => sh4/test_r2d.py} (100%) rename tests/functional/{test_sh4_tuxrun.py => sh4/test_tuxrun.py} (100%) create mode 100644 tests/functional/sh4eb/meson.build rename tests/functional/{test_sh4eb_r2d.py => sh4eb/test_r2d.py} (100%) diff --git a/MAINTAINERS b/MAINTAINERS index 4a55a20f6a..eddec0058e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1736,8 +1736,8 @@ F: hw/pci-host/sh_pci.c F: hw/timer/sh_timer.c F: include/hw/sh4/sh_intc.h F: include/hw/timer/tmu012.h -F: tests/functional/test_sh4*_r2d.py -F: tests/functional/test_sh4_tuxrun.py +F: tests/functional/sh4*/test_r2d.py +F: tests/functional/sh4/test_tuxrun.py SPARC Machines -------------- diff --git a/tests/functional/meson.build b/tests/functional/meson.build index abaa4e00fc..ce713509e3 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -30,10 +30,8 @@ subdir('riscv32') subdir('riscv64') subdir('rx') subdir('s390x') - -test_sh4_timeouts = { - 'sh4_tuxrun' : 240, -} +subdir('sh4') +subdir('sh4eb') test_x86_64_timeouts = { 'acpi_bits' : 420, @@ -56,15 +54,6 @@ tests_generic_linuxuser = [ tests_generic_bsduser = [ ] -tests_sh4_system_thorough = [ - 'sh4_r2d', - 'sh4_tuxrun', -] - -tests_sh4eb_system_thorough = [ - 'sh4eb_r2d', -] - tests_sparc_system_quick = [ 'sparc_migration', ] diff --git a/tests/functional/sh4/meson.build b/tests/functional/sh4/meson.build new file mode 100644 index 0000000000..56f824e1e7 --- /dev/null +++ b/tests/functional/sh4/meson.build @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +test_sh4_timeouts = { + 'tuxrun' : 240, +} + +tests_sh4_system_thorough = [ + 'r2d', + 'tuxrun', +] diff --git a/tests/functional/test_sh4_r2d.py b/tests/functional/sh4/test_r2d.py similarity index 100% rename from tests/functional/test_sh4_r2d.py rename to tests/functional/sh4/test_r2d.py diff --git a/tests/functional/test_sh4_tuxrun.py b/tests/functional/sh4/test_tuxrun.py similarity index 100% rename from tests/functional/test_sh4_tuxrun.py rename to tests/functional/sh4/test_tuxrun.py diff --git a/tests/functional/sh4eb/meson.build b/tests/functional/sh4eb/meson.build new file mode 100644 index 0000000000..25e9a6e404 --- /dev/null +++ b/tests/functional/sh4eb/meson.build @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +tests_sh4eb_system_thorough = [ + 'r2d', +] diff --git a/tests/functional/test_sh4eb_r2d.py b/tests/functional/sh4eb/test_r2d.py similarity index 100% rename from tests/functional/test_sh4eb_r2d.py rename to tests/functional/sh4eb/test_r2d.py From e76291a65457b1fe18b6bf7a90b1d022fa15bf9d Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 19 Aug 2025 13:23:58 +0200 Subject: [PATCH 0032/2396] tests/functional: Move sparc/sparc64 tests into target-specific folders MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The tests/functional folder has become quite crowded, thus move the sparc tests into a target-specific subfolder. Reviewed-by: Pierrick Bouvier Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Thomas Huth Message-ID: <20250819112403.432587-22-thuth@redhat.com> --- MAINTAINERS | 6 +++--- tests/functional/meson.build | 20 ++----------------- tests/functional/sparc/meson.build | 10 ++++++++++ .../test_migration.py} | 0 .../test_replay.py} | 0 .../test_sun4m.py} | 0 tests/functional/sparc64/meson.build | 10 ++++++++++ .../test_migration.py} | 0 .../test_sun4u.py} | 0 .../test_tuxrun.py} | 0 10 files changed, 25 insertions(+), 21 deletions(-) create mode 100644 tests/functional/sparc/meson.build rename tests/functional/{test_sparc_migration.py => sparc/test_migration.py} (100%) rename tests/functional/{test_sparc_replay.py => sparc/test_replay.py} (100%) rename tests/functional/{test_sparc_sun4m.py => sparc/test_sun4m.py} (100%) create mode 100644 tests/functional/sparc64/meson.build rename tests/functional/{test_sparc64_migration.py => sparc64/test_migration.py} (100%) rename tests/functional/{test_sparc64_sun4u.py => sparc64/test_sun4u.py} (100%) rename tests/functional/{test_sparc64_tuxrun.py => sparc64/test_tuxrun.py} (100%) diff --git a/MAINTAINERS b/MAINTAINERS index eddec0058e..b46445ff5c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1755,7 +1755,7 @@ F: include/hw/nvram/sun_nvram.h F: include/hw/sparc/sparc32_dma.h F: include/hw/sparc/sun4m_iommu.h F: pc-bios/openbios-sparc32 -F: tests/functional/test_sparc_sun4m.py +F: tests/functional/sparc/test_sun4m.py Sun4u M: Mark Cave-Ayland @@ -1768,8 +1768,8 @@ F: include/hw/pci-host/sabre.h F: hw/pci-bridge/simba.c F: include/hw/pci-bridge/simba.h F: pc-bios/openbios-sparc64 -F: tests/functional/test_sparc64_sun4u.py -F: tests/functional/test_sparc64_tuxrun.py +F: tests/functional/sparc64/test_sun4u.py +F: tests/functional/sparc64/test_tuxrun.py Sun4v M: Artyom Tarasenko diff --git a/tests/functional/meson.build b/tests/functional/meson.build index ce713509e3..00d18dba3c 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -32,6 +32,8 @@ subdir('rx') subdir('s390x') subdir('sh4') subdir('sh4eb') +subdir('sparc') +subdir('sparc64') test_x86_64_timeouts = { 'acpi_bits' : 420, @@ -54,24 +56,6 @@ tests_generic_linuxuser = [ tests_generic_bsduser = [ ] -tests_sparc_system_quick = [ - 'sparc_migration', -] - -tests_sparc_system_thorough = [ - 'sparc_replay', - 'sparc_sun4m', -] - -tests_sparc64_system_quick = [ - 'sparc64_migration', -] - -tests_sparc64_system_thorough = [ - 'sparc64_sun4u', - 'sparc64_tuxrun', -] - tests_x86_64_system_quick = [ 'cpu_queries', 'mem_addr_space', diff --git a/tests/functional/sparc/meson.build b/tests/functional/sparc/meson.build new file mode 100644 index 0000000000..88732becd8 --- /dev/null +++ b/tests/functional/sparc/meson.build @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +tests_sparc_system_quick = [ + 'migration', +] + +tests_sparc_system_thorough = [ + 'replay', + 'sun4m', +] diff --git a/tests/functional/test_sparc_migration.py b/tests/functional/sparc/test_migration.py similarity index 100% rename from tests/functional/test_sparc_migration.py rename to tests/functional/sparc/test_migration.py diff --git a/tests/functional/test_sparc_replay.py b/tests/functional/sparc/test_replay.py similarity index 100% rename from tests/functional/test_sparc_replay.py rename to tests/functional/sparc/test_replay.py diff --git a/tests/functional/test_sparc_sun4m.py b/tests/functional/sparc/test_sun4m.py similarity index 100% rename from tests/functional/test_sparc_sun4m.py rename to tests/functional/sparc/test_sun4m.py diff --git a/tests/functional/sparc64/meson.build b/tests/functional/sparc64/meson.build new file mode 100644 index 0000000000..2e04e7d4f3 --- /dev/null +++ b/tests/functional/sparc64/meson.build @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +tests_sparc64_system_quick = [ + 'migration', +] + +tests_sparc64_system_thorough = [ + 'sun4u', + 'tuxrun', +] diff --git a/tests/functional/test_sparc64_migration.py b/tests/functional/sparc64/test_migration.py similarity index 100% rename from tests/functional/test_sparc64_migration.py rename to tests/functional/sparc64/test_migration.py diff --git a/tests/functional/test_sparc64_sun4u.py b/tests/functional/sparc64/test_sun4u.py similarity index 100% rename from tests/functional/test_sparc64_sun4u.py rename to tests/functional/sparc64/test_sun4u.py diff --git a/tests/functional/test_sparc64_tuxrun.py b/tests/functional/sparc64/test_tuxrun.py similarity index 100% rename from tests/functional/test_sparc64_tuxrun.py rename to tests/functional/sparc64/test_tuxrun.py From 1917d47dd78adbd30abd462712458d0a0b583308 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 19 Aug 2025 13:23:59 +0200 Subject: [PATCH 0033/2396] tests/functional: Move x86_64 tests into target-specific folder The tests/functional folder has become quite crowded, thus move the x86_64 tests into a target-specific subfolder. Reviewed-by: Pierrick Bouvier Signed-off-by: Thomas Huth Message-ID: <20250819112403.432587-23-thuth@redhat.com> --- MAINTAINERS | 32 ++++++++-------- tests/functional/meson.build | 37 +------------------ tests/functional/x86_64/meson.build | 37 +++++++++++++++++++ .../functional/{ => x86_64}/test_acpi_bits.py | 0 .../test_cpu_model_versions.py} | 0 .../{ => x86_64}/test_cpu_queries.py | 0 .../test_hotplug_blk.py} | 0 .../test_hotplug_cpu.py} | 0 .../{ => x86_64}/test_intel_iommu.py | 0 .../test_kvm_xen.py} | 0 .../{ => x86_64}/test_linux_initrd.py | 0 .../{ => x86_64}/test_mem_addr_space.py | 0 tests/functional/{ => x86_64}/test_memlock.py | 0 .../test_migration.py} | 0 .../test_multiprocess.py} | 0 .../{ => x86_64}/test_netdev_ethtool.py | 0 .../{ => x86_64}/test_pc_cpu_hotplug_props.py | 0 .../test_replay.py} | 0 .../test_reverse_debug.py} | 0 .../test_tuxrun.py} | 0 .../{ => x86_64}/test_virtio_balloon.py | 0 .../{ => x86_64}/test_virtio_gpu.py | 0 .../{ => x86_64}/test_virtio_version.py | 0 23 files changed, 55 insertions(+), 51 deletions(-) create mode 100644 tests/functional/x86_64/meson.build rename tests/functional/{ => x86_64}/test_acpi_bits.py (100%) rename tests/functional/{test_x86_cpu_model_versions.py => x86_64/test_cpu_model_versions.py} (100%) rename tests/functional/{ => x86_64}/test_cpu_queries.py (100%) rename tests/functional/{test_x86_64_hotplug_blk.py => x86_64/test_hotplug_blk.py} (100%) rename tests/functional/{test_x86_64_hotplug_cpu.py => x86_64/test_hotplug_cpu.py} (100%) rename tests/functional/{ => x86_64}/test_intel_iommu.py (100%) rename tests/functional/{test_x86_64_kvm_xen.py => x86_64/test_kvm_xen.py} (100%) rename tests/functional/{ => x86_64}/test_linux_initrd.py (100%) rename tests/functional/{ => x86_64}/test_mem_addr_space.py (100%) rename tests/functional/{ => x86_64}/test_memlock.py (100%) rename tests/functional/{test_x86_64_migration.py => x86_64/test_migration.py} (100%) rename tests/functional/{test_x86_64_multiprocess.py => x86_64/test_multiprocess.py} (100%) rename tests/functional/{ => x86_64}/test_netdev_ethtool.py (100%) rename tests/functional/{ => x86_64}/test_pc_cpu_hotplug_props.py (100%) rename tests/functional/{test_x86_64_replay.py => x86_64/test_replay.py} (100%) rename tests/functional/{test_x86_64_reverse_debug.py => x86_64/test_reverse_debug.py} (100%) rename tests/functional/{test_x86_64_tuxrun.py => x86_64/test_tuxrun.py} (100%) rename tests/functional/{ => x86_64}/test_virtio_balloon.py (100%) rename tests/functional/{ => x86_64}/test_virtio_gpu.py (100%) rename tests/functional/{ => x86_64}/test_virtio_version.py (100%) diff --git a/MAINTAINERS b/MAINTAINERS index b46445ff5c..7b1a94f696 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -147,6 +147,7 @@ F: target/i386/Kconfig F: target/i386/meson.build F: tools/i386/ F: tests/functional/i386/ +F: tests/functional/x86_64/ Guest CPU cores (TCG) --------------------- @@ -483,7 +484,7 @@ F: docs/system/i386/sgx.rst F: target/i386/kvm/ F: target/i386/sev* F: scripts/kvm/vmxcap -F: tests/functional/test_x86_64_hotplug_cpu.py +F: tests/functional/x86_64/test_hotplug_cpu.py Xen emulation on X86 KVM CPUs M: David Woodhouse @@ -492,7 +493,7 @@ S: Supported F: include/system/kvm_xen.h F: target/i386/kvm/xen* F: hw/i386/kvm/xen* -F: tests/functional/test_x86_64_kvm_xen.py +F: tests/functional/x86_64/test_kvm_xen.py Guest CPU Cores (other accelerators) ------------------------------------ @@ -1894,11 +1895,11 @@ F: include/hw/isa/apm.h F: tests/unit/test-x86-topo.c F: tests/qtest/test-x86-cpuid-compat.c F: tests/functional/i386/test_tuxrun.py -F: tests/functional/test_linux_initrd.py -F: tests/functional/test_mem_addr_space.py -F: tests/functional/test_pc_cpu_hotplug_props.py -F: tests/functional/test_x86_64_tuxrun.py -F: tests/functional/test_x86_cpu_model_versions.py +F: tests/functional/x86_64/test_linux_initrd.py +F: tests/functional/x86_64/test_mem_addr_space.py +F: tests/functional/x86_64/test_pc_cpu_hotplug_props.py +F: tests/functional/x86_64/test_tuxrun.py +F: tests/functional/x86_64/test_cpu_model_versions.py PC Chipset M: Michael S. Tsirkin @@ -1974,7 +1975,7 @@ F: include/hw/boards.h F: include/hw/core/cpu.h F: include/hw/cpu/cluster.h F: include/system/numa.h -F: tests/functional/test_cpu_queries.py +F: tests/functional/x86_64/test_cpu_queries.py F: tests/functional/test_empty_cpu_model.py F: tests/unit/test-smp-parse.c T: git https://gitlab.com/ehabkost/qemu.git machine-next @@ -2159,7 +2160,7 @@ M: Ani Sinha M: Michael S. Tsirkin S: Supported F: tests/functional/acpi-bits/* -F: tests/functional/test_acpi_bits.py +F: tests/functional/x86_64/test_acpi_bits.py F: docs/devel/testing/acpi-bits.rst ACPI/HEST/GHES @@ -2345,7 +2346,7 @@ F: net/vhost-user.c F: include/hw/virtio/ F: docs/devel/virtio* F: docs/devel/migration/virtio.rst -F: tests/functional/test_virtio_version.py +F: tests/functional/x86_64/test_virtio_version.py virtio-balloon M: Michael S. Tsirkin @@ -2357,7 +2358,7 @@ F: include/hw/virtio/virtio-balloon.h F: system/balloon.c F: include/system/balloon.h F: tests/qtest/virtio-balloon-test.c -F: tests/functional/test_virtio_balloon.py +F: tests/functional/x86_64/test_virtio_balloon.py virtio-9p M: Christian Schoenebeck @@ -2380,7 +2381,7 @@ F: hw/block/virtio-blk.c F: hw/block/dataplane/* F: include/hw/virtio/virtio-blk-common.h F: tests/qtest/virtio-blk-test.c -F: tests/functional/test_x86_64_hotplug_blk.py +F: tests/functional/x86_64/test_hotplug_blk.py T: git https://github.com/stefanha/qemu.git block virtio-ccw @@ -2604,7 +2605,7 @@ R: Sriram Yagnaraman S: Odd Fixes F: docs/system/devices/igb.rst F: hw/net/igb* -F: tests/functional/test_netdev_ethtool.py +F: tests/functional/x86_64/test_netdev_ethtool.py F: tests/qtest/igb-test.c F: tests/qtest/libqos/igb.c @@ -2713,6 +2714,7 @@ F: hw/display/virtio-vga.* F: include/hw/virtio/virtio-gpu.h F: docs/system/devices/virtio-gpu.rst F: tests/functional/aarch64/test_virt_gpu.py +F: tests/functional/x86_64/test_virtio_gpu.py vhost-user-blk M: Raphael Norwitz @@ -3856,7 +3858,7 @@ S: Supported F: hw/i386/intel_iommu.c F: hw/i386/intel_iommu_internal.h F: include/hw/i386/intel_iommu.h -F: tests/functional/test_intel_iommu.py +F: tests/functional/x86_64/test_intel_iommu.py F: tests/qtest/intel-iommu-test.c AMD-Vi Emulation @@ -4330,7 +4332,7 @@ F: scripts/ci/ F: tests/docker/ F: tests/vm/ F: tests/lcitool/ -F: tests/functional/test_*_tuxrun.py +F: tests/functional/*/test_tuxrun.py F: scripts/archive-source.sh F: docs/devel/testing/ci* F: docs/devel/testing/main.rst diff --git a/tests/functional/meson.build b/tests/functional/meson.build index 00d18dba3c..34e30239a6 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -34,15 +34,7 @@ subdir('sh4') subdir('sh4eb') subdir('sparc') subdir('sparc64') - -test_x86_64_timeouts = { - 'acpi_bits' : 420, - 'intel_iommu': 300, - 'netdev_ethtool' : 180, - 'virtio_balloon': 120, - 'x86_64_kvm_xen' : 180, - 'x86_64_replay' : 480, -} +subdir('x86_64') tests_generic_system = [ 'empty_cpu_model', @@ -56,33 +48,6 @@ tests_generic_linuxuser = [ tests_generic_bsduser = [ ] -tests_x86_64_system_quick = [ - 'cpu_queries', - 'mem_addr_space', - 'x86_64_migration', - 'pc_cpu_hotplug_props', - 'virtio_version', - 'x86_cpu_model_versions', - 'vnc', - 'memlock', -] - -tests_x86_64_system_thorough = [ - 'acpi_bits', - 'intel_iommu', - 'linux_initrd', - 'x86_64_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', diff --git a/tests/functional/x86_64/meson.build b/tests/functional/x86_64/meson.build new file mode 100644 index 0000000000..696a9ecab4 --- /dev/null +++ b/tests/functional/x86_64/meson.build @@ -0,0 +1,37 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +test_x86_64_timeouts = { + 'acpi_bits' : 420, + 'intel_iommu': 300, + 'kvm_xen' : 180, + 'netdev_ethtool' : 180, + 'replay' : 480, + 'virtio_balloon': 120, +} + +tests_x86_64_system_quick = [ + 'cpu_model_versions', + 'cpu_queries', + 'mem_addr_space', + 'migration', + 'pc_cpu_hotplug_props', + 'virtio_version', + 'vnc', + 'memlock', +] + +tests_x86_64_system_thorough = [ + 'acpi_bits', + 'hotplug_blk', + 'hotplug_cpu', + 'intel_iommu', + 'kvm_xen', + 'linux_initrd', + 'multiprocess', + 'netdev_ethtool', + 'replay', + 'reverse_debug', + 'tuxrun', + 'virtio_balloon', + 'virtio_gpu', +] diff --git a/tests/functional/test_acpi_bits.py b/tests/functional/x86_64/test_acpi_bits.py similarity index 100% rename from tests/functional/test_acpi_bits.py rename to tests/functional/x86_64/test_acpi_bits.py diff --git a/tests/functional/test_x86_cpu_model_versions.py b/tests/functional/x86_64/test_cpu_model_versions.py similarity index 100% rename from tests/functional/test_x86_cpu_model_versions.py rename to tests/functional/x86_64/test_cpu_model_versions.py diff --git a/tests/functional/test_cpu_queries.py b/tests/functional/x86_64/test_cpu_queries.py similarity index 100% rename from tests/functional/test_cpu_queries.py rename to tests/functional/x86_64/test_cpu_queries.py diff --git a/tests/functional/test_x86_64_hotplug_blk.py b/tests/functional/x86_64/test_hotplug_blk.py similarity index 100% rename from tests/functional/test_x86_64_hotplug_blk.py rename to tests/functional/x86_64/test_hotplug_blk.py diff --git a/tests/functional/test_x86_64_hotplug_cpu.py b/tests/functional/x86_64/test_hotplug_cpu.py similarity index 100% rename from tests/functional/test_x86_64_hotplug_cpu.py rename to tests/functional/x86_64/test_hotplug_cpu.py diff --git a/tests/functional/test_intel_iommu.py b/tests/functional/x86_64/test_intel_iommu.py similarity index 100% rename from tests/functional/test_intel_iommu.py rename to tests/functional/x86_64/test_intel_iommu.py diff --git a/tests/functional/test_x86_64_kvm_xen.py b/tests/functional/x86_64/test_kvm_xen.py similarity index 100% rename from tests/functional/test_x86_64_kvm_xen.py rename to tests/functional/x86_64/test_kvm_xen.py diff --git a/tests/functional/test_linux_initrd.py b/tests/functional/x86_64/test_linux_initrd.py similarity index 100% rename from tests/functional/test_linux_initrd.py rename to tests/functional/x86_64/test_linux_initrd.py diff --git a/tests/functional/test_mem_addr_space.py b/tests/functional/x86_64/test_mem_addr_space.py similarity index 100% rename from tests/functional/test_mem_addr_space.py rename to tests/functional/x86_64/test_mem_addr_space.py diff --git a/tests/functional/test_memlock.py b/tests/functional/x86_64/test_memlock.py similarity index 100% rename from tests/functional/test_memlock.py rename to tests/functional/x86_64/test_memlock.py diff --git a/tests/functional/test_x86_64_migration.py b/tests/functional/x86_64/test_migration.py similarity index 100% rename from tests/functional/test_x86_64_migration.py rename to tests/functional/x86_64/test_migration.py diff --git a/tests/functional/test_x86_64_multiprocess.py b/tests/functional/x86_64/test_multiprocess.py similarity index 100% rename from tests/functional/test_x86_64_multiprocess.py rename to tests/functional/x86_64/test_multiprocess.py diff --git a/tests/functional/test_netdev_ethtool.py b/tests/functional/x86_64/test_netdev_ethtool.py similarity index 100% rename from tests/functional/test_netdev_ethtool.py rename to tests/functional/x86_64/test_netdev_ethtool.py diff --git a/tests/functional/test_pc_cpu_hotplug_props.py b/tests/functional/x86_64/test_pc_cpu_hotplug_props.py similarity index 100% rename from tests/functional/test_pc_cpu_hotplug_props.py rename to tests/functional/x86_64/test_pc_cpu_hotplug_props.py diff --git a/tests/functional/test_x86_64_replay.py b/tests/functional/x86_64/test_replay.py similarity index 100% rename from tests/functional/test_x86_64_replay.py rename to tests/functional/x86_64/test_replay.py diff --git a/tests/functional/test_x86_64_reverse_debug.py b/tests/functional/x86_64/test_reverse_debug.py similarity index 100% rename from tests/functional/test_x86_64_reverse_debug.py rename to tests/functional/x86_64/test_reverse_debug.py diff --git a/tests/functional/test_x86_64_tuxrun.py b/tests/functional/x86_64/test_tuxrun.py similarity index 100% rename from tests/functional/test_x86_64_tuxrun.py rename to tests/functional/x86_64/test_tuxrun.py diff --git a/tests/functional/test_virtio_balloon.py b/tests/functional/x86_64/test_virtio_balloon.py similarity index 100% rename from tests/functional/test_virtio_balloon.py rename to tests/functional/x86_64/test_virtio_balloon.py diff --git a/tests/functional/test_virtio_gpu.py b/tests/functional/x86_64/test_virtio_gpu.py similarity index 100% rename from tests/functional/test_virtio_gpu.py rename to tests/functional/x86_64/test_virtio_gpu.py diff --git a/tests/functional/test_virtio_version.py b/tests/functional/x86_64/test_virtio_version.py similarity index 100% rename from tests/functional/test_virtio_version.py rename to tests/functional/x86_64/test_virtio_version.py From e365d26e42281aae3dcb47aa63c862892efadb0c Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 19 Aug 2025 13:24:00 +0200 Subject: [PATCH 0034/2396] tests/functional: Move xtensa tests into target-specific folder MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The tests/functional folder has become quite crowded, thus move the xtensa tests into a target-specific subfolder. Reviewed-by: Pierrick Bouvier Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Thomas Huth Message-ID: <20250819112403.432587-24-thuth@redhat.com> --- MAINTAINERS | 2 +- tests/functional/meson.build | 6 +----- tests/functional/xtensa/meson.build | 6 ++++++ .../functional/{test_xtensa_lx60.py => xtensa/test_lx60.py} | 0 .../{test_xtensa_replay.py => xtensa/test_replay.py} | 0 5 files changed, 8 insertions(+), 6 deletions(-) create mode 100644 tests/functional/xtensa/meson.build rename tests/functional/{test_xtensa_lx60.py => xtensa/test_lx60.py} (100%) rename tests/functional/{test_xtensa_replay.py => xtensa/test_replay.py} (100%) diff --git a/MAINTAINERS b/MAINTAINERS index 7b1a94f696..792d2d6f2d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2005,7 +2005,7 @@ S: Maintained F: hw/xtensa/xtfpga.c F: hw/net/opencores_eth.c F: include/hw/xtensa/mx_pic.h -F: tests/functional/test_xtensa_lx60.py +F: tests/functional/xtensa/test_lx60.py Devices ------- diff --git a/tests/functional/meson.build b/tests/functional/meson.build index 34e30239a6..b1eec16add 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -35,6 +35,7 @@ subdir('sh4eb') subdir('sparc') subdir('sparc64') subdir('x86_64') +subdir('xtensa') tests_generic_system = [ 'empty_cpu_model', @@ -48,11 +49,6 @@ tests_generic_linuxuser = [ tests_generic_bsduser = [ ] -tests_xtensa_system_thorough = [ - 'xtensa_lx60', - 'xtensa_replay', -] - precache_all = [] foreach speed : ['quick', 'thorough'] foreach dir : target_dirs diff --git a/tests/functional/xtensa/meson.build b/tests/functional/xtensa/meson.build new file mode 100644 index 0000000000..d61d82a135 --- /dev/null +++ b/tests/functional/xtensa/meson.build @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +tests_xtensa_system_thorough = [ + 'lx60', + 'replay', +] diff --git a/tests/functional/test_xtensa_lx60.py b/tests/functional/xtensa/test_lx60.py similarity index 100% rename from tests/functional/test_xtensa_lx60.py rename to tests/functional/xtensa/test_lx60.py diff --git a/tests/functional/test_xtensa_replay.py b/tests/functional/xtensa/test_replay.py similarity index 100% rename from tests/functional/test_xtensa_replay.py rename to tests/functional/xtensa/test_replay.py From 0137f60b37c5678e9fa4971fd7e4f07afed33294 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 19 Aug 2025 13:24:01 +0200 Subject: [PATCH 0035/2396] tests/functional: Move the generic tests to a subfolder This also removes the line for using tests from the main folder since we do not have any tests left here. And while we're at it, also mark the vnc test as generic now since it is not specific to x86. Reviewed-by: Pierrick Bouvier Signed-off-by: Thomas Huth Message-ID: <20250819112403.432587-25-thuth@redhat.com> --- MAINTAINERS | 8 ++++---- tests/functional/generic/meson.build | 14 ++++++++++++++ .../{ => generic}/test_empty_cpu_model.py | 0 .../{ => generic}/test_info_usernet.py | 0 tests/functional/{ => generic}/test_version.py | 0 tests/functional/{ => generic}/test_vnc.py | 0 tests/functional/meson.build | 17 ++--------------- tests/functional/x86_64/meson.build | 1 - 8 files changed, 20 insertions(+), 20 deletions(-) create mode 100644 tests/functional/generic/meson.build rename tests/functional/{ => generic}/test_empty_cpu_model.py (100%) rename tests/functional/{ => generic}/test_info_usernet.py (100%) rename tests/functional/{ => generic}/test_version.py (100%) rename tests/functional/{ => generic}/test_vnc.py (100%) diff --git a/MAINTAINERS b/MAINTAINERS index 792d2d6f2d..625fe67b41 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1976,7 +1976,7 @@ F: include/hw/core/cpu.h F: include/hw/cpu/cluster.h F: include/system/numa.h F: tests/functional/x86_64/test_cpu_queries.py -F: tests/functional/test_empty_cpu_model.py +F: tests/functional/generic/test_empty_cpu_model.py F: tests/unit/test-smp-parse.c T: git https://gitlab.com/ehabkost/qemu.git machine-next @@ -2197,7 +2197,7 @@ S: Odd Fixes F: hw/net/ F: include/hw/net/ F: tests/qtest/virtio-net-test.c -F: tests/functional/test_info_usernet.py +F: tests/functional/generic/test_info_usernet.py F: docs/system/virtio-net-failover.rst T: git https://github.com/jasowang/qemu.git net @@ -3134,7 +3134,7 @@ S: Supported F: include/qemu/option.h F: tests/unit/test-keyval.c F: tests/unit/test-qemu-opts.c -F: tests/functional/test_version.py +F: tests/functional/generic/test_version.py F: util/keyval.c F: util/qemu-option.c @@ -3252,7 +3252,7 @@ F: include/ui/ F: qapi/ui.json F: util/drm.c F: docs/devel/ui.rst -F: tests/functional/test_vnc.py +F: tests/functional/generic/test_vnc.py Cocoa graphics M: Peter Maydell diff --git a/tests/functional/generic/meson.build b/tests/functional/generic/meson.build new file mode 100644 index 0000000000..013cc96fbf --- /dev/null +++ b/tests/functional/generic/meson.build @@ -0,0 +1,14 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +tests_generic_system = [ + 'empty_cpu_model', + 'info_usernet', + 'version', + 'vnc', +] + +tests_generic_linuxuser = [ +] + +tests_generic_bsduser = [ +] diff --git a/tests/functional/test_empty_cpu_model.py b/tests/functional/generic/test_empty_cpu_model.py similarity index 100% rename from tests/functional/test_empty_cpu_model.py rename to tests/functional/generic/test_empty_cpu_model.py diff --git a/tests/functional/test_info_usernet.py b/tests/functional/generic/test_info_usernet.py similarity index 100% rename from tests/functional/test_info_usernet.py rename to tests/functional/generic/test_info_usernet.py diff --git a/tests/functional/test_version.py b/tests/functional/generic/test_version.py similarity index 100% rename from tests/functional/test_version.py rename to tests/functional/generic/test_version.py diff --git a/tests/functional/test_vnc.py b/tests/functional/generic/test_vnc.py similarity index 100% rename from tests/functional/test_vnc.py rename to tests/functional/generic/test_vnc.py diff --git a/tests/functional/meson.build b/tests/functional/meson.build index b1eec16add..2a0c5aa141 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -36,18 +36,7 @@ subdir('sparc') subdir('sparc64') subdir('x86_64') subdir('xtensa') - -tests_generic_system = [ - 'empty_cpu_model', - 'info_usernet', - 'version', -] - -tests_generic_linuxuser = [ -] - -tests_generic_bsduser = [ -] +subdir('generic') precache_all = [] foreach speed : ['quick', 'thorough'] @@ -90,9 +79,7 @@ foreach speed : ['quick', 'thorough'] foreach test : target_tests testname = '@0@-@1@'.format(target_base, test) - if fs.exists('test_' + test + '.py') - testfile = 'test_' + test + '.py' - elif fs.exists('generic' / 'test_' + test + '.py') + if fs.exists('generic' / 'test_' + test + '.py') testfile = 'generic' / 'test_' + test + '.py' else testfile = target_base / 'test_' + test + '.py' diff --git a/tests/functional/x86_64/meson.build b/tests/functional/x86_64/meson.build index 696a9ecab4..d0b4667bb8 100644 --- a/tests/functional/x86_64/meson.build +++ b/tests/functional/x86_64/meson.build @@ -16,7 +16,6 @@ tests_x86_64_system_quick = [ 'migration', 'pc_cpu_hotplug_props', 'virtio_version', - 'vnc', 'memlock', ] From 45d34fa8a4f7fed96f7fbf2ae42b33382bf25d0d Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 19 Aug 2025 13:24:02 +0200 Subject: [PATCH 0036/2396] MAINTAINERS: Adjust wildcards for the migration, multiprocess and replay tests Now that we moved the tests into subfolders, we have to adjust the wildcards accordingly. Signed-off-by: Thomas Huth Message-ID: <20250819112403.432587-26-thuth@redhat.com> --- MAINTAINERS | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index 625fe67b41..a64b5b849b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3604,7 +3604,8 @@ F: include/migration/ F: include/qemu/userfaultfd.h F: migration/ F: scripts/vmstate-static-checker.py -F: tests/functional/*migration.py +F: tests/functional/migration.py +F: tests/functional/*/*migration.py F: tests/vmstate-static-checker-data/ F: tests/qtest/migration/ F: tests/qtest/migration-* @@ -3773,8 +3774,10 @@ F: include/system/replay.h F: docs/devel/replay.rst F: docs/system/replay.rst F: stubs/replay.c -F: tests/functional/*reverse_debug*.py -F: tests/functional/*replay*.py +F: tests/functional/replay_kernel.py +F: tests/functional/reverse_debugging.py +F: tests/functional/*/*replay*.py +F: tests/functional/*/*reverse_debug*.py F: qapi/replay.json IOVA Tree @@ -4293,7 +4296,8 @@ F: hw/remote/vfio-user-obj.c F: include/hw/remote/vfio-user-obj.h F: hw/remote/iommu.c F: include/hw/remote/iommu.h -F: tests/functional/*multiprocess.py +F: tests/functional/multiprocess.py +F: tests/functional/*/*multiprocess.py VFIO-USER: M: John Levon From 0ac3c314130eff8e3ea9860fe3202908a7746225 Mon Sep 17 00:00:00 2001 From: Gustavo Romero Date: Tue, 19 Aug 2025 14:39:15 +0000 Subject: [PATCH 0037/2396] tests/functional: Mark main in QemuBaseTest class as a static method MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The main() method in the QemuBaseTest class has no parameters but is defined as a regular method. Currently, this does not cause any issues because in the functional tests main() is always called directly from QemuBaseTest (never from instances), but the way this method is defined makes its signature wrong, implying a 'self'. Hence, it's best practice to define such a method as a static method, so decorate it with @staticmethod. Signed-off-by: Gustavo Romero Message-ID: <20250819143916.4138035-4-gustavo.romero@linaro.org> Reviewed-by: Daniel P. Berrangé Signed-off-by: Thomas Huth --- tests/functional/qemu_test/testcase.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/functional/qemu_test/testcase.py b/tests/functional/qemu_test/testcase.py index 5caf7b13fe..fbeb171058 100644 --- a/tests/functional/qemu_test/testcase.py +++ b/tests/functional/qemu_test/testcase.py @@ -235,6 +235,7 @@ class QemuBaseTest(unittest.TestCase): self.log.removeHandler(self._log_fh) self._log_fh.close() + @staticmethod def main(): warnings.simplefilter("default") os.environ["PYTHONWARNINGS"] = "default" From a7542a38f399c50337e10aadd60513a400c45013 Mon Sep 17 00:00:00 2001 From: Xiaoyao Li Date: Thu, 14 Aug 2025 17:21:11 +0800 Subject: [PATCH 0038/2396] x86/loader: Don't update kernel header for CoCo VMs Update the header makes it different from the original kernel that user provides via "-kernel", which leads to a different hash and breaks the attestation, e.g., for TDX. We already skip it for SEV VMs. Instead of adding another check of is_tdx_vm() to cover the TDX case, check machine->cgs to cover all the confidential computing case for x86. Reported-by: Vikrant Garg Signed-off-by: Xiaoyao Li Link: https://lore.kernel.org/r/20250814092111.2353598-1-xiaoyao.li@intel.com Signed-off-by: Paolo Bonzini --- hw/i386/x86-common.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/i386/x86-common.c b/hw/i386/x86-common.c index b1b5f11e73..7512be64d6 100644 --- a/hw/i386/x86-common.c +++ b/hw/i386/x86-common.c @@ -952,7 +952,7 @@ void x86_load_linux(X86MachineState *x86ms, * kernel on the other side of the fw_cfg interface matches the hash of the * file the user passed in. */ - if (!sev_enabled() && protocol > 0) { + if (!MACHINE(x86ms)->cgs && protocol > 0) { memcpy(setup, header, MIN(sizeof(header), setup_size)); } From c12cbaa007c9da97a11e74119ea3aed9fcc3ac4c Mon Sep 17 00:00:00 2001 From: Zero Tang Date: Mon, 18 Aug 2025 12:16:47 +0200 Subject: [PATCH 0039/2396] i386/tcg/svm: fix incorrect canonicalization For all 32-bit systems and 64-bit Windows systems, "long" is 4 bytes long. Due to using "long" for a linear address, svm_canonicalization would set all high bits to 1 when (assuming 48-bit linear address) the segment base is bigger than 0x7FFF. This fixes booting guests under TCG when the guest IDT and GDT bases are above 0x7FFF, thereby resulting in incorrect bases. When an interrupt arrives, it would trigger a #PF exception; the #PF would trigger again, resulting in a #DF exception; the #PF would trigger for the third time, resulting in triple-fault, and eventually causes a shutdown VM-Exit to the hypervisor right after guest boot. Cc: qemu-stable@nongnu.org Signed-off-by: Zero Tang --- target/i386/tcg/system/svm_helper.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/i386/tcg/system/svm_helper.c b/target/i386/tcg/system/svm_helper.c index b27049b9ed..dea039b87a 100644 --- a/target/i386/tcg/system/svm_helper.c +++ b/target/i386/tcg/system/svm_helper.c @@ -49,7 +49,7 @@ static void svm_save_seg(CPUX86State *env, int mmu_idx, hwaddr addr, static inline void svm_canonicalization(CPUX86State *env, target_ulong *seg_base) { uint16_t shift_amt = 64 - cpu_x86_virtual_addr_width(env); - *seg_base = ((((long) *seg_base) << shift_amt) >> shift_amt); + *seg_base = (((int64_t) *seg_base) << shift_amt) >> shift_amt; } static void svm_load_seg(CPUX86State *env, int mmu_idx, hwaddr addr, From 8e98961f6eb691b59ff0230ee22917061bfae5f8 Mon Sep 17 00:00:00 2001 From: Ani Sinha Date: Fri, 15 Aug 2025 12:24:45 +0530 Subject: [PATCH 0040/2396] kvm/kvm-all: make kvm_park/unpark_vcpu local to kvm-all.c kvm_park_vcpu() and kvm_unpark_vcpu() is only used in kvm-all.c. Declare it static, remove it from common header file and make it local to kvm-all.c Signed-off-by: Ani Sinha Reviewed-by: Igor Mammedov Reviewed-by: Zhao Liu Link: https://lore.kernel.org/r/20250815065445.8978-1-anisinha@redhat.com Signed-off-by: Paolo Bonzini --- accel/kvm/kvm-all.c | 4 ++-- include/system/kvm.h | 17 ----------------- 2 files changed, 2 insertions(+), 19 deletions(-) diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index 890d5ea9f8..f36dfe3349 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -414,7 +414,7 @@ err: return ret; } -void kvm_park_vcpu(CPUState *cpu) +static void kvm_park_vcpu(CPUState *cpu) { struct KVMParkedVcpu *vcpu; @@ -426,7 +426,7 @@ void kvm_park_vcpu(CPUState *cpu) QLIST_INSERT_HEAD(&kvm_state->kvm_parked_vcpus, vcpu, node); } -int kvm_unpark_vcpu(KVMState *s, unsigned long vcpu_id) +static int kvm_unpark_vcpu(KVMState *s, unsigned long vcpu_id) { struct KVMParkedVcpu *cpu; int kvm_fd = -ENOENT; diff --git a/include/system/kvm.h b/include/system/kvm.h index 3c7d314736..4fc09e3891 100644 --- a/include/system/kvm.h +++ b/include/system/kvm.h @@ -317,23 +317,6 @@ int kvm_create_device(KVMState *s, uint64_t type, bool test); */ bool kvm_device_supported(int vmfd, uint64_t type); -/** - * kvm_park_vcpu - Park QEMU KVM vCPU context - * @cpu: QOM CPUState object for which QEMU KVM vCPU context has to be parked. - * - * @returns: none - */ -void kvm_park_vcpu(CPUState *cpu); - -/** - * kvm_unpark_vcpu - unpark QEMU KVM vCPU context - * @s: KVM State - * @vcpu_id: Architecture vCPU ID of the parked vCPU - * - * @returns: KVM fd - */ -int kvm_unpark_vcpu(KVMState *s, unsigned long vcpu_id); - /** * kvm_create_and_park_vcpu - Create and park a KVM vCPU * @cpu: QOM CPUState object for which KVM vCPU has to be created and parked. From db62680edd04c78eada3cf67f27d8825e08feb9a Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 31 Jul 2025 19:36:38 +0200 Subject: [PATCH 0041/2396] rust: disable borrow_as_ptr warning This is pretty noisy, but it was not visible until now because it only shows up if the rust-version has "&raw const". Signed-off-by: Paolo Bonzini --- rust/Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 0868e1b426..0a83db1535 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -53,7 +53,6 @@ as_ptr_cast_mut = "deny" as_underscore = "deny" assertions_on_result_states = "deny" bool_to_int_with_if = "deny" -borrow_as_ptr = "deny" cast_lossless = "deny" dbg_macro = "deny" debug_assert_with_mut_call = "deny" From 2102780c5523c240c66ba52ea1629353a7518072 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 16 Jul 2025 13:17:09 +0200 Subject: [PATCH 0042/2396] rust: qemu-api-macros: support matching more than one error Signed-off-by: Paolo Bonzini --- rust/qemu-api-macros/src/tests.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rust/qemu-api-macros/src/tests.rs b/rust/qemu-api-macros/src/tests.rs index d6dcd62fcf..6028cdbc4c 100644 --- a/rust/qemu-api-macros/src/tests.rs +++ b/rust/qemu-api-macros/src/tests.rs @@ -7,9 +7,9 @@ use quote::quote; use super::*; macro_rules! derive_compile_fail { - ($derive_fn:ident, $input:expr, $error_msg:expr) => {{ + ($derive_fn:ident, $input:expr, $($error_msg:expr),+ $(,)?) => {{ let input: proc_macro2::TokenStream = $input; - let error_msg: &str = $error_msg; + let error_msg = &[$( quote! { ::core::compile_error! { $error_msg } } ),*]; let derive_fn: fn(input: syn::DeriveInput) -> Result = $derive_fn; @@ -18,7 +18,7 @@ macro_rules! derive_compile_fail { let err = result.unwrap_err().into_compile_error(); assert_eq!( err.to_string(), - quote! { ::core::compile_error! { #error_msg } }.to_string() + quote! { #(#error_msg)* }.to_string() ); }}; } From 9a6d6ae8afb18e18eacb94e105722c08e84fe9fd Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 17 Jul 2025 08:09:26 +0200 Subject: [PATCH 0043/2396] subprojects: update proc-macro2 and syn syn 2.0.69 adds Punctuated::get(). The serde and attrs crate also need a newer version. Signed-off-by: Paolo Bonzini --- rust/Cargo.lock | 8 ++++---- subprojects/packagefiles/proc-macro2-1-rs/meson.build | 2 +- subprojects/packagefiles/syn-2-rs/meson.build | 2 +- subprojects/proc-macro2-1-rs.wrap | 8 ++++---- subprojects/syn-2-rs.wrap | 8 ++++---- 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/rust/Cargo.lock b/rust/Cargo.lock index b785c718f3..4baf6ba663 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -118,9 +118,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.84" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec96c6a92621310b51366f1e28d05ef11489516e93be030060e5fc12024a49d6" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" dependencies = [ "unicode-ident", ] @@ -155,9 +155,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.66" +version = "2.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5" +checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" dependencies = [ "proc-macro2", "quote", diff --git a/subprojects/packagefiles/proc-macro2-1-rs/meson.build b/subprojects/packagefiles/proc-macro2-1-rs/meson.build index 5759df3ecc..ba7de07029 100644 --- a/subprojects/packagefiles/proc-macro2-1-rs/meson.build +++ b/subprojects/packagefiles/proc-macro2-1-rs/meson.build @@ -1,6 +1,6 @@ project('proc-macro2-1-rs', 'rust', meson_version: '>=1.5.0', - version: '1.0.84', + version: '1.0.95', license: 'MIT OR Apache-2.0', default_options: []) diff --git a/subprojects/packagefiles/syn-2-rs/meson.build b/subprojects/packagefiles/syn-2-rs/meson.build index a009417408..3e6dc318a9 100644 --- a/subprojects/packagefiles/syn-2-rs/meson.build +++ b/subprojects/packagefiles/syn-2-rs/meson.build @@ -1,6 +1,6 @@ project('syn-2-rs', 'rust', meson_version: '>=1.5.0', - version: '2.0.66', + version: '2.0.104', license: 'MIT OR Apache-2.0', default_options: []) diff --git a/subprojects/proc-macro2-1-rs.wrap b/subprojects/proc-macro2-1-rs.wrap index 6c9369f0df..0f06cd8e11 100644 --- a/subprojects/proc-macro2-1-rs.wrap +++ b/subprojects/proc-macro2-1-rs.wrap @@ -1,8 +1,8 @@ [wrap-file] -directory = proc-macro2-1.0.84 -source_url = https://crates.io/api/v1/crates/proc-macro2/1.0.84/download -source_filename = proc-macro2-1.0.84.0.tar.gz -source_hash = ec96c6a92621310b51366f1e28d05ef11489516e93be030060e5fc12024a49d6 +directory = proc-macro2-1.0.95 +source_url = https://crates.io/api/v1/crates/proc-macro2/1.0.95/download +source_filename = proc-macro2-1.0.95.0.tar.gz +source_hash = 02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778 #method = cargo patch_directory = proc-macro2-1-rs diff --git a/subprojects/syn-2-rs.wrap b/subprojects/syn-2-rs.wrap index d79cf750fb..1e5e9d9fb6 100644 --- a/subprojects/syn-2-rs.wrap +++ b/subprojects/syn-2-rs.wrap @@ -1,8 +1,8 @@ [wrap-file] -directory = syn-2.0.66 -source_url = https://crates.io/api/v1/crates/syn/2.0.66/download -source_filename = syn-2.0.66.0.tar.gz -source_hash = c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5 +directory = syn-2.0.104 +source_url = https://crates.io/api/v1/crates/syn/2.0.104/download +source_filename = syn-2.0.104.0.tar.gz +source_hash = 17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40 #method = cargo patch_directory = syn-2-rs From 96f2c80fed20790fec0b35b774af676d5068077b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Tue, 26 Aug 2025 17:31:32 +0400 Subject: [PATCH 0044/2396] rust/qemu-api-macros: make derive(Object) friendly when missing parent MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marc-André Lureau Link: https://lore.kernel.org/r/20250826133132.4064478-5-marcandre.lureau@redhat.com Signed-off-by: Paolo Bonzini --- rust/qemu-api-macros/src/lib.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/rust/qemu-api-macros/src/lib.rs b/rust/qemu-api-macros/src/lib.rs index b525d89c09..a614741889 100644 --- a/rust/qemu-api-macros/src/lib.rs +++ b/rust/qemu-api-macros/src/lib.rs @@ -85,7 +85,15 @@ fn derive_object_or_error(input: DeriveInput) -> Result Date: Mon, 28 Jul 2025 14:48:23 +0300 Subject: [PATCH 0045/2396] rust: declare self as qemu_api for proc-macros Fix an outstanding TODO. Declaring `extern crate self as qemu_api` allows use of `qemu_api` within the qemu_api crate; this allows the Wrapper derive macro and future proc macros to be used interchangeably in the qemu_api crate and other crates. This is not required currently and is only for future-proofing. Signed-off-by: Manos Pitsidianakis Link: https://lore.kernel.org/r/20250728-self-as-qemu_api-v1-1-001c339cccc8@linaro.org Signed-off-by: Paolo Bonzini --- rust/qemu-api-macros/src/lib.rs | 14 ++++++-------- rust/qemu-api/src/lib.rs | 4 ++++ 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/rust/qemu-api-macros/src/lib.rs b/rust/qemu-api-macros/src/lib.rs index a614741889..959726efe6 100644 --- a/rust/qemu-api-macros/src/lib.rs +++ b/rust/qemu-api-macros/src/lib.rs @@ -123,23 +123,21 @@ fn derive_opaque_or_error(input: DeriveInput) -> Result::Wrapped; + unsafe impl ::qemu_api::cell::Wrapper for #name { + type Wrapped = <#typ as ::qemu_api::cell::Wrapper>::Wrapped; } impl #name { - pub unsafe fn from_raw<'a>(ptr: *mut ::Wrapped) -> &'a Self { + pub unsafe fn from_raw<'a>(ptr: *mut ::Wrapped) -> &'a Self { let ptr = ::std::ptr::NonNull::new(ptr).unwrap().cast::(); unsafe { ptr.as_ref() } } - pub const fn as_mut_ptr(&self) -> *mut ::Wrapped { + pub const fn as_mut_ptr(&self) -> *mut ::Wrapped { self.0.as_mut_ptr() } - pub const fn as_ptr(&self) -> *const ::Wrapped { + pub const fn as_ptr(&self) -> *const ::Wrapped { self.0.as_ptr() } @@ -147,7 +145,7 @@ fn derive_opaque_or_error(input: DeriveInput) -> Result *mut ::Wrapped { + pub const fn raw_get(slot: *mut Self) -> *mut ::Wrapped { slot.cast() } } diff --git a/rust/qemu-api/src/lib.rs b/rust/qemu-api/src/lib.rs index 86dcd8ef17..bcb51c7986 100644 --- a/rust/qemu-api/src/lib.rs +++ b/rust/qemu-api/src/lib.rs @@ -32,6 +32,10 @@ pub mod uninit; pub mod vmstate; pub mod zeroable; +// Allow proc-macros to refer to `::qemu_api` inside the `qemu_api` crate (this +// crate). +extern crate self as qemu_api; + use std::{ alloc::{GlobalAlloc, Layout}, ffi::c_void, From 92dedaf169ddcf8c81fa6d21c86c60f3b82458e5 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 22 Aug 2025 12:07:44 +0200 Subject: [PATCH 0046/2396] rust: move dependencies to rust/Cargo.toml As more crates start using the same dependencies, it's better to not repeat the versions and move the dependency declarations to the workspace. Reviewed-by: Manos Pitsidianakis Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- rust/Cargo.toml | 5 +++++ rust/qemu-api/Cargo.toml | 6 +++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 0a83db1535..6f8884eb30 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -15,6 +15,11 @@ license = "GPL-2.0-or-later" repository = "https://gitlab.com/qemu-project/qemu/" rust-version = "1.77.0" +[workspace.dependencies] +anyhow = "~1.0" +foreign = "~0.3.1" +libc = "0.2.162" + [workspace.lints.rust] unexpected_cfgs = { level = "deny", check-cfg = [ 'cfg(MESON)', 'cfg(HAVE_GLIB_WITH_ALIGNED_ALLOC)', diff --git a/rust/qemu-api/Cargo.toml b/rust/qemu-api/Cargo.toml index db7000dee4..c07a17a28b 100644 --- a/rust/qemu-api/Cargo.toml +++ b/rust/qemu-api/Cargo.toml @@ -15,9 +15,9 @@ rust-version.workspace = true [dependencies] qemu_api_macros = { path = "../qemu-api-macros" } -anyhow = "~1.0" -libc = "0.2.162" -foreign = "~0.3.1" +anyhow = { workspace = true } +foreign = { workspace = true } +libc = { workspace = true } [features] default = ["debug_cell"] From 05c84cf8df23b8dc81317ee0cea748e6199637f4 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 2 Aug 2025 10:12:01 +1000 Subject: [PATCH 0047/2396] semihosting: Retrieve stack top from image_info Remove the write-once field TaskState.stack_base, and use the same value from struct image_info. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/aarch64/cpu_loop.c | 1 - linux-user/arm/cpu_loop.c | 1 - linux-user/m68k/cpu_loop.c | 1 - linux-user/qemu.h | 1 - linux-user/riscv/cpu_loop.c | 1 - semihosting/arm-compat-semi.c | 6 +++++- 6 files changed, 5 insertions(+), 6 deletions(-) diff --git a/linux-user/aarch64/cpu_loop.c b/linux-user/aarch64/cpu_loop.c index fea43cefa6..b65999a75b 100644 --- a/linux-user/aarch64/cpu_loop.c +++ b/linux-user/aarch64/cpu_loop.c @@ -168,7 +168,6 @@ void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) qemu_guest_getrandom_nofail(&env->keys, sizeof(env->keys)); } - ts->stack_base = info->start_stack; ts->heap_base = info->brk; /* This will be filled in on the first SYS_HEAPINFO call. */ ts->heap_limit = 0; diff --git a/linux-user/arm/cpu_loop.c b/linux-user/arm/cpu_loop.c index 33f63951a9..e40d6beafa 100644 --- a/linux-user/arm/cpu_loop.c +++ b/linux-user/arm/cpu_loop.c @@ -504,7 +504,6 @@ void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) arm_rebuild_hflags(env); #endif - ts->stack_base = info->start_stack; ts->heap_base = info->brk; /* This will be filled in on the first SYS_HEAPINFO call. */ ts->heap_limit = 0; diff --git a/linux-user/m68k/cpu_loop.c b/linux-user/m68k/cpu_loop.c index 5da91b997a..3aaaf02ca4 100644 --- a/linux-user/m68k/cpu_loop.c +++ b/linux-user/m68k/cpu_loop.c @@ -117,7 +117,6 @@ void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) env->aregs[7] = regs->usp; env->sr = regs->sr; - ts->stack_base = info->start_stack; ts->heap_base = info->brk; /* This will be filled in on the first SYS_HEAPINFO call. */ ts->heap_limit = 0; diff --git a/linux-user/qemu.h b/linux-user/qemu.h index 0b19fa43e6..b6621536b3 100644 --- a/linux-user/qemu.h +++ b/linux-user/qemu.h @@ -127,7 +127,6 @@ struct TaskState { abi_ulong heap_base; abi_ulong heap_limit; #endif - abi_ulong stack_base; int used; /* non zero if used */ struct image_info *info; struct linux_binprm *bprm; diff --git a/linux-user/riscv/cpu_loop.c b/linux-user/riscv/cpu_loop.c index 3ac8bbfec1..541de765ff 100644 --- a/linux-user/riscv/cpu_loop.c +++ b/linux-user/riscv/cpu_loop.c @@ -109,7 +109,6 @@ void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) exit(EXIT_FAILURE); } - ts->stack_base = info->start_stack; ts->heap_base = info->brk; /* This will be filled in on the first SYS_HEAPINFO call. */ ts->heap_limit = 0; diff --git a/semihosting/arm-compat-semi.c b/semihosting/arm-compat-semi.c index 86e5260e50..bc04b02eba 100644 --- a/semihosting/arm-compat-semi.c +++ b/semihosting/arm-compat-semi.c @@ -696,7 +696,11 @@ void do_common_semihosting(CPUState *cs) retvals[0] = ts->heap_base; retvals[1] = ts->heap_limit; - retvals[2] = ts->stack_base; + /* + * Note that semihosting is *not* thread aware. + * Always return the stack base of the main thread. + */ + retvals[2] = ts->info->start_stack; retvals[3] = 0; /* Stack limit. */ #else retvals[0] = info.heapbase; /* Heap Base */ From 7adf9ebb0ac72637833f61e24e44def6228b4484 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 2 Aug 2025 10:25:12 +1000 Subject: [PATCH 0048/2396] semihosting: Initialize heap once per process While semihosting isn't really thread aware, the current implementation allocates space for the heap per-thread. Remove the heap_base and heap_limit fields from TaskState. Replace with static variables within do_common_semihosting. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/aarch64/cpu_loop.c | 7 ------- linux-user/arm/cpu_loop.c | 25 +++++++++++-------------- linux-user/m68k/cpu_loop.c | 8 -------- linux-user/qemu.h | 5 ----- linux-user/riscv/cpu_loop.c | 4 ---- semihosting/arm-compat-semi.c | 22 +++++++++------------- 6 files changed, 20 insertions(+), 51 deletions(-) diff --git a/linux-user/aarch64/cpu_loop.c b/linux-user/aarch64/cpu_loop.c index b65999a75b..030a630c93 100644 --- a/linux-user/aarch64/cpu_loop.c +++ b/linux-user/aarch64/cpu_loop.c @@ -140,9 +140,6 @@ void cpu_loop(CPUARMState *env) void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) { ARMCPU *cpu = env_archcpu(env); - CPUState *cs = env_cpu(env); - TaskState *ts = get_task_state(cs); - struct image_info *info = ts->info; int i; if (!(arm_feature(env, ARM_FEATURE_AARCH64))) { @@ -167,8 +164,4 @@ void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) if (cpu_isar_feature(aa64_pauth, cpu)) { qemu_guest_getrandom_nofail(&env->keys, sizeof(env->keys)); } - - ts->heap_base = info->brk; - /* This will be filled in on the first SYS_HEAPINFO call. */ - ts->heap_limit = 0; } diff --git a/linux-user/arm/cpu_loop.c b/linux-user/arm/cpu_loop.c index e40d6beafa..9d54422736 100644 --- a/linux-user/arm/cpu_loop.c +++ b/linux-user/arm/cpu_loop.c @@ -492,19 +492,16 @@ void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) for(i = 0; i < 16; i++) { env->regs[i] = regs->uregs[i]; } -#if TARGET_BIG_ENDIAN - /* Enable BE8. */ - if (EF_ARM_EABI_VERSION(info->elf_flags) >= EF_ARM_EABI_VER4 - && (info->elf_flags & EF_ARM_BE8)) { - env->uncached_cpsr |= CPSR_E; - env->cp15.sctlr_el[1] |= SCTLR_E0E; - } else { - env->cp15.sctlr_el[1] |= SCTLR_B; - } - arm_rebuild_hflags(env); -#endif - ts->heap_base = info->brk; - /* This will be filled in on the first SYS_HEAPINFO call. */ - ts->heap_limit = 0; + if (TARGET_BIG_ENDIAN) { + /* Enable BE8. */ + if (EF_ARM_EABI_VERSION(info->elf_flags) >= EF_ARM_EABI_VER4 + && (info->elf_flags & EF_ARM_BE8)) { + env->uncached_cpsr |= CPSR_E; + env->cp15.sctlr_el[1] |= SCTLR_E0E; + } else { + env->cp15.sctlr_el[1] |= SCTLR_B; + } + arm_rebuild_hflags(env); + } } diff --git a/linux-user/m68k/cpu_loop.c b/linux-user/m68k/cpu_loop.c index 3aaaf02ca4..23693f3358 100644 --- a/linux-user/m68k/cpu_loop.c +++ b/linux-user/m68k/cpu_loop.c @@ -94,10 +94,6 @@ void cpu_loop(CPUM68KState *env) void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) { - CPUState *cpu = env_cpu(env); - TaskState *ts = get_task_state(cpu); - struct image_info *info = ts->info; - env->pc = regs->pc; env->dregs[0] = regs->d0; env->dregs[1] = regs->d1; @@ -116,8 +112,4 @@ void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) env->aregs[6] = regs->a6; env->aregs[7] = regs->usp; env->sr = regs->sr; - - ts->heap_base = info->brk; - /* This will be filled in on the first SYS_HEAPINFO call. */ - ts->heap_limit = 0; } diff --git a/linux-user/qemu.h b/linux-user/qemu.h index b6621536b3..4d6fad28c6 100644 --- a/linux-user/qemu.h +++ b/linux-user/qemu.h @@ -121,11 +121,6 @@ struct TaskState { abi_ulong child_tidptr; #ifdef TARGET_M68K abi_ulong tp_value; -#endif -#if defined(TARGET_ARM) || defined(TARGET_M68K) || defined(TARGET_RISCV) - /* Extra fields for semihosted binaries. */ - abi_ulong heap_base; - abi_ulong heap_limit; #endif int used; /* non zero if used */ struct image_info *info; diff --git a/linux-user/riscv/cpu_loop.c b/linux-user/riscv/cpu_loop.c index 541de765ff..2dd30c7b28 100644 --- a/linux-user/riscv/cpu_loop.c +++ b/linux-user/riscv/cpu_loop.c @@ -108,8 +108,4 @@ void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) error_report("Incompatible ELF: RVE cpu requires RVE ABI binary"); exit(EXIT_FAILURE); } - - ts->heap_base = info->brk; - /* This will be filled in on the first SYS_HEAPINFO call. */ - ts->heap_limit = 0; } diff --git a/semihosting/arm-compat-semi.c b/semihosting/arm-compat-semi.c index bc04b02eba..bcd13cd6df 100644 --- a/semihosting/arm-compat-semi.c +++ b/semihosting/arm-compat-semi.c @@ -666,7 +666,7 @@ void do_common_semihosting(CPUState *cs) int i; #ifdef CONFIG_USER_ONLY TaskState *ts = get_task_state(cs); - target_ulong limit; + static abi_ulong heapbase, heaplimit; #else LayoutInfo info = common_semi_find_bases(cs); #endif @@ -678,24 +678,20 @@ void do_common_semihosting(CPUState *cs) * Some C libraries assume the heap immediately follows .bss, so * allocate it using sbrk. */ - if (!ts->heap_limit) { - abi_ulong ret; - - ts->heap_base = do_brk(0); - limit = ts->heap_base + COMMON_SEMI_HEAP_SIZE; + if (!heaplimit) { + heapbase = do_brk(0); /* Try a big heap, and reduce the size if that fails. */ - for (;;) { - ret = do_brk(limit); + for (abi_ulong size = COMMON_SEMI_HEAP_SIZE; ; size >>= 1) { + abi_ulong limit = heapbase + size; + abi_ulong ret = do_brk(limit); if (ret >= limit) { + heaplimit = limit; break; } - limit = (ts->heap_base >> 1) + (limit >> 1); } - ts->heap_limit = limit; } - - retvals[0] = ts->heap_base; - retvals[1] = ts->heap_limit; + retvals[0] = heapbase; + retvals[1] = heaplimit; /* * Note that semihosting is *not* thread aware. * Always return the stack base of the main thread. From 58afe4cfe93be0e283e7a0fcd9a50fd52fc44169 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 29 Jul 2025 08:03:41 -1000 Subject: [PATCH 0049/2396] linux-user: Create target/elfload.c files Prepare to split the main linux-user/elfload.c. Create empty files for each target, and add the common build rule. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/aarch64/elfload.c | 1 + linux-user/alpha/elfload.c | 1 + linux-user/arm/elfload.c | 1 + linux-user/hexagon/elfload.c | 1 + linux-user/hppa/elfload.c | 1 + linux-user/i386/elfload.c | 1 + linux-user/loongarch64/elfload.c | 1 + linux-user/m68k/elfload.c | 1 + linux-user/microblaze/elfload.c | 1 + linux-user/mips/elfload.c | 1 + linux-user/mips64/elfload.c | 1 + linux-user/openrisc/elfload.c | 1 + linux-user/ppc/elfload.c | 1 + linux-user/riscv/elfload.c | 1 + linux-user/s390x/elfload.c | 1 + linux-user/sh4/elfload.c | 1 + linux-user/sparc/elfload.c | 1 + linux-user/x86_64/elfload.c | 1 + linux-user/xtensa/elfload.c | 1 + meson.build | 6 +++++- 20 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 linux-user/aarch64/elfload.c create mode 100644 linux-user/alpha/elfload.c create mode 100644 linux-user/arm/elfload.c create mode 100644 linux-user/hexagon/elfload.c create mode 100644 linux-user/hppa/elfload.c create mode 100644 linux-user/i386/elfload.c create mode 100644 linux-user/loongarch64/elfload.c create mode 100644 linux-user/m68k/elfload.c create mode 100644 linux-user/microblaze/elfload.c create mode 100644 linux-user/mips/elfload.c create mode 100644 linux-user/mips64/elfload.c create mode 100644 linux-user/openrisc/elfload.c create mode 100644 linux-user/ppc/elfload.c create mode 100644 linux-user/riscv/elfload.c create mode 100644 linux-user/s390x/elfload.c create mode 100644 linux-user/sh4/elfload.c create mode 100644 linux-user/sparc/elfload.c create mode 100644 linux-user/x86_64/elfload.c create mode 100644 linux-user/xtensa/elfload.c diff --git a/linux-user/aarch64/elfload.c b/linux-user/aarch64/elfload.c new file mode 100644 index 0000000000..73fa78ef14 --- /dev/null +++ b/linux-user/aarch64/elfload.c @@ -0,0 +1 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/linux-user/alpha/elfload.c b/linux-user/alpha/elfload.c new file mode 100644 index 0000000000..73fa78ef14 --- /dev/null +++ b/linux-user/alpha/elfload.c @@ -0,0 +1 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/linux-user/arm/elfload.c b/linux-user/arm/elfload.c new file mode 100644 index 0000000000..73fa78ef14 --- /dev/null +++ b/linux-user/arm/elfload.c @@ -0,0 +1 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/linux-user/hexagon/elfload.c b/linux-user/hexagon/elfload.c new file mode 100644 index 0000000000..73fa78ef14 --- /dev/null +++ b/linux-user/hexagon/elfload.c @@ -0,0 +1 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/linux-user/hppa/elfload.c b/linux-user/hppa/elfload.c new file mode 100644 index 0000000000..73fa78ef14 --- /dev/null +++ b/linux-user/hppa/elfload.c @@ -0,0 +1 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/linux-user/i386/elfload.c b/linux-user/i386/elfload.c new file mode 100644 index 0000000000..73fa78ef14 --- /dev/null +++ b/linux-user/i386/elfload.c @@ -0,0 +1 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/linux-user/loongarch64/elfload.c b/linux-user/loongarch64/elfload.c new file mode 100644 index 0000000000..73fa78ef14 --- /dev/null +++ b/linux-user/loongarch64/elfload.c @@ -0,0 +1 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/linux-user/m68k/elfload.c b/linux-user/m68k/elfload.c new file mode 100644 index 0000000000..73fa78ef14 --- /dev/null +++ b/linux-user/m68k/elfload.c @@ -0,0 +1 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/linux-user/microblaze/elfload.c b/linux-user/microblaze/elfload.c new file mode 100644 index 0000000000..73fa78ef14 --- /dev/null +++ b/linux-user/microblaze/elfload.c @@ -0,0 +1 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/linux-user/mips/elfload.c b/linux-user/mips/elfload.c new file mode 100644 index 0000000000..73fa78ef14 --- /dev/null +++ b/linux-user/mips/elfload.c @@ -0,0 +1 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/linux-user/mips64/elfload.c b/linux-user/mips64/elfload.c new file mode 100644 index 0000000000..b719555e65 --- /dev/null +++ b/linux-user/mips64/elfload.c @@ -0,0 +1 @@ +#include "../mips/elfload.c" diff --git a/linux-user/openrisc/elfload.c b/linux-user/openrisc/elfload.c new file mode 100644 index 0000000000..73fa78ef14 --- /dev/null +++ b/linux-user/openrisc/elfload.c @@ -0,0 +1 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/linux-user/ppc/elfload.c b/linux-user/ppc/elfload.c new file mode 100644 index 0000000000..73fa78ef14 --- /dev/null +++ b/linux-user/ppc/elfload.c @@ -0,0 +1 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/linux-user/riscv/elfload.c b/linux-user/riscv/elfload.c new file mode 100644 index 0000000000..73fa78ef14 --- /dev/null +++ b/linux-user/riscv/elfload.c @@ -0,0 +1 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/linux-user/s390x/elfload.c b/linux-user/s390x/elfload.c new file mode 100644 index 0000000000..73fa78ef14 --- /dev/null +++ b/linux-user/s390x/elfload.c @@ -0,0 +1 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/linux-user/sh4/elfload.c b/linux-user/sh4/elfload.c new file mode 100644 index 0000000000..73fa78ef14 --- /dev/null +++ b/linux-user/sh4/elfload.c @@ -0,0 +1 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/linux-user/sparc/elfload.c b/linux-user/sparc/elfload.c new file mode 100644 index 0000000000..73fa78ef14 --- /dev/null +++ b/linux-user/sparc/elfload.c @@ -0,0 +1 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/linux-user/x86_64/elfload.c b/linux-user/x86_64/elfload.c new file mode 100644 index 0000000000..73fa78ef14 --- /dev/null +++ b/linux-user/x86_64/elfload.c @@ -0,0 +1 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/linux-user/xtensa/elfload.c b/linux-user/xtensa/elfload.c new file mode 100644 index 0000000000..73fa78ef14 --- /dev/null +++ b/linux-user/xtensa/elfload.c @@ -0,0 +1 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ diff --git a/meson.build b/meson.build index 50c774a195..0d42de61ae 100644 --- a/meson.build +++ b/meson.build @@ -4327,7 +4327,11 @@ foreach target : target_dirs ) if 'CONFIG_LINUX_USER' in config_target dir = base_dir / abi - arch_srcs += files(dir / 'signal.c', dir / 'cpu_loop.c') + arch_srcs += files( + dir / 'cpu_loop.c', + dir / 'elfload.c', + dir / 'signal.c', + ) if config_target.has_key('TARGET_SYSTBL_ABI') arch_srcs += \ syscall_nr_generators[abi].process(base_dir / abi / config_target['TARGET_SYSTBL'], From a859022ceaba12444f1a8f5a5efc5e76c7d5dc13 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 29 Jul 2025 13:40:15 -1000 Subject: [PATCH 0050/2396] linux-user: Move ppc uabi/asm/elf.h workaround to osdep.h Move the workaround out of linux-user/elfload.c, so that we don't have to replicate it in many places. Place it immediately after the include of , which draws in the relevant symbols. Note that ARCH_DLINFO is not defined by the kernel header, and so there's no need to undef it either. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- hw/core/loader.c | 4 ---- include/qemu/osdep.h | 8 ++++++++ linux-user/elfload.c | 10 ---------- 3 files changed, 8 insertions(+), 14 deletions(-) diff --git a/hw/core/loader.c b/hw/core/loader.c index e7056ba4bd..524af6f14a 100644 --- a/hw/core/loader.c +++ b/hw/core/loader.c @@ -295,10 +295,6 @@ static void *load_at(int fd, off_t offset, size_t size) return ptr; } -#ifdef ELF_CLASS -#undef ELF_CLASS -#endif - #define ELF_CLASS ELFCLASS32 #include "elf.h" diff --git a/include/qemu/osdep.h b/include/qemu/osdep.h index 96fe51bc39..be3460b32f 100644 --- a/include/qemu/osdep.h +++ b/include/qemu/osdep.h @@ -133,6 +133,14 @@ QEMU_EXTERN_C int daemon(int, int); #include #include +/* + * Avoid conflict with linux/arch/powerpc/include/uapi/asm/elf.h, included + * from , but we might as well do this unconditionally. + */ +#undef ELF_CLASS +#undef ELF_DATA +#undef ELF_ARCH + #ifdef CONFIG_IOVEC #include #endif diff --git a/linux-user/elfload.c b/linux-user/elfload.c index ea214105ff..4ca8c39dc2 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -35,16 +35,6 @@ #include "target/arm/cpu-features.h" #endif -#ifdef _ARCH_PPC64 -#undef ARCH_DLINFO -#undef ELF_PLATFORM -#undef ELF_HWCAP -#undef ELF_HWCAP2 -#undef ELF_CLASS -#undef ELF_DATA -#undef ELF_ARCH -#endif - #ifndef TARGET_ARCH_HAS_SIGTRAMP_PAGE #define TARGET_ARCH_HAS_SIGTRAMP_PAGE 0 #endif From af880af8d4624b619c9d44ff5d27440ae064f99a Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 29 Jul 2025 10:09:57 -1000 Subject: [PATCH 0051/2396] linux-user: Move get_elf_cpu_model to target/elfload.c Rename from cpu_get_model to emphasize that this is an elf-specific function. Declare the function once in loader.h. This frees up target_elf.h for other uses. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/aarch64/elfload.c | 10 +++++++ linux-user/aarch64/target_elf.h | 5 +--- linux-user/alpha/elfload.c | 10 +++++++ linux-user/alpha/target_elf.h | 5 +--- linux-user/arm/elfload.c | 10 +++++++ linux-user/arm/target_elf.h | 5 +--- linux-user/hexagon/elfload.c | 34 +++++++++++++++++++++++ linux-user/hexagon/target_elf.h | 29 ------------------- linux-user/hppa/elfload.c | 10 +++++++ linux-user/hppa/target_elf.h | 5 +--- linux-user/i386/elfload.c | 10 +++++++ linux-user/i386/target_elf.h | 5 +--- linux-user/loader.h | 3 ++ linux-user/loongarch64/elfload.c | 10 +++++++ linux-user/loongarch64/target_elf.h | 5 +--- linux-user/m68k/elfload.c | 17 ++++++++++++ linux-user/m68k/target_elf.h | 9 ------ linux-user/main.c | 3 +- linux-user/microblaze/elfload.c | 10 +++++++ linux-user/microblaze/target_elf.h | 5 +--- linux-user/mips/elfload.c | 43 +++++++++++++++++++++++++++++ linux-user/mips/target_elf.h | 11 +------- linux-user/mips64/target_elf.h | 27 +----------------- linux-user/openrisc/elfload.c | 10 +++++++ linux-user/openrisc/target_elf.h | 5 +--- linux-user/ppc/elfload.c | 14 ++++++++++ linux-user/ppc/target_elf.h | 9 +----- linux-user/riscv/elfload.c | 10 +++++++ linux-user/riscv/target_elf.h | 5 +--- linux-user/s390x/elfload.c | 10 +++++++ linux-user/s390x/target_elf.h | 5 +--- linux-user/sh4/elfload.c | 10 +++++++ linux-user/sh4/target_elf.h | 5 +--- linux-user/sparc/elfload.c | 14 ++++++++++ linux-user/sparc/target_elf.h | 9 +----- linux-user/x86_64/elfload.c | 10 +++++++ linux-user/x86_64/target_elf.h | 5 +--- linux-user/xtensa/elfload.c | 10 +++++++ linux-user/xtensa/target_elf.h | 5 ---- 39 files changed, 272 insertions(+), 145 deletions(-) diff --git a/linux-user/aarch64/elfload.c b/linux-user/aarch64/elfload.c index 73fa78ef14..b92442dfeb 100644 --- a/linux-user/aarch64/elfload.c +++ b/linux-user/aarch64/elfload.c @@ -1 +1,11 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "qemu/osdep.h" +#include "qemu.h" +#include "loader.h" + + +const char *get_elf_cpu_model(uint32_t eflags) +{ + return "any"; +} diff --git a/linux-user/aarch64/target_elf.h b/linux-user/aarch64/target_elf.h index a7eb962fba..d955b3d07f 100644 --- a/linux-user/aarch64/target_elf.h +++ b/linux-user/aarch64/target_elf.h @@ -7,8 +7,5 @@ #ifndef AARCH64_TARGET_ELF_H #define AARCH64_TARGET_ELF_H -static inline const char *cpu_get_model(uint32_t eflags) -{ - return "any"; -} + #endif diff --git a/linux-user/alpha/elfload.c b/linux-user/alpha/elfload.c index 73fa78ef14..1e44475c47 100644 --- a/linux-user/alpha/elfload.c +++ b/linux-user/alpha/elfload.c @@ -1 +1,11 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "qemu/osdep.h" +#include "qemu.h" +#include "loader.h" + + +const char *get_elf_cpu_model(uint32_t eflags) +{ + return "ev67"; +} diff --git a/linux-user/alpha/target_elf.h b/linux-user/alpha/target_elf.h index b77d638f6d..52b68680ad 100644 --- a/linux-user/alpha/target_elf.h +++ b/linux-user/alpha/target_elf.h @@ -7,8 +7,5 @@ #ifndef ALPHA_TARGET_ELF_H #define ALPHA_TARGET_ELF_H -static inline const char *cpu_get_model(uint32_t eflags) -{ - return "ev67"; -} + #endif diff --git a/linux-user/arm/elfload.c b/linux-user/arm/elfload.c index 73fa78ef14..b92442dfeb 100644 --- a/linux-user/arm/elfload.c +++ b/linux-user/arm/elfload.c @@ -1 +1,11 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "qemu/osdep.h" +#include "qemu.h" +#include "loader.h" + + +const char *get_elf_cpu_model(uint32_t eflags) +{ + return "any"; +} diff --git a/linux-user/arm/target_elf.h b/linux-user/arm/target_elf.h index 58ff6a0986..2abb27a733 100644 --- a/linux-user/arm/target_elf.h +++ b/linux-user/arm/target_elf.h @@ -7,8 +7,5 @@ #ifndef ARM_TARGET_ELF_H #define ARM_TARGET_ELF_H -static inline const char *cpu_get_model(uint32_t eflags) -{ - return "any"; -} + #endif diff --git a/linux-user/hexagon/elfload.c b/linux-user/hexagon/elfload.c index 73fa78ef14..d8b545032a 100644 --- a/linux-user/hexagon/elfload.c +++ b/linux-user/hexagon/elfload.c @@ -1 +1,35 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "qemu/osdep.h" +#include "qemu.h" +#include "loader.h" + + +const char *get_elf_cpu_model(uint32_t eflags) +{ + static char buf[32]; + int err; + + /* For now, treat anything newer than v5 as a v73 */ + /* FIXME - Disable instructions that are newer than the specified arch */ + if (eflags == 0x04 || /* v5 */ + eflags == 0x05 || /* v55 */ + eflags == 0x60 || /* v60 */ + eflags == 0x61 || /* v61 */ + eflags == 0x62 || /* v62 */ + eflags == 0x65 || /* v65 */ + eflags == 0x66 || /* v66 */ + eflags == 0x67 || /* v67 */ + eflags == 0x8067 || /* v67t */ + eflags == 0x68 || /* v68 */ + eflags == 0x69 || /* v69 */ + eflags == 0x71 || /* v71 */ + eflags == 0x8071 || /* v71t */ + eflags == 0x73 /* v73 */ + ) { + return "v73"; + } + + err = snprintf(buf, sizeof(buf), "unknown (0x%x)", eflags); + return err >= 0 && err < sizeof(buf) ? buf : "unknown"; +} diff --git a/linux-user/hexagon/target_elf.h b/linux-user/hexagon/target_elf.h index 36056fc9f0..eccf207f6b 100644 --- a/linux-user/hexagon/target_elf.h +++ b/linux-user/hexagon/target_elf.h @@ -18,33 +18,4 @@ #ifndef HEXAGON_TARGET_ELF_H #define HEXAGON_TARGET_ELF_H -static inline const char *cpu_get_model(uint32_t eflags) -{ - static char buf[32]; - int err; - - /* For now, treat anything newer than v5 as a v73 */ - /* FIXME - Disable instructions that are newer than the specified arch */ - if (eflags == 0x04 || /* v5 */ - eflags == 0x05 || /* v55 */ - eflags == 0x60 || /* v60 */ - eflags == 0x61 || /* v61 */ - eflags == 0x62 || /* v62 */ - eflags == 0x65 || /* v65 */ - eflags == 0x66 || /* v66 */ - eflags == 0x67 || /* v67 */ - eflags == 0x8067 || /* v67t */ - eflags == 0x68 || /* v68 */ - eflags == 0x69 || /* v69 */ - eflags == 0x71 || /* v71 */ - eflags == 0x8071 || /* v71t */ - eflags == 0x73 /* v73 */ - ) { - return "v73"; - } - - err = snprintf(buf, sizeof(buf), "unknown (0x%x)", eflags); - return err >= 0 && err < sizeof(buf) ? buf : "unknown"; -} - #endif diff --git a/linux-user/hppa/elfload.c b/linux-user/hppa/elfload.c index 73fa78ef14..2274fcbde4 100644 --- a/linux-user/hppa/elfload.c +++ b/linux-user/hppa/elfload.c @@ -1 +1,11 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "qemu/osdep.h" +#include "qemu.h" +#include "loader.h" + + +const char *get_elf_cpu_model(uint32_t eflags) +{ + return "hppa"; +} diff --git a/linux-user/hppa/target_elf.h b/linux-user/hppa/target_elf.h index 19cae8bd65..5826ca2cd2 100644 --- a/linux-user/hppa/target_elf.h +++ b/linux-user/hppa/target_elf.h @@ -7,8 +7,5 @@ #ifndef HPPA_TARGET_ELF_H #define HPPA_TARGET_ELF_H -static inline const char *cpu_get_model(uint32_t eflags) -{ - return "hppa"; -} + #endif diff --git a/linux-user/i386/elfload.c b/linux-user/i386/elfload.c index 73fa78ef14..f92adb7308 100644 --- a/linux-user/i386/elfload.c +++ b/linux-user/i386/elfload.c @@ -1 +1,11 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "qemu/osdep.h" +#include "qemu.h" +#include "loader.h" + + +const char *get_elf_cpu_model(uint32_t eflags) +{ + return "max"; +} diff --git a/linux-user/i386/target_elf.h b/linux-user/i386/target_elf.h index 238a9aba73..e6f0d8fa4e 100644 --- a/linux-user/i386/target_elf.h +++ b/linux-user/i386/target_elf.h @@ -7,8 +7,5 @@ #ifndef I386_TARGET_ELF_H #define I386_TARGET_ELF_H -static inline const char *cpu_get_model(uint32_t eflags) -{ - return "max"; -} + #endif diff --git a/linux-user/loader.h b/linux-user/loader.h index e102e6f410..75ee9975a0 100644 --- a/linux-user/loader.h +++ b/linux-user/loader.h @@ -98,6 +98,9 @@ abi_long memcpy_to_target(abi_ulong dest, const void *src, extern unsigned long guest_stack_size; +/* Note that Elf32 and Elf64 use uint32_t for e_flags. */ +const char *get_elf_cpu_model(uint32_t eflags); + #if defined(TARGET_S390X) || defined(TARGET_AARCH64) || defined(TARGET_ARM) uint32_t get_elf_hwcap(void); const char *elf_hwcap_str(uint32_t bit); diff --git a/linux-user/loongarch64/elfload.c b/linux-user/loongarch64/elfload.c index 73fa78ef14..874dc4c230 100644 --- a/linux-user/loongarch64/elfload.c +++ b/linux-user/loongarch64/elfload.c @@ -1 +1,11 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "qemu/osdep.h" +#include "qemu.h" +#include "loader.h" + + +const char *get_elf_cpu_model(uint32_t eflags) +{ + return "la464"; +} diff --git a/linux-user/loongarch64/target_elf.h b/linux-user/loongarch64/target_elf.h index 95c3f05a46..39a08d35d9 100644 --- a/linux-user/loongarch64/target_elf.h +++ b/linux-user/loongarch64/target_elf.h @@ -5,8 +5,5 @@ #ifndef LOONGARCH_TARGET_ELF_H #define LOONGARCH_TARGET_ELF_H -static inline const char *cpu_get_model(uint32_t eflags) -{ - return "la464"; -} + #endif diff --git a/linux-user/m68k/elfload.c b/linux-user/m68k/elfload.c index 73fa78ef14..561ac5b3b3 100644 --- a/linux-user/m68k/elfload.c +++ b/linux-user/m68k/elfload.c @@ -1 +1,18 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "qemu/osdep.h" +#include "qemu.h" +#include "loader.h" +#include "elf.h" + + +const char *get_elf_cpu_model(uint32_t eflags) +{ + if (eflags == 0 || (eflags & EF_M68K_M68000)) { + /* 680x0 */ + return "m68040"; + } + + /* Coldfire */ + return "any"; +} diff --git a/linux-user/m68k/target_elf.h b/linux-user/m68k/target_elf.h index 998fe0fe2f..62ff9d38d4 100644 --- a/linux-user/m68k/target_elf.h +++ b/linux-user/m68k/target_elf.h @@ -7,14 +7,5 @@ #ifndef M68K_TARGET_ELF_H #define M68K_TARGET_ELF_H -static inline const char *cpu_get_model(uint32_t eflags) -{ - if (eflags == 0 || (eflags & EF_M68K_M68000)) { - /* 680x0 */ - return "m68040"; - } - /* Coldfire */ - return "any"; -} #endif diff --git a/linux-user/main.c b/linux-user/main.c index 68972f00a1..ad1a29d198 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -49,7 +49,6 @@ #include "qemu/guest-random.h" #include "elf.h" #include "trace/control.h" -#include "target_elf.h" #include "user/cpu_loop.h" #include "crypto/init.h" #include "fd-trans.h" @@ -809,7 +808,7 @@ int main(int argc, char **argv, char **envp) } if (cpu_model == NULL) { - cpu_model = cpu_get_model(get_elf_eflags(execfd)); + cpu_model = get_elf_cpu_model(get_elf_eflags(execfd)); } cpu_type = parse_cpu_option(cpu_model); diff --git a/linux-user/microblaze/elfload.c b/linux-user/microblaze/elfload.c index 73fa78ef14..b92442dfeb 100644 --- a/linux-user/microblaze/elfload.c +++ b/linux-user/microblaze/elfload.c @@ -1 +1,11 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "qemu/osdep.h" +#include "qemu.h" +#include "loader.h" + + +const char *get_elf_cpu_model(uint32_t eflags) +{ + return "any"; +} diff --git a/linux-user/microblaze/target_elf.h b/linux-user/microblaze/target_elf.h index 8a8f1debff..bfe2997fd2 100644 --- a/linux-user/microblaze/target_elf.h +++ b/linux-user/microblaze/target_elf.h @@ -7,8 +7,5 @@ #ifndef MICROBLAZE_TARGET_ELF_H #define MICROBLAZE_TARGET_ELF_H -static inline const char *cpu_get_model(uint32_t eflags) -{ - return "any"; -} + #endif diff --git a/linux-user/mips/elfload.c b/linux-user/mips/elfload.c index 73fa78ef14..04e3b76740 100644 --- a/linux-user/mips/elfload.c +++ b/linux-user/mips/elfload.c @@ -1 +1,44 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "qemu/osdep.h" +#include "qemu.h" +#include "loader.h" +#include "elf.h" + + +const char *get_elf_cpu_model(uint32_t eflags) +{ +#ifdef TARGET_MIPS64 + switch (eflags & EF_MIPS_MACH) { + case EF_MIPS_MACH_OCTEON: + case EF_MIPS_MACH_OCTEON2: + case EF_MIPS_MACH_OCTEON3: + return "Octeon68XX"; + case EF_MIPS_MACH_LS2E: + return "Loongson-2E"; + case EF_MIPS_MACH_LS2F: + return "Loongson-2F"; + case EF_MIPS_MACH_LS3A: + return "Loongson-3A1000"; + default: + break; + } + switch (eflags & EF_MIPS_ARCH) { + case EF_MIPS_ARCH_64R6: + return "I6400"; + case EF_MIPS_ARCH_64R2: + return "MIPS64R2-generic"; + default: + break; + } + return "5KEf"; +#else + if ((eflags & EF_MIPS_ARCH) == EF_MIPS_ARCH_32R6) { + return "mips32r6-generic"; + } + if (eflags & EF_MIPS_NAN2008) { + return "P5600"; + } + return "24Kf"; +#endif +} diff --git a/linux-user/mips/target_elf.h b/linux-user/mips/target_elf.h index 71a32315a8..febf710c7a 100644 --- a/linux-user/mips/target_elf.h +++ b/linux-user/mips/target_elf.h @@ -7,14 +7,5 @@ #ifndef MIPS_TARGET_ELF_H #define MIPS_TARGET_ELF_H -static inline const char *cpu_get_model(uint32_t eflags) -{ - if ((eflags & EF_MIPS_ARCH) == EF_MIPS_ARCH_32R6) { - return "mips32r6-generic"; - } - if (eflags & EF_MIPS_NAN2008) { - return "P5600"; - } - return "24Kf"; -} + #endif diff --git a/linux-user/mips64/target_elf.h b/linux-user/mips64/target_elf.h index 502af9d278..02e6d14840 100644 --- a/linux-user/mips64/target_elf.h +++ b/linux-user/mips64/target_elf.h @@ -7,30 +7,5 @@ #ifndef MIPS64_TARGET_ELF_H #define MIPS64_TARGET_ELF_H -static inline const char *cpu_get_model(uint32_t eflags) -{ - switch (eflags & EF_MIPS_MACH) { - case EF_MIPS_MACH_OCTEON: - case EF_MIPS_MACH_OCTEON2: - case EF_MIPS_MACH_OCTEON3: - return "Octeon68XX"; - case EF_MIPS_MACH_LS2E: - return "Loongson-2E"; - case EF_MIPS_MACH_LS2F: - return "Loongson-2F"; - case EF_MIPS_MACH_LS3A: - return "Loongson-3A1000"; - default: - break; - } - switch (eflags & EF_MIPS_ARCH) { - case EF_MIPS_ARCH_64R6: - return "I6400"; - case EF_MIPS_ARCH_64R2: - return "MIPS64R2-generic"; - default: - break; - } - return "5KEf"; -} + #endif diff --git a/linux-user/openrisc/elfload.c b/linux-user/openrisc/elfload.c index 73fa78ef14..b92442dfeb 100644 --- a/linux-user/openrisc/elfload.c +++ b/linux-user/openrisc/elfload.c @@ -1 +1,11 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "qemu/osdep.h" +#include "qemu.h" +#include "loader.h" + + +const char *get_elf_cpu_model(uint32_t eflags) +{ + return "any"; +} diff --git a/linux-user/openrisc/target_elf.h b/linux-user/openrisc/target_elf.h index 265ecd3079..b34f2ff672 100644 --- a/linux-user/openrisc/target_elf.h +++ b/linux-user/openrisc/target_elf.h @@ -7,8 +7,5 @@ #ifndef OPENRISC_TARGET_ELF_H #define OPENRISC_TARGET_ELF_H -static inline const char *cpu_get_model(uint32_t eflags) -{ - return "any"; -} + #endif diff --git a/linux-user/ppc/elfload.c b/linux-user/ppc/elfload.c index 73fa78ef14..7775dc06fa 100644 --- a/linux-user/ppc/elfload.c +++ b/linux-user/ppc/elfload.c @@ -1 +1,15 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "qemu/osdep.h" +#include "qemu.h" +#include "loader.h" + + +const char *get_elf_cpu_model(uint32_t eflags) +{ +#ifdef TARGET_PPC64 + return "POWER9"; +#else + return "750"; +#endif +} diff --git a/linux-user/ppc/target_elf.h b/linux-user/ppc/target_elf.h index 0616618854..8c0a8ea431 100644 --- a/linux-user/ppc/target_elf.h +++ b/linux-user/ppc/target_elf.h @@ -7,12 +7,5 @@ #ifndef PPC_TARGET_ELF_H #define PPC_TARGET_ELF_H -static inline const char *cpu_get_model(uint32_t eflags) -{ -#ifdef TARGET_PPC64 - return "POWER9"; -#else - return "750"; -#endif -} + #endif diff --git a/linux-user/riscv/elfload.c b/linux-user/riscv/elfload.c index 73fa78ef14..f92adb7308 100644 --- a/linux-user/riscv/elfload.c +++ b/linux-user/riscv/elfload.c @@ -1 +1,11 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "qemu/osdep.h" +#include "qemu.h" +#include "loader.h" + + +const char *get_elf_cpu_model(uint32_t eflags) +{ + return "max"; +} diff --git a/linux-user/riscv/target_elf.h b/linux-user/riscv/target_elf.h index dedd5956f3..bfe86105d0 100644 --- a/linux-user/riscv/target_elf.h +++ b/linux-user/riscv/target_elf.h @@ -7,8 +7,5 @@ #ifndef RISCV_TARGET_ELF_H #define RISCV_TARGET_ELF_H -static inline const char *cpu_get_model(uint32_t eflags) -{ - return "max"; -} + #endif diff --git a/linux-user/s390x/elfload.c b/linux-user/s390x/elfload.c index 73fa78ef14..989953a247 100644 --- a/linux-user/s390x/elfload.c +++ b/linux-user/s390x/elfload.c @@ -1 +1,11 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "qemu/osdep.h" +#include "qemu.h" +#include "loader.h" + + +const char *get_elf_cpu_model(uint32_t eflags) +{ + return "qemu"; +} diff --git a/linux-user/s390x/target_elf.h b/linux-user/s390x/target_elf.h index 8114b59c1d..e51b053339 100644 --- a/linux-user/s390x/target_elf.h +++ b/linux-user/s390x/target_elf.h @@ -7,8 +7,5 @@ #ifndef S390X_TARGET_ELF_H #define S390X_TARGET_ELF_H -static inline const char *cpu_get_model(uint32_t eflags) -{ - return "qemu"; -} + #endif diff --git a/linux-user/sh4/elfload.c b/linux-user/sh4/elfload.c index 73fa78ef14..546034ec07 100644 --- a/linux-user/sh4/elfload.c +++ b/linux-user/sh4/elfload.c @@ -1 +1,11 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "qemu/osdep.h" +#include "qemu.h" +#include "loader.h" + + +const char *get_elf_cpu_model(uint32_t eflags) +{ + return "sh7785"; +} diff --git a/linux-user/sh4/target_elf.h b/linux-user/sh4/target_elf.h index f485e0cef2..d17011bd75 100644 --- a/linux-user/sh4/target_elf.h +++ b/linux-user/sh4/target_elf.h @@ -7,8 +7,5 @@ #ifndef SH4_TARGET_ELF_H #define SH4_TARGET_ELF_H -static inline const char *cpu_get_model(uint32_t eflags) -{ - return "sh7785"; -} + #endif diff --git a/linux-user/sparc/elfload.c b/linux-user/sparc/elfload.c index 73fa78ef14..243e6f9b66 100644 --- a/linux-user/sparc/elfload.c +++ b/linux-user/sparc/elfload.c @@ -1 +1,15 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "qemu/osdep.h" +#include "qemu.h" +#include "loader.h" + + +const char *get_elf_cpu_model(uint32_t eflags) +{ +#ifdef TARGET_SPARC64 + return "TI UltraSparc II"; +#else + return "Fujitsu MB86904"; +#endif +} diff --git a/linux-user/sparc/target_elf.h b/linux-user/sparc/target_elf.h index a510ceb612..7e46748d26 100644 --- a/linux-user/sparc/target_elf.h +++ b/linux-user/sparc/target_elf.h @@ -7,12 +7,5 @@ #ifndef SPARC_TARGET_ELF_H #define SPARC_TARGET_ELF_H -static inline const char *cpu_get_model(uint32_t eflags) -{ -#ifdef TARGET_SPARC64 - return "TI UltraSparc II"; -#else - return "Fujitsu MB86904"; -#endif -} + #endif diff --git a/linux-user/x86_64/elfload.c b/linux-user/x86_64/elfload.c index 73fa78ef14..f92adb7308 100644 --- a/linux-user/x86_64/elfload.c +++ b/linux-user/x86_64/elfload.c @@ -1 +1,11 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "qemu/osdep.h" +#include "qemu.h" +#include "loader.h" + + +const char *get_elf_cpu_model(uint32_t eflags) +{ + return "max"; +} diff --git a/linux-user/x86_64/target_elf.h b/linux-user/x86_64/target_elf.h index 3f628f8d66..5849f96350 100644 --- a/linux-user/x86_64/target_elf.h +++ b/linux-user/x86_64/target_elf.h @@ -7,8 +7,5 @@ #ifndef X86_64_TARGET_ELF_H #define X86_64_TARGET_ELF_H -static inline const char *cpu_get_model(uint32_t eflags) -{ - return "max"; -} + #endif diff --git a/linux-user/xtensa/elfload.c b/linux-user/xtensa/elfload.c index 73fa78ef14..e35ba69a10 100644 --- a/linux-user/xtensa/elfload.c +++ b/linux-user/xtensa/elfload.c @@ -1 +1,11 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "qemu/osdep.h" +#include "qemu.h" +#include "loader.h" + + +const char *get_elf_cpu_model(uint32_t eflags) +{ + return XTENSA_DEFAULT_CPU_MODEL; +} diff --git a/linux-user/xtensa/target_elf.h b/linux-user/xtensa/target_elf.h index a9a3fabd89..2c55c22e14 100644 --- a/linux-user/xtensa/target_elf.h +++ b/linux-user/xtensa/target_elf.h @@ -8,9 +8,4 @@ #ifndef XTENSA_TARGET_ELF_H #define XTENSA_TARGET_ELF_H -static inline const char *cpu_get_model(uint32_t eflags) -{ - return XTENSA_DEFAULT_CPU_MODEL; -} - #endif From 39476538942f0ae1eff5e03f08399ef1eeca0cc0 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 27 Jul 2025 20:34:19 -1000 Subject: [PATCH 0052/2396] linux-user: Move get_elf_hwcap to {i386,x86_64}/elfload.c Change the return type to abi_ulong, and pass in the cpu. Duplicate the one line function between i386 and x86_64, as most other additions to elfload.c won't be common. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 9 +-------- linux-user/i386/elfload.c | 5 +++++ linux-user/i386/target_elf.h | 2 ++ linux-user/loader.h | 3 +++ linux-user/x86_64/elfload.c | 5 +++++ linux-user/x86_64/target_elf.h | 2 ++ 6 files changed, 18 insertions(+), 8 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 4ca8c39dc2..0c62c249e9 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -148,14 +148,7 @@ typedef abi_int target_pid_t; #ifdef TARGET_I386 -#define ELF_HWCAP get_elf_hwcap() - -static uint32_t get_elf_hwcap(void) -{ - X86CPU *cpu = X86_CPU(thread_cpu); - - return cpu->env.features[FEAT_1_EDX]; -} +#define ELF_HWCAP get_elf_hwcap(thread_cpu) #ifdef TARGET_X86_64 #define ELF_CLASS ELFCLASS64 diff --git a/linux-user/i386/elfload.c b/linux-user/i386/elfload.c index f92adb7308..f99336e73c 100644 --- a/linux-user/i386/elfload.c +++ b/linux-user/i386/elfload.c @@ -9,3 +9,8 @@ const char *get_elf_cpu_model(uint32_t eflags) { return "max"; } + +abi_ulong get_elf_hwcap(CPUState *cs) +{ + return cpu_env(cs)->features[FEAT_1_EDX]; +} diff --git a/linux-user/i386/target_elf.h b/linux-user/i386/target_elf.h index e6f0d8fa4e..802395af3a 100644 --- a/linux-user/i386/target_elf.h +++ b/linux-user/i386/target_elf.h @@ -8,4 +8,6 @@ #ifndef I386_TARGET_ELF_H #define I386_TARGET_ELF_H +#define HAVE_ELF_HWCAP 1 + #endif diff --git a/linux-user/loader.h b/linux-user/loader.h index 75ee9975a0..457bb36daa 100644 --- a/linux-user/loader.h +++ b/linux-user/loader.h @@ -101,6 +101,9 @@ extern unsigned long guest_stack_size; /* Note that Elf32 and Elf64 use uint32_t for e_flags. */ const char *get_elf_cpu_model(uint32_t eflags); +#if defined(TARGET_I386) || defined(TARGET_X86_64) +abi_ulong get_elf_hwcap(CPUState *cs); +#endif #if defined(TARGET_S390X) || defined(TARGET_AARCH64) || defined(TARGET_ARM) uint32_t get_elf_hwcap(void); const char *elf_hwcap_str(uint32_t bit); diff --git a/linux-user/x86_64/elfload.c b/linux-user/x86_64/elfload.c index f92adb7308..f99336e73c 100644 --- a/linux-user/x86_64/elfload.c +++ b/linux-user/x86_64/elfload.c @@ -9,3 +9,8 @@ const char *get_elf_cpu_model(uint32_t eflags) { return "max"; } + +abi_ulong get_elf_hwcap(CPUState *cs) +{ + return cpu_env(cs)->features[FEAT_1_EDX]; +} diff --git a/linux-user/x86_64/target_elf.h b/linux-user/x86_64/target_elf.h index 5849f96350..03483bad57 100644 --- a/linux-user/x86_64/target_elf.h +++ b/linux-user/x86_64/target_elf.h @@ -8,4 +8,6 @@ #ifndef X86_64_TARGET_ELF_H #define X86_64_TARGET_ELF_H +#define HAVE_ELF_HWCAP 1 + #endif From 2d0687a514c63956de6215978acdda4f55c190dd Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 27 Jul 2025 20:52:01 -1000 Subject: [PATCH 0053/2396] linux-user: Move hwcap functions to {arm,aarch64}/elfload.c For get_elf_hwcap and get_elf_hwcap2, change the return type to abi_ulong, and pass in the cpu. We must do these targets at the same time because of the ifdef dependency between TARGET_AARCH64 and TARGET_ARM. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/aarch64/elfload.c | 333 +++++++++++++++++++++ linux-user/aarch64/target_elf.h | 3 + linux-user/arm/elfload.c | 161 ++++++++++ linux-user/arm/target_elf.h | 3 + linux-user/arm/target_proc.h | 4 +- linux-user/elfload.c | 505 +------------------------------- linux-user/loader.h | 10 +- 7 files changed, 510 insertions(+), 509 deletions(-) diff --git a/linux-user/aarch64/elfload.c b/linux-user/aarch64/elfload.c index b92442dfeb..92c8ea62c6 100644 --- a/linux-user/aarch64/elfload.c +++ b/linux-user/aarch64/elfload.c @@ -3,9 +3,342 @@ #include "qemu/osdep.h" #include "qemu.h" #include "loader.h" +#include "target/arm/cpu-features.h" const char *get_elf_cpu_model(uint32_t eflags) { return "any"; } + +enum { + ARM_HWCAP_A64_FP = 1 << 0, + ARM_HWCAP_A64_ASIMD = 1 << 1, + ARM_HWCAP_A64_EVTSTRM = 1 << 2, + ARM_HWCAP_A64_AES = 1 << 3, + ARM_HWCAP_A64_PMULL = 1 << 4, + ARM_HWCAP_A64_SHA1 = 1 << 5, + ARM_HWCAP_A64_SHA2 = 1 << 6, + ARM_HWCAP_A64_CRC32 = 1 << 7, + ARM_HWCAP_A64_ATOMICS = 1 << 8, + ARM_HWCAP_A64_FPHP = 1 << 9, + ARM_HWCAP_A64_ASIMDHP = 1 << 10, + ARM_HWCAP_A64_CPUID = 1 << 11, + ARM_HWCAP_A64_ASIMDRDM = 1 << 12, + ARM_HWCAP_A64_JSCVT = 1 << 13, + ARM_HWCAP_A64_FCMA = 1 << 14, + ARM_HWCAP_A64_LRCPC = 1 << 15, + ARM_HWCAP_A64_DCPOP = 1 << 16, + ARM_HWCAP_A64_SHA3 = 1 << 17, + ARM_HWCAP_A64_SM3 = 1 << 18, + ARM_HWCAP_A64_SM4 = 1 << 19, + ARM_HWCAP_A64_ASIMDDP = 1 << 20, + ARM_HWCAP_A64_SHA512 = 1 << 21, + ARM_HWCAP_A64_SVE = 1 << 22, + ARM_HWCAP_A64_ASIMDFHM = 1 << 23, + ARM_HWCAP_A64_DIT = 1 << 24, + ARM_HWCAP_A64_USCAT = 1 << 25, + ARM_HWCAP_A64_ILRCPC = 1 << 26, + ARM_HWCAP_A64_FLAGM = 1 << 27, + ARM_HWCAP_A64_SSBS = 1 << 28, + ARM_HWCAP_A64_SB = 1 << 29, + ARM_HWCAP_A64_PACA = 1 << 30, + ARM_HWCAP_A64_PACG = 1ULL << 31, + ARM_HWCAP_A64_GCS = 1ULL << 32, + ARM_HWCAP_A64_CMPBR = 1ULL << 33, + ARM_HWCAP_A64_FPRCVT = 1ULL << 34, + ARM_HWCAP_A64_F8MM8 = 1ULL << 35, + ARM_HWCAP_A64_F8MM4 = 1ULL << 36, + ARM_HWCAP_A64_SVE_F16MM = 1ULL << 37, + ARM_HWCAP_A64_SVE_ELTPERM = 1ULL << 38, + ARM_HWCAP_A64_SVE_AES2 = 1ULL << 39, + ARM_HWCAP_A64_SVE_BFSCALE = 1ULL << 40, + ARM_HWCAP_A64_SVE2P2 = 1ULL << 41, + ARM_HWCAP_A64_SME2P2 = 1ULL << 42, + ARM_HWCAP_A64_SME_SBITPERM = 1ULL << 43, + ARM_HWCAP_A64_SME_AES = 1ULL << 44, + ARM_HWCAP_A64_SME_SFEXPA = 1ULL << 45, + ARM_HWCAP_A64_SME_STMOP = 1ULL << 46, + ARM_HWCAP_A64_SME_SMOP4 = 1ULL << 47, + + ARM_HWCAP2_A64_DCPODP = 1 << 0, + ARM_HWCAP2_A64_SVE2 = 1 << 1, + ARM_HWCAP2_A64_SVEAES = 1 << 2, + ARM_HWCAP2_A64_SVEPMULL = 1 << 3, + ARM_HWCAP2_A64_SVEBITPERM = 1 << 4, + ARM_HWCAP2_A64_SVESHA3 = 1 << 5, + ARM_HWCAP2_A64_SVESM4 = 1 << 6, + ARM_HWCAP2_A64_FLAGM2 = 1 << 7, + ARM_HWCAP2_A64_FRINT = 1 << 8, + ARM_HWCAP2_A64_SVEI8MM = 1 << 9, + ARM_HWCAP2_A64_SVEF32MM = 1 << 10, + ARM_HWCAP2_A64_SVEF64MM = 1 << 11, + ARM_HWCAP2_A64_SVEBF16 = 1 << 12, + ARM_HWCAP2_A64_I8MM = 1 << 13, + ARM_HWCAP2_A64_BF16 = 1 << 14, + ARM_HWCAP2_A64_DGH = 1 << 15, + ARM_HWCAP2_A64_RNG = 1 << 16, + ARM_HWCAP2_A64_BTI = 1 << 17, + ARM_HWCAP2_A64_MTE = 1 << 18, + ARM_HWCAP2_A64_ECV = 1 << 19, + ARM_HWCAP2_A64_AFP = 1 << 20, + ARM_HWCAP2_A64_RPRES = 1 << 21, + ARM_HWCAP2_A64_MTE3 = 1 << 22, + ARM_HWCAP2_A64_SME = 1 << 23, + ARM_HWCAP2_A64_SME_I16I64 = 1 << 24, + ARM_HWCAP2_A64_SME_F64F64 = 1 << 25, + ARM_HWCAP2_A64_SME_I8I32 = 1 << 26, + ARM_HWCAP2_A64_SME_F16F32 = 1 << 27, + ARM_HWCAP2_A64_SME_B16F32 = 1 << 28, + ARM_HWCAP2_A64_SME_F32F32 = 1 << 29, + ARM_HWCAP2_A64_SME_FA64 = 1 << 30, + ARM_HWCAP2_A64_WFXT = 1ULL << 31, + ARM_HWCAP2_A64_EBF16 = 1ULL << 32, + ARM_HWCAP2_A64_SVE_EBF16 = 1ULL << 33, + ARM_HWCAP2_A64_CSSC = 1ULL << 34, + ARM_HWCAP2_A64_RPRFM = 1ULL << 35, + ARM_HWCAP2_A64_SVE2P1 = 1ULL << 36, + ARM_HWCAP2_A64_SME2 = 1ULL << 37, + ARM_HWCAP2_A64_SME2P1 = 1ULL << 38, + ARM_HWCAP2_A64_SME_I16I32 = 1ULL << 39, + ARM_HWCAP2_A64_SME_BI32I32 = 1ULL << 40, + ARM_HWCAP2_A64_SME_B16B16 = 1ULL << 41, + ARM_HWCAP2_A64_SME_F16F16 = 1ULL << 42, + ARM_HWCAP2_A64_MOPS = 1ULL << 43, + ARM_HWCAP2_A64_HBC = 1ULL << 44, + ARM_HWCAP2_A64_SVE_B16B16 = 1ULL << 45, + ARM_HWCAP2_A64_LRCPC3 = 1ULL << 46, + ARM_HWCAP2_A64_LSE128 = 1ULL << 47, + ARM_HWCAP2_A64_FPMR = 1ULL << 48, + ARM_HWCAP2_A64_LUT = 1ULL << 49, + ARM_HWCAP2_A64_FAMINMAX = 1ULL << 50, + ARM_HWCAP2_A64_F8CVT = 1ULL << 51, + ARM_HWCAP2_A64_F8FMA = 1ULL << 52, + ARM_HWCAP2_A64_F8DP4 = 1ULL << 53, + ARM_HWCAP2_A64_F8DP2 = 1ULL << 54, + ARM_HWCAP2_A64_F8E4M3 = 1ULL << 55, + ARM_HWCAP2_A64_F8E5M2 = 1ULL << 56, + ARM_HWCAP2_A64_SME_LUTV2 = 1ULL << 57, + ARM_HWCAP2_A64_SME_F8F16 = 1ULL << 58, + ARM_HWCAP2_A64_SME_F8F32 = 1ULL << 59, + ARM_HWCAP2_A64_SME_SF8FMA = 1ULL << 60, + ARM_HWCAP2_A64_SME_SF8DP4 = 1ULL << 61, + ARM_HWCAP2_A64_SME_SF8DP2 = 1ULL << 62, + ARM_HWCAP2_A64_POE = 1ULL << 63, +}; + +#define GET_FEATURE_ID(feat, hwcap) \ + do { if (cpu_isar_feature(feat, cpu)) { hwcaps |= hwcap; } } while (0) + +abi_ulong get_elf_hwcap(CPUState *cs) +{ + ARMCPU *cpu = ARM_CPU(cs); + abi_ulong hwcaps = 0; + + hwcaps |= ARM_HWCAP_A64_FP; + hwcaps |= ARM_HWCAP_A64_ASIMD; + hwcaps |= ARM_HWCAP_A64_CPUID; + + /* probe for the extra features */ + + GET_FEATURE_ID(aa64_aes, ARM_HWCAP_A64_AES); + GET_FEATURE_ID(aa64_pmull, ARM_HWCAP_A64_PMULL); + GET_FEATURE_ID(aa64_sha1, ARM_HWCAP_A64_SHA1); + GET_FEATURE_ID(aa64_sha256, ARM_HWCAP_A64_SHA2); + GET_FEATURE_ID(aa64_sha512, ARM_HWCAP_A64_SHA512); + GET_FEATURE_ID(aa64_crc32, ARM_HWCAP_A64_CRC32); + GET_FEATURE_ID(aa64_sha3, ARM_HWCAP_A64_SHA3); + GET_FEATURE_ID(aa64_sm3, ARM_HWCAP_A64_SM3); + GET_FEATURE_ID(aa64_sm4, ARM_HWCAP_A64_SM4); + GET_FEATURE_ID(aa64_fp16, ARM_HWCAP_A64_FPHP | ARM_HWCAP_A64_ASIMDHP); + GET_FEATURE_ID(aa64_atomics, ARM_HWCAP_A64_ATOMICS); + GET_FEATURE_ID(aa64_lse2, ARM_HWCAP_A64_USCAT); + GET_FEATURE_ID(aa64_rdm, ARM_HWCAP_A64_ASIMDRDM); + GET_FEATURE_ID(aa64_dp, ARM_HWCAP_A64_ASIMDDP); + GET_FEATURE_ID(aa64_fcma, ARM_HWCAP_A64_FCMA); + GET_FEATURE_ID(aa64_sve, ARM_HWCAP_A64_SVE); + GET_FEATURE_ID(aa64_pauth, ARM_HWCAP_A64_PACA | ARM_HWCAP_A64_PACG); + GET_FEATURE_ID(aa64_fhm, ARM_HWCAP_A64_ASIMDFHM); + GET_FEATURE_ID(aa64_dit, ARM_HWCAP_A64_DIT); + GET_FEATURE_ID(aa64_jscvt, ARM_HWCAP_A64_JSCVT); + GET_FEATURE_ID(aa64_sb, ARM_HWCAP_A64_SB); + GET_FEATURE_ID(aa64_condm_4, ARM_HWCAP_A64_FLAGM); + GET_FEATURE_ID(aa64_dcpop, ARM_HWCAP_A64_DCPOP); + GET_FEATURE_ID(aa64_rcpc_8_3, ARM_HWCAP_A64_LRCPC); + GET_FEATURE_ID(aa64_rcpc_8_4, ARM_HWCAP_A64_ILRCPC); + + return hwcaps; +} + +abi_ulong get_elf_hwcap2(CPUState *cs) +{ + ARMCPU *cpu = ARM_CPU(cs); + abi_ulong hwcaps = 0; + + GET_FEATURE_ID(aa64_dcpodp, ARM_HWCAP2_A64_DCPODP); + GET_FEATURE_ID(aa64_sve2, ARM_HWCAP2_A64_SVE2); + GET_FEATURE_ID(aa64_sve2_aes, ARM_HWCAP2_A64_SVEAES); + GET_FEATURE_ID(aa64_sve2_pmull128, ARM_HWCAP2_A64_SVEPMULL); + GET_FEATURE_ID(aa64_sve2_bitperm, ARM_HWCAP2_A64_SVEBITPERM); + GET_FEATURE_ID(aa64_sve2_sha3, ARM_HWCAP2_A64_SVESHA3); + GET_FEATURE_ID(aa64_sve2_sm4, ARM_HWCAP2_A64_SVESM4); + GET_FEATURE_ID(aa64_condm_5, ARM_HWCAP2_A64_FLAGM2); + GET_FEATURE_ID(aa64_frint, ARM_HWCAP2_A64_FRINT); + GET_FEATURE_ID(aa64_sve_i8mm, ARM_HWCAP2_A64_SVEI8MM); + GET_FEATURE_ID(aa64_sve_f32mm, ARM_HWCAP2_A64_SVEF32MM); + GET_FEATURE_ID(aa64_sve_f64mm, ARM_HWCAP2_A64_SVEF64MM); + GET_FEATURE_ID(aa64_sve_bf16, ARM_HWCAP2_A64_SVEBF16); + GET_FEATURE_ID(aa64_i8mm, ARM_HWCAP2_A64_I8MM); + GET_FEATURE_ID(aa64_bf16, ARM_HWCAP2_A64_BF16); + GET_FEATURE_ID(aa64_rndr, ARM_HWCAP2_A64_RNG); + GET_FEATURE_ID(aa64_bti, ARM_HWCAP2_A64_BTI); + GET_FEATURE_ID(aa64_mte, ARM_HWCAP2_A64_MTE); + GET_FEATURE_ID(aa64_mte3, ARM_HWCAP2_A64_MTE3); + GET_FEATURE_ID(aa64_sme, (ARM_HWCAP2_A64_SME | + ARM_HWCAP2_A64_SME_F32F32 | + ARM_HWCAP2_A64_SME_B16F32 | + ARM_HWCAP2_A64_SME_F16F32 | + ARM_HWCAP2_A64_SME_I8I32)); + GET_FEATURE_ID(aa64_sme_f64f64, ARM_HWCAP2_A64_SME_F64F64); + GET_FEATURE_ID(aa64_sme_i16i64, ARM_HWCAP2_A64_SME_I16I64); + GET_FEATURE_ID(aa64_sme_fa64, ARM_HWCAP2_A64_SME_FA64); + GET_FEATURE_ID(aa64_hbc, ARM_HWCAP2_A64_HBC); + GET_FEATURE_ID(aa64_mops, ARM_HWCAP2_A64_MOPS); + GET_FEATURE_ID(aa64_sve2p1, ARM_HWCAP2_A64_SVE2P1); + GET_FEATURE_ID(aa64_sme2, (ARM_HWCAP2_A64_SME2 | + ARM_HWCAP2_A64_SME_I16I32 | + ARM_HWCAP2_A64_SME_BI32I32)); + GET_FEATURE_ID(aa64_sme2p1, ARM_HWCAP2_A64_SME2P1); + GET_FEATURE_ID(aa64_sme_b16b16, ARM_HWCAP2_A64_SME_B16B16); + GET_FEATURE_ID(aa64_sme_f16f16, ARM_HWCAP2_A64_SME_F16F16); + GET_FEATURE_ID(aa64_sve_b16b16, ARM_HWCAP2_A64_SVE_B16B16); + + return hwcaps; +} + +const char *elf_hwcap_str(uint32_t bit) +{ + static const char * const hwcap_str[] = { + [__builtin_ctz(ARM_HWCAP_A64_FP )] = "fp", + [__builtin_ctz(ARM_HWCAP_A64_ASIMD )] = "asimd", + [__builtin_ctz(ARM_HWCAP_A64_EVTSTRM )] = "evtstrm", + [__builtin_ctz(ARM_HWCAP_A64_AES )] = "aes", + [__builtin_ctz(ARM_HWCAP_A64_PMULL )] = "pmull", + [__builtin_ctz(ARM_HWCAP_A64_SHA1 )] = "sha1", + [__builtin_ctz(ARM_HWCAP_A64_SHA2 )] = "sha2", + [__builtin_ctz(ARM_HWCAP_A64_CRC32 )] = "crc32", + [__builtin_ctz(ARM_HWCAP_A64_ATOMICS )] = "atomics", + [__builtin_ctz(ARM_HWCAP_A64_FPHP )] = "fphp", + [__builtin_ctz(ARM_HWCAP_A64_ASIMDHP )] = "asimdhp", + [__builtin_ctz(ARM_HWCAP_A64_CPUID )] = "cpuid", + [__builtin_ctz(ARM_HWCAP_A64_ASIMDRDM)] = "asimdrdm", + [__builtin_ctz(ARM_HWCAP_A64_JSCVT )] = "jscvt", + [__builtin_ctz(ARM_HWCAP_A64_FCMA )] = "fcma", + [__builtin_ctz(ARM_HWCAP_A64_LRCPC )] = "lrcpc", + [__builtin_ctz(ARM_HWCAP_A64_DCPOP )] = "dcpop", + [__builtin_ctz(ARM_HWCAP_A64_SHA3 )] = "sha3", + [__builtin_ctz(ARM_HWCAP_A64_SM3 )] = "sm3", + [__builtin_ctz(ARM_HWCAP_A64_SM4 )] = "sm4", + [__builtin_ctz(ARM_HWCAP_A64_ASIMDDP )] = "asimddp", + [__builtin_ctz(ARM_HWCAP_A64_SHA512 )] = "sha512", + [__builtin_ctz(ARM_HWCAP_A64_SVE )] = "sve", + [__builtin_ctz(ARM_HWCAP_A64_ASIMDFHM)] = "asimdfhm", + [__builtin_ctz(ARM_HWCAP_A64_DIT )] = "dit", + [__builtin_ctz(ARM_HWCAP_A64_USCAT )] = "uscat", + [__builtin_ctz(ARM_HWCAP_A64_ILRCPC )] = "ilrcpc", + [__builtin_ctz(ARM_HWCAP_A64_FLAGM )] = "flagm", + [__builtin_ctz(ARM_HWCAP_A64_SSBS )] = "ssbs", + [__builtin_ctz(ARM_HWCAP_A64_SB )] = "sb", + [__builtin_ctz(ARM_HWCAP_A64_PACA )] = "paca", + [__builtin_ctz(ARM_HWCAP_A64_PACG )] = "pacg", + [__builtin_ctzll(ARM_HWCAP_A64_GCS )] = "gcs", + [__builtin_ctzll(ARM_HWCAP_A64_CMPBR )] = "cmpbr", + [__builtin_ctzll(ARM_HWCAP_A64_FPRCVT)] = "fprcvt", + [__builtin_ctzll(ARM_HWCAP_A64_F8MM8 )] = "f8mm8", + [__builtin_ctzll(ARM_HWCAP_A64_F8MM4 )] = "f8mm4", + [__builtin_ctzll(ARM_HWCAP_A64_SVE_F16MM)] = "svef16mm", + [__builtin_ctzll(ARM_HWCAP_A64_SVE_ELTPERM)] = "sveeltperm", + [__builtin_ctzll(ARM_HWCAP_A64_SVE_AES2)] = "sveaes2", + [__builtin_ctzll(ARM_HWCAP_A64_SVE_BFSCALE)] = "svebfscale", + [__builtin_ctzll(ARM_HWCAP_A64_SVE2P2)] = "sve2p2", + [__builtin_ctzll(ARM_HWCAP_A64_SME2P2)] = "sme2p2", + [__builtin_ctzll(ARM_HWCAP_A64_SME_SBITPERM)] = "smesbitperm", + [__builtin_ctzll(ARM_HWCAP_A64_SME_AES)] = "smeaes", + [__builtin_ctzll(ARM_HWCAP_A64_SME_SFEXPA)] = "smesfexpa", + [__builtin_ctzll(ARM_HWCAP_A64_SME_STMOP)] = "smestmop", + [__builtin_ctzll(ARM_HWCAP_A64_SME_SMOP4)] = "smesmop4", + }; + + return bit < ARRAY_SIZE(hwcap_str) ? hwcap_str[bit] : NULL; +} + +const char *elf_hwcap2_str(uint32_t bit) +{ + static const char * const hwcap_str[] = { + [__builtin_ctz(ARM_HWCAP2_A64_DCPODP )] = "dcpodp", + [__builtin_ctz(ARM_HWCAP2_A64_SVE2 )] = "sve2", + [__builtin_ctz(ARM_HWCAP2_A64_SVEAES )] = "sveaes", + [__builtin_ctz(ARM_HWCAP2_A64_SVEPMULL )] = "svepmull", + [__builtin_ctz(ARM_HWCAP2_A64_SVEBITPERM )] = "svebitperm", + [__builtin_ctz(ARM_HWCAP2_A64_SVESHA3 )] = "svesha3", + [__builtin_ctz(ARM_HWCAP2_A64_SVESM4 )] = "svesm4", + [__builtin_ctz(ARM_HWCAP2_A64_FLAGM2 )] = "flagm2", + [__builtin_ctz(ARM_HWCAP2_A64_FRINT )] = "frint", + [__builtin_ctz(ARM_HWCAP2_A64_SVEI8MM )] = "svei8mm", + [__builtin_ctz(ARM_HWCAP2_A64_SVEF32MM )] = "svef32mm", + [__builtin_ctz(ARM_HWCAP2_A64_SVEF64MM )] = "svef64mm", + [__builtin_ctz(ARM_HWCAP2_A64_SVEBF16 )] = "svebf16", + [__builtin_ctz(ARM_HWCAP2_A64_I8MM )] = "i8mm", + [__builtin_ctz(ARM_HWCAP2_A64_BF16 )] = "bf16", + [__builtin_ctz(ARM_HWCAP2_A64_DGH )] = "dgh", + [__builtin_ctz(ARM_HWCAP2_A64_RNG )] = "rng", + [__builtin_ctz(ARM_HWCAP2_A64_BTI )] = "bti", + [__builtin_ctz(ARM_HWCAP2_A64_MTE )] = "mte", + [__builtin_ctz(ARM_HWCAP2_A64_ECV )] = "ecv", + [__builtin_ctz(ARM_HWCAP2_A64_AFP )] = "afp", + [__builtin_ctz(ARM_HWCAP2_A64_RPRES )] = "rpres", + [__builtin_ctz(ARM_HWCAP2_A64_MTE3 )] = "mte3", + [__builtin_ctz(ARM_HWCAP2_A64_SME )] = "sme", + [__builtin_ctz(ARM_HWCAP2_A64_SME_I16I64 )] = "smei16i64", + [__builtin_ctz(ARM_HWCAP2_A64_SME_F64F64 )] = "smef64f64", + [__builtin_ctz(ARM_HWCAP2_A64_SME_I8I32 )] = "smei8i32", + [__builtin_ctz(ARM_HWCAP2_A64_SME_F16F32 )] = "smef16f32", + [__builtin_ctz(ARM_HWCAP2_A64_SME_B16F32 )] = "smeb16f32", + [__builtin_ctz(ARM_HWCAP2_A64_SME_F32F32 )] = "smef32f32", + [__builtin_ctz(ARM_HWCAP2_A64_SME_FA64 )] = "smefa64", + [__builtin_ctz(ARM_HWCAP2_A64_WFXT )] = "wfxt", + [__builtin_ctzll(ARM_HWCAP2_A64_EBF16 )] = "ebf16", + [__builtin_ctzll(ARM_HWCAP2_A64_SVE_EBF16 )] = "sveebf16", + [__builtin_ctzll(ARM_HWCAP2_A64_CSSC )] = "cssc", + [__builtin_ctzll(ARM_HWCAP2_A64_RPRFM )] = "rprfm", + [__builtin_ctzll(ARM_HWCAP2_A64_SVE2P1 )] = "sve2p1", + [__builtin_ctzll(ARM_HWCAP2_A64_SME2 )] = "sme2", + [__builtin_ctzll(ARM_HWCAP2_A64_SME2P1 )] = "sme2p1", + [__builtin_ctzll(ARM_HWCAP2_A64_SME_I16I32 )] = "smei16i32", + [__builtin_ctzll(ARM_HWCAP2_A64_SME_BI32I32)] = "smebi32i32", + [__builtin_ctzll(ARM_HWCAP2_A64_SME_B16B16 )] = "smeb16b16", + [__builtin_ctzll(ARM_HWCAP2_A64_SME_F16F16 )] = "smef16f16", + [__builtin_ctzll(ARM_HWCAP2_A64_MOPS )] = "mops", + [__builtin_ctzll(ARM_HWCAP2_A64_HBC )] = "hbc", + [__builtin_ctzll(ARM_HWCAP2_A64_SVE_B16B16 )] = "sveb16b16", + [__builtin_ctzll(ARM_HWCAP2_A64_LRCPC3 )] = "lrcpc3", + [__builtin_ctzll(ARM_HWCAP2_A64_LSE128 )] = "lse128", + [__builtin_ctzll(ARM_HWCAP2_A64_FPMR )] = "fpmr", + [__builtin_ctzll(ARM_HWCAP2_A64_LUT )] = "lut", + [__builtin_ctzll(ARM_HWCAP2_A64_FAMINMAX )] = "faminmax", + [__builtin_ctzll(ARM_HWCAP2_A64_F8CVT )] = "f8cvt", + [__builtin_ctzll(ARM_HWCAP2_A64_F8FMA )] = "f8fma", + [__builtin_ctzll(ARM_HWCAP2_A64_F8DP4 )] = "f8dp4", + [__builtin_ctzll(ARM_HWCAP2_A64_F8DP2 )] = "f8dp2", + [__builtin_ctzll(ARM_HWCAP2_A64_F8E4M3 )] = "f8e4m3", + [__builtin_ctzll(ARM_HWCAP2_A64_F8E5M2 )] = "f8e5m2", + [__builtin_ctzll(ARM_HWCAP2_A64_SME_LUTV2 )] = "smelutv2", + [__builtin_ctzll(ARM_HWCAP2_A64_SME_F8F16 )] = "smef8f16", + [__builtin_ctzll(ARM_HWCAP2_A64_SME_F8F32 )] = "smef8f32", + [__builtin_ctzll(ARM_HWCAP2_A64_SME_SF8DP4 )] = "smesf8dp4", + [__builtin_ctzll(ARM_HWCAP2_A64_SME_SF8DP2 )] = "smesf8dp2", + [__builtin_ctzll(ARM_HWCAP2_A64_POE )] = "poe", + }; + + return bit < ARRAY_SIZE(hwcap_str) ? hwcap_str[bit] : NULL; +} diff --git a/linux-user/aarch64/target_elf.h b/linux-user/aarch64/target_elf.h index d955b3d07f..77108f3cb0 100644 --- a/linux-user/aarch64/target_elf.h +++ b/linux-user/aarch64/target_elf.h @@ -8,4 +8,7 @@ #ifndef AARCH64_TARGET_ELF_H #define AARCH64_TARGET_ELF_H +#define HAVE_ELF_HWCAP 1 +#define HAVE_ELF_HWCAP2 1 + #endif diff --git a/linux-user/arm/elfload.c b/linux-user/arm/elfload.c index b92442dfeb..c7561b005b 100644 --- a/linux-user/arm/elfload.c +++ b/linux-user/arm/elfload.c @@ -3,9 +3,170 @@ #include "qemu/osdep.h" #include "qemu.h" #include "loader.h" +#include "target/arm/cpu-features.h" const char *get_elf_cpu_model(uint32_t eflags) { return "any"; } + +enum +{ + ARM_HWCAP_ARM_SWP = 1 << 0, + ARM_HWCAP_ARM_HALF = 1 << 1, + ARM_HWCAP_ARM_THUMB = 1 << 2, + ARM_HWCAP_ARM_26BIT = 1 << 3, + ARM_HWCAP_ARM_FAST_MULT = 1 << 4, + ARM_HWCAP_ARM_FPA = 1 << 5, + ARM_HWCAP_ARM_VFP = 1 << 6, + ARM_HWCAP_ARM_EDSP = 1 << 7, + ARM_HWCAP_ARM_JAVA = 1 << 8, + ARM_HWCAP_ARM_IWMMXT = 1 << 9, + ARM_HWCAP_ARM_CRUNCH = 1 << 10, + ARM_HWCAP_ARM_THUMBEE = 1 << 11, + ARM_HWCAP_ARM_NEON = 1 << 12, + ARM_HWCAP_ARM_VFPv3 = 1 << 13, + ARM_HWCAP_ARM_VFPv3D16 = 1 << 14, + ARM_HWCAP_ARM_TLS = 1 << 15, + ARM_HWCAP_ARM_VFPv4 = 1 << 16, + ARM_HWCAP_ARM_IDIVA = 1 << 17, + ARM_HWCAP_ARM_IDIVT = 1 << 18, + ARM_HWCAP_ARM_VFPD32 = 1 << 19, + ARM_HWCAP_ARM_LPAE = 1 << 20, + ARM_HWCAP_ARM_EVTSTRM = 1 << 21, + ARM_HWCAP_ARM_FPHP = 1 << 22, + ARM_HWCAP_ARM_ASIMDHP = 1 << 23, + ARM_HWCAP_ARM_ASIMDDP = 1 << 24, + ARM_HWCAP_ARM_ASIMDFHM = 1 << 25, + ARM_HWCAP_ARM_ASIMDBF16 = 1 << 26, + ARM_HWCAP_ARM_I8MM = 1 << 27, +}; + +enum { + ARM_HWCAP2_ARM_AES = 1 << 0, + ARM_HWCAP2_ARM_PMULL = 1 << 1, + ARM_HWCAP2_ARM_SHA1 = 1 << 2, + ARM_HWCAP2_ARM_SHA2 = 1 << 3, + ARM_HWCAP2_ARM_CRC32 = 1 << 4, + ARM_HWCAP2_ARM_SB = 1 << 5, + ARM_HWCAP2_ARM_SSBS = 1 << 6, +}; + +abi_ulong get_elf_hwcap(CPUState *cs) +{ + ARMCPU *cpu = ARM_CPU(cs); + abi_ulong hwcaps = 0; + + hwcaps |= ARM_HWCAP_ARM_SWP; + hwcaps |= ARM_HWCAP_ARM_HALF; + hwcaps |= ARM_HWCAP_ARM_THUMB; + hwcaps |= ARM_HWCAP_ARM_FAST_MULT; + + /* probe for the extra features */ +#define GET_FEATURE(feat, hwcap) \ + do { if (arm_feature(&cpu->env, feat)) { hwcaps |= hwcap; } } while (0) + +#define GET_FEATURE_ID(feat, hwcap) \ + do { if (cpu_isar_feature(feat, cpu)) { hwcaps |= hwcap; } } while (0) + + /* EDSP is in v5TE and above, but all our v5 CPUs are v5TE */ + GET_FEATURE(ARM_FEATURE_V5, ARM_HWCAP_ARM_EDSP); + GET_FEATURE(ARM_FEATURE_IWMMXT, ARM_HWCAP_ARM_IWMMXT); + GET_FEATURE(ARM_FEATURE_THUMB2EE, ARM_HWCAP_ARM_THUMBEE); + GET_FEATURE(ARM_FEATURE_NEON, ARM_HWCAP_ARM_NEON); + GET_FEATURE(ARM_FEATURE_V6K, ARM_HWCAP_ARM_TLS); + GET_FEATURE(ARM_FEATURE_LPAE, ARM_HWCAP_ARM_LPAE); + GET_FEATURE_ID(aa32_arm_div, ARM_HWCAP_ARM_IDIVA); + GET_FEATURE_ID(aa32_thumb_div, ARM_HWCAP_ARM_IDIVT); + GET_FEATURE_ID(aa32_vfp, ARM_HWCAP_ARM_VFP); + + if (cpu_isar_feature(aa32_fpsp_v3, cpu) || + cpu_isar_feature(aa32_fpdp_v3, cpu)) { + hwcaps |= ARM_HWCAP_ARM_VFPv3; + if (cpu_isar_feature(aa32_simd_r32, cpu)) { + hwcaps |= ARM_HWCAP_ARM_VFPD32; + } else { + hwcaps |= ARM_HWCAP_ARM_VFPv3D16; + } + } + GET_FEATURE_ID(aa32_simdfmac, ARM_HWCAP_ARM_VFPv4); + /* + * MVFR1.FPHP and .SIMDHP must be in sync, and QEMU uses the same + * isar_feature function for both. The kernel reports them as two hwcaps. + */ + GET_FEATURE_ID(aa32_fp16_arith, ARM_HWCAP_ARM_FPHP); + GET_FEATURE_ID(aa32_fp16_arith, ARM_HWCAP_ARM_ASIMDHP); + GET_FEATURE_ID(aa32_dp, ARM_HWCAP_ARM_ASIMDDP); + GET_FEATURE_ID(aa32_fhm, ARM_HWCAP_ARM_ASIMDFHM); + GET_FEATURE_ID(aa32_bf16, ARM_HWCAP_ARM_ASIMDBF16); + GET_FEATURE_ID(aa32_i8mm, ARM_HWCAP_ARM_I8MM); + + return hwcaps; +} + +abi_ulong get_elf_hwcap2(CPUState *cs) +{ + ARMCPU *cpu = ARM_CPU(cs); + abi_ulong hwcaps = 0; + + GET_FEATURE_ID(aa32_aes, ARM_HWCAP2_ARM_AES); + GET_FEATURE_ID(aa32_pmull, ARM_HWCAP2_ARM_PMULL); + GET_FEATURE_ID(aa32_sha1, ARM_HWCAP2_ARM_SHA1); + GET_FEATURE_ID(aa32_sha2, ARM_HWCAP2_ARM_SHA2); + GET_FEATURE_ID(aa32_crc32, ARM_HWCAP2_ARM_CRC32); + GET_FEATURE_ID(aa32_sb, ARM_HWCAP2_ARM_SB); + GET_FEATURE_ID(aa32_ssbs, ARM_HWCAP2_ARM_SSBS); + return hwcaps; +} + +const char *elf_hwcap_str(uint32_t bit) +{ + static const char *hwcap_str[] = { + [__builtin_ctz(ARM_HWCAP_ARM_SWP )] = "swp", + [__builtin_ctz(ARM_HWCAP_ARM_HALF )] = "half", + [__builtin_ctz(ARM_HWCAP_ARM_THUMB )] = "thumb", + [__builtin_ctz(ARM_HWCAP_ARM_26BIT )] = "26bit", + [__builtin_ctz(ARM_HWCAP_ARM_FAST_MULT)] = "fast_mult", + [__builtin_ctz(ARM_HWCAP_ARM_FPA )] = "fpa", + [__builtin_ctz(ARM_HWCAP_ARM_VFP )] = "vfp", + [__builtin_ctz(ARM_HWCAP_ARM_EDSP )] = "edsp", + [__builtin_ctz(ARM_HWCAP_ARM_JAVA )] = "java", + [__builtin_ctz(ARM_HWCAP_ARM_IWMMXT )] = "iwmmxt", + [__builtin_ctz(ARM_HWCAP_ARM_CRUNCH )] = "crunch", + [__builtin_ctz(ARM_HWCAP_ARM_THUMBEE )] = "thumbee", + [__builtin_ctz(ARM_HWCAP_ARM_NEON )] = "neon", + [__builtin_ctz(ARM_HWCAP_ARM_VFPv3 )] = "vfpv3", + [__builtin_ctz(ARM_HWCAP_ARM_VFPv3D16 )] = "vfpv3d16", + [__builtin_ctz(ARM_HWCAP_ARM_TLS )] = "tls", + [__builtin_ctz(ARM_HWCAP_ARM_VFPv4 )] = "vfpv4", + [__builtin_ctz(ARM_HWCAP_ARM_IDIVA )] = "idiva", + [__builtin_ctz(ARM_HWCAP_ARM_IDIVT )] = "idivt", + [__builtin_ctz(ARM_HWCAP_ARM_VFPD32 )] = "vfpd32", + [__builtin_ctz(ARM_HWCAP_ARM_LPAE )] = "lpae", + [__builtin_ctz(ARM_HWCAP_ARM_EVTSTRM )] = "evtstrm", + [__builtin_ctz(ARM_HWCAP_ARM_FPHP )] = "fphp", + [__builtin_ctz(ARM_HWCAP_ARM_ASIMDHP )] = "asimdhp", + [__builtin_ctz(ARM_HWCAP_ARM_ASIMDDP )] = "asimddp", + [__builtin_ctz(ARM_HWCAP_ARM_ASIMDFHM )] = "asimdfhm", + [__builtin_ctz(ARM_HWCAP_ARM_ASIMDBF16)] = "asimdbf16", + [__builtin_ctz(ARM_HWCAP_ARM_I8MM )] = "i8mm", + }; + + return bit < ARRAY_SIZE(hwcap_str) ? hwcap_str[bit] : NULL; +} + +const char *elf_hwcap2_str(uint32_t bit) +{ + static const char *hwcap_str[] = { + [__builtin_ctz(ARM_HWCAP2_ARM_AES )] = "aes", + [__builtin_ctz(ARM_HWCAP2_ARM_PMULL)] = "pmull", + [__builtin_ctz(ARM_HWCAP2_ARM_SHA1 )] = "sha1", + [__builtin_ctz(ARM_HWCAP2_ARM_SHA2 )] = "sha2", + [__builtin_ctz(ARM_HWCAP2_ARM_CRC32)] = "crc32", + [__builtin_ctz(ARM_HWCAP2_ARM_SB )] = "sb", + [__builtin_ctz(ARM_HWCAP2_ARM_SSBS )] = "ssbs", + }; + + return bit < ARRAY_SIZE(hwcap_str) ? hwcap_str[bit] : NULL; +} diff --git a/linux-user/arm/target_elf.h b/linux-user/arm/target_elf.h index 2abb27a733..90470bd87b 100644 --- a/linux-user/arm/target_elf.h +++ b/linux-user/arm/target_elf.h @@ -8,4 +8,7 @@ #ifndef ARM_TARGET_ELF_H #define ARM_TARGET_ELF_H +#define HAVE_ELF_HWCAP 1 +#define HAVE_ELF_HWCAP2 1 + #endif diff --git a/linux-user/arm/target_proc.h b/linux-user/arm/target_proc.h index ac75af9ca6..a4cd6948c6 100644 --- a/linux-user/arm/target_proc.h +++ b/linux-user/arm/target_proc.h @@ -10,8 +10,8 @@ static int open_cpuinfo(CPUArchState *cpu_env, int fd) { ARMCPU *cpu = env_archcpu(cpu_env); int arch, midr_rev, midr_part, midr_var, midr_impl; - target_ulong elf_hwcap = get_elf_hwcap(); - target_ulong elf_hwcap2 = get_elf_hwcap2(); + target_ulong elf_hwcap = get_elf_hwcap(env_cpu(cpu_env)); + target_ulong elf_hwcap2 = get_elf_hwcap2(env_cpu(cpu_env)); const char *elf_name; int num_cpus, len_part, len_var; diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 0c62c249e9..149d1313c0 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -401,48 +401,6 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUARMState *en #define USE_ELF_CORE_DUMP #define ELF_EXEC_PAGESIZE 4096 -enum -{ - ARM_HWCAP_ARM_SWP = 1 << 0, - ARM_HWCAP_ARM_HALF = 1 << 1, - ARM_HWCAP_ARM_THUMB = 1 << 2, - ARM_HWCAP_ARM_26BIT = 1 << 3, - ARM_HWCAP_ARM_FAST_MULT = 1 << 4, - ARM_HWCAP_ARM_FPA = 1 << 5, - ARM_HWCAP_ARM_VFP = 1 << 6, - ARM_HWCAP_ARM_EDSP = 1 << 7, - ARM_HWCAP_ARM_JAVA = 1 << 8, - ARM_HWCAP_ARM_IWMMXT = 1 << 9, - ARM_HWCAP_ARM_CRUNCH = 1 << 10, - ARM_HWCAP_ARM_THUMBEE = 1 << 11, - ARM_HWCAP_ARM_NEON = 1 << 12, - ARM_HWCAP_ARM_VFPv3 = 1 << 13, - ARM_HWCAP_ARM_VFPv3D16 = 1 << 14, - ARM_HWCAP_ARM_TLS = 1 << 15, - ARM_HWCAP_ARM_VFPv4 = 1 << 16, - ARM_HWCAP_ARM_IDIVA = 1 << 17, - ARM_HWCAP_ARM_IDIVT = 1 << 18, - ARM_HWCAP_ARM_VFPD32 = 1 << 19, - ARM_HWCAP_ARM_LPAE = 1 << 20, - ARM_HWCAP_ARM_EVTSTRM = 1 << 21, - ARM_HWCAP_ARM_FPHP = 1 << 22, - ARM_HWCAP_ARM_ASIMDHP = 1 << 23, - ARM_HWCAP_ARM_ASIMDDP = 1 << 24, - ARM_HWCAP_ARM_ASIMDFHM = 1 << 25, - ARM_HWCAP_ARM_ASIMDBF16 = 1 << 26, - ARM_HWCAP_ARM_I8MM = 1 << 27, -}; - -enum { - ARM_HWCAP2_ARM_AES = 1 << 0, - ARM_HWCAP2_ARM_PMULL = 1 << 1, - ARM_HWCAP2_ARM_SHA1 = 1 << 2, - ARM_HWCAP2_ARM_SHA2 = 1 << 3, - ARM_HWCAP2_ARM_CRC32 = 1 << 4, - ARM_HWCAP2_ARM_SB = 1 << 5, - ARM_HWCAP2_ARM_SSBS = 1 << 6, -}; - /* The commpage only exists for 32 bit kernels */ #define HI_COMMPAGE (intptr_t)0xffff0f00u @@ -491,129 +449,8 @@ static bool init_guest_commpage(void) return true; } -#define ELF_HWCAP get_elf_hwcap() -#define ELF_HWCAP2 get_elf_hwcap2() - -uint32_t get_elf_hwcap(void) -{ - ARMCPU *cpu = ARM_CPU(thread_cpu); - uint32_t hwcaps = 0; - - hwcaps |= ARM_HWCAP_ARM_SWP; - hwcaps |= ARM_HWCAP_ARM_HALF; - hwcaps |= ARM_HWCAP_ARM_THUMB; - hwcaps |= ARM_HWCAP_ARM_FAST_MULT; - - /* probe for the extra features */ -#define GET_FEATURE(feat, hwcap) \ - do { if (arm_feature(&cpu->env, feat)) { hwcaps |= hwcap; } } while (0) - -#define GET_FEATURE_ID(feat, hwcap) \ - do { if (cpu_isar_feature(feat, cpu)) { hwcaps |= hwcap; } } while (0) - - /* EDSP is in v5TE and above, but all our v5 CPUs are v5TE */ - GET_FEATURE(ARM_FEATURE_V5, ARM_HWCAP_ARM_EDSP); - GET_FEATURE(ARM_FEATURE_IWMMXT, ARM_HWCAP_ARM_IWMMXT); - GET_FEATURE(ARM_FEATURE_THUMB2EE, ARM_HWCAP_ARM_THUMBEE); - GET_FEATURE(ARM_FEATURE_NEON, ARM_HWCAP_ARM_NEON); - GET_FEATURE(ARM_FEATURE_V6K, ARM_HWCAP_ARM_TLS); - GET_FEATURE(ARM_FEATURE_LPAE, ARM_HWCAP_ARM_LPAE); - GET_FEATURE_ID(aa32_arm_div, ARM_HWCAP_ARM_IDIVA); - GET_FEATURE_ID(aa32_thumb_div, ARM_HWCAP_ARM_IDIVT); - GET_FEATURE_ID(aa32_vfp, ARM_HWCAP_ARM_VFP); - - if (cpu_isar_feature(aa32_fpsp_v3, cpu) || - cpu_isar_feature(aa32_fpdp_v3, cpu)) { - hwcaps |= ARM_HWCAP_ARM_VFPv3; - if (cpu_isar_feature(aa32_simd_r32, cpu)) { - hwcaps |= ARM_HWCAP_ARM_VFPD32; - } else { - hwcaps |= ARM_HWCAP_ARM_VFPv3D16; - } - } - GET_FEATURE_ID(aa32_simdfmac, ARM_HWCAP_ARM_VFPv4); - /* - * MVFR1.FPHP and .SIMDHP must be in sync, and QEMU uses the same - * isar_feature function for both. The kernel reports them as two hwcaps. - */ - GET_FEATURE_ID(aa32_fp16_arith, ARM_HWCAP_ARM_FPHP); - GET_FEATURE_ID(aa32_fp16_arith, ARM_HWCAP_ARM_ASIMDHP); - GET_FEATURE_ID(aa32_dp, ARM_HWCAP_ARM_ASIMDDP); - GET_FEATURE_ID(aa32_fhm, ARM_HWCAP_ARM_ASIMDFHM); - GET_FEATURE_ID(aa32_bf16, ARM_HWCAP_ARM_ASIMDBF16); - GET_FEATURE_ID(aa32_i8mm, ARM_HWCAP_ARM_I8MM); - - return hwcaps; -} - -uint64_t get_elf_hwcap2(void) -{ - ARMCPU *cpu = ARM_CPU(thread_cpu); - uint64_t hwcaps = 0; - - GET_FEATURE_ID(aa32_aes, ARM_HWCAP2_ARM_AES); - GET_FEATURE_ID(aa32_pmull, ARM_HWCAP2_ARM_PMULL); - GET_FEATURE_ID(aa32_sha1, ARM_HWCAP2_ARM_SHA1); - GET_FEATURE_ID(aa32_sha2, ARM_HWCAP2_ARM_SHA2); - GET_FEATURE_ID(aa32_crc32, ARM_HWCAP2_ARM_CRC32); - GET_FEATURE_ID(aa32_sb, ARM_HWCAP2_ARM_SB); - GET_FEATURE_ID(aa32_ssbs, ARM_HWCAP2_ARM_SSBS); - return hwcaps; -} - -const char *elf_hwcap_str(uint32_t bit) -{ - static const char *hwcap_str[] = { - [__builtin_ctz(ARM_HWCAP_ARM_SWP )] = "swp", - [__builtin_ctz(ARM_HWCAP_ARM_HALF )] = "half", - [__builtin_ctz(ARM_HWCAP_ARM_THUMB )] = "thumb", - [__builtin_ctz(ARM_HWCAP_ARM_26BIT )] = "26bit", - [__builtin_ctz(ARM_HWCAP_ARM_FAST_MULT)] = "fast_mult", - [__builtin_ctz(ARM_HWCAP_ARM_FPA )] = "fpa", - [__builtin_ctz(ARM_HWCAP_ARM_VFP )] = "vfp", - [__builtin_ctz(ARM_HWCAP_ARM_EDSP )] = "edsp", - [__builtin_ctz(ARM_HWCAP_ARM_JAVA )] = "java", - [__builtin_ctz(ARM_HWCAP_ARM_IWMMXT )] = "iwmmxt", - [__builtin_ctz(ARM_HWCAP_ARM_CRUNCH )] = "crunch", - [__builtin_ctz(ARM_HWCAP_ARM_THUMBEE )] = "thumbee", - [__builtin_ctz(ARM_HWCAP_ARM_NEON )] = "neon", - [__builtin_ctz(ARM_HWCAP_ARM_VFPv3 )] = "vfpv3", - [__builtin_ctz(ARM_HWCAP_ARM_VFPv3D16 )] = "vfpv3d16", - [__builtin_ctz(ARM_HWCAP_ARM_TLS )] = "tls", - [__builtin_ctz(ARM_HWCAP_ARM_VFPv4 )] = "vfpv4", - [__builtin_ctz(ARM_HWCAP_ARM_IDIVA )] = "idiva", - [__builtin_ctz(ARM_HWCAP_ARM_IDIVT )] = "idivt", - [__builtin_ctz(ARM_HWCAP_ARM_VFPD32 )] = "vfpd32", - [__builtin_ctz(ARM_HWCAP_ARM_LPAE )] = "lpae", - [__builtin_ctz(ARM_HWCAP_ARM_EVTSTRM )] = "evtstrm", - [__builtin_ctz(ARM_HWCAP_ARM_FPHP )] = "fphp", - [__builtin_ctz(ARM_HWCAP_ARM_ASIMDHP )] = "asimdhp", - [__builtin_ctz(ARM_HWCAP_ARM_ASIMDDP )] = "asimddp", - [__builtin_ctz(ARM_HWCAP_ARM_ASIMDFHM )] = "asimdfhm", - [__builtin_ctz(ARM_HWCAP_ARM_ASIMDBF16)] = "asimdbf16", - [__builtin_ctz(ARM_HWCAP_ARM_I8MM )] = "i8mm", - }; - - return bit < ARRAY_SIZE(hwcap_str) ? hwcap_str[bit] : NULL; -} - -const char *elf_hwcap2_str(uint32_t bit) -{ - static const char *hwcap_str[] = { - [__builtin_ctz(ARM_HWCAP2_ARM_AES )] = "aes", - [__builtin_ctz(ARM_HWCAP2_ARM_PMULL)] = "pmull", - [__builtin_ctz(ARM_HWCAP2_ARM_SHA1 )] = "sha1", - [__builtin_ctz(ARM_HWCAP2_ARM_SHA2 )] = "sha2", - [__builtin_ctz(ARM_HWCAP2_ARM_CRC32)] = "crc32", - [__builtin_ctz(ARM_HWCAP2_ARM_SB )] = "sb", - [__builtin_ctz(ARM_HWCAP2_ARM_SSBS )] = "ssbs", - }; - - return bit < ARRAY_SIZE(hwcap_str) ? hwcap_str[bit] : NULL; -} - -#undef GET_FEATURE -#undef GET_FEATURE_ID +#define ELF_HWCAP get_elf_hwcap(thread_cpu) +#define ELF_HWCAP2 get_elf_hwcap2(thread_cpu) #define ELF_PLATFORM get_elf_platform() @@ -702,342 +539,8 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, #define USE_ELF_CORE_DUMP #define ELF_EXEC_PAGESIZE 4096 -enum { - ARM_HWCAP_A64_FP = 1 << 0, - ARM_HWCAP_A64_ASIMD = 1 << 1, - ARM_HWCAP_A64_EVTSTRM = 1 << 2, - ARM_HWCAP_A64_AES = 1 << 3, - ARM_HWCAP_A64_PMULL = 1 << 4, - ARM_HWCAP_A64_SHA1 = 1 << 5, - ARM_HWCAP_A64_SHA2 = 1 << 6, - ARM_HWCAP_A64_CRC32 = 1 << 7, - ARM_HWCAP_A64_ATOMICS = 1 << 8, - ARM_HWCAP_A64_FPHP = 1 << 9, - ARM_HWCAP_A64_ASIMDHP = 1 << 10, - ARM_HWCAP_A64_CPUID = 1 << 11, - ARM_HWCAP_A64_ASIMDRDM = 1 << 12, - ARM_HWCAP_A64_JSCVT = 1 << 13, - ARM_HWCAP_A64_FCMA = 1 << 14, - ARM_HWCAP_A64_LRCPC = 1 << 15, - ARM_HWCAP_A64_DCPOP = 1 << 16, - ARM_HWCAP_A64_SHA3 = 1 << 17, - ARM_HWCAP_A64_SM3 = 1 << 18, - ARM_HWCAP_A64_SM4 = 1 << 19, - ARM_HWCAP_A64_ASIMDDP = 1 << 20, - ARM_HWCAP_A64_SHA512 = 1 << 21, - ARM_HWCAP_A64_SVE = 1 << 22, - ARM_HWCAP_A64_ASIMDFHM = 1 << 23, - ARM_HWCAP_A64_DIT = 1 << 24, - ARM_HWCAP_A64_USCAT = 1 << 25, - ARM_HWCAP_A64_ILRCPC = 1 << 26, - ARM_HWCAP_A64_FLAGM = 1 << 27, - ARM_HWCAP_A64_SSBS = 1 << 28, - ARM_HWCAP_A64_SB = 1 << 29, - ARM_HWCAP_A64_PACA = 1 << 30, - ARM_HWCAP_A64_PACG = 1ULL << 31, - ARM_HWCAP_A64_GCS = 1ULL << 32, - ARM_HWCAP_A64_CMPBR = 1ULL << 33, - ARM_HWCAP_A64_FPRCVT = 1ULL << 34, - ARM_HWCAP_A64_F8MM8 = 1ULL << 35, - ARM_HWCAP_A64_F8MM4 = 1ULL << 36, - ARM_HWCAP_A64_SVE_F16MM = 1ULL << 37, - ARM_HWCAP_A64_SVE_ELTPERM = 1ULL << 38, - ARM_HWCAP_A64_SVE_AES2 = 1ULL << 39, - ARM_HWCAP_A64_SVE_BFSCALE = 1ULL << 40, - ARM_HWCAP_A64_SVE2P2 = 1ULL << 41, - ARM_HWCAP_A64_SME2P2 = 1ULL << 42, - ARM_HWCAP_A64_SME_SBITPERM = 1ULL << 43, - ARM_HWCAP_A64_SME_AES = 1ULL << 44, - ARM_HWCAP_A64_SME_SFEXPA = 1ULL << 45, - ARM_HWCAP_A64_SME_STMOP = 1ULL << 46, - ARM_HWCAP_A64_SME_SMOP4 = 1ULL << 47, - - ARM_HWCAP2_A64_DCPODP = 1 << 0, - ARM_HWCAP2_A64_SVE2 = 1 << 1, - ARM_HWCAP2_A64_SVEAES = 1 << 2, - ARM_HWCAP2_A64_SVEPMULL = 1 << 3, - ARM_HWCAP2_A64_SVEBITPERM = 1 << 4, - ARM_HWCAP2_A64_SVESHA3 = 1 << 5, - ARM_HWCAP2_A64_SVESM4 = 1 << 6, - ARM_HWCAP2_A64_FLAGM2 = 1 << 7, - ARM_HWCAP2_A64_FRINT = 1 << 8, - ARM_HWCAP2_A64_SVEI8MM = 1 << 9, - ARM_HWCAP2_A64_SVEF32MM = 1 << 10, - ARM_HWCAP2_A64_SVEF64MM = 1 << 11, - ARM_HWCAP2_A64_SVEBF16 = 1 << 12, - ARM_HWCAP2_A64_I8MM = 1 << 13, - ARM_HWCAP2_A64_BF16 = 1 << 14, - ARM_HWCAP2_A64_DGH = 1 << 15, - ARM_HWCAP2_A64_RNG = 1 << 16, - ARM_HWCAP2_A64_BTI = 1 << 17, - ARM_HWCAP2_A64_MTE = 1 << 18, - ARM_HWCAP2_A64_ECV = 1 << 19, - ARM_HWCAP2_A64_AFP = 1 << 20, - ARM_HWCAP2_A64_RPRES = 1 << 21, - ARM_HWCAP2_A64_MTE3 = 1 << 22, - ARM_HWCAP2_A64_SME = 1 << 23, - ARM_HWCAP2_A64_SME_I16I64 = 1 << 24, - ARM_HWCAP2_A64_SME_F64F64 = 1 << 25, - ARM_HWCAP2_A64_SME_I8I32 = 1 << 26, - ARM_HWCAP2_A64_SME_F16F32 = 1 << 27, - ARM_HWCAP2_A64_SME_B16F32 = 1 << 28, - ARM_HWCAP2_A64_SME_F32F32 = 1 << 29, - ARM_HWCAP2_A64_SME_FA64 = 1 << 30, - ARM_HWCAP2_A64_WFXT = 1ULL << 31, - ARM_HWCAP2_A64_EBF16 = 1ULL << 32, - ARM_HWCAP2_A64_SVE_EBF16 = 1ULL << 33, - ARM_HWCAP2_A64_CSSC = 1ULL << 34, - ARM_HWCAP2_A64_RPRFM = 1ULL << 35, - ARM_HWCAP2_A64_SVE2P1 = 1ULL << 36, - ARM_HWCAP2_A64_SME2 = 1ULL << 37, - ARM_HWCAP2_A64_SME2P1 = 1ULL << 38, - ARM_HWCAP2_A64_SME_I16I32 = 1ULL << 39, - ARM_HWCAP2_A64_SME_BI32I32 = 1ULL << 40, - ARM_HWCAP2_A64_SME_B16B16 = 1ULL << 41, - ARM_HWCAP2_A64_SME_F16F16 = 1ULL << 42, - ARM_HWCAP2_A64_MOPS = 1ULL << 43, - ARM_HWCAP2_A64_HBC = 1ULL << 44, - ARM_HWCAP2_A64_SVE_B16B16 = 1ULL << 45, - ARM_HWCAP2_A64_LRCPC3 = 1ULL << 46, - ARM_HWCAP2_A64_LSE128 = 1ULL << 47, - ARM_HWCAP2_A64_FPMR = 1ULL << 48, - ARM_HWCAP2_A64_LUT = 1ULL << 49, - ARM_HWCAP2_A64_FAMINMAX = 1ULL << 50, - ARM_HWCAP2_A64_F8CVT = 1ULL << 51, - ARM_HWCAP2_A64_F8FMA = 1ULL << 52, - ARM_HWCAP2_A64_F8DP4 = 1ULL << 53, - ARM_HWCAP2_A64_F8DP2 = 1ULL << 54, - ARM_HWCAP2_A64_F8E4M3 = 1ULL << 55, - ARM_HWCAP2_A64_F8E5M2 = 1ULL << 56, - ARM_HWCAP2_A64_SME_LUTV2 = 1ULL << 57, - ARM_HWCAP2_A64_SME_F8F16 = 1ULL << 58, - ARM_HWCAP2_A64_SME_F8F32 = 1ULL << 59, - ARM_HWCAP2_A64_SME_SF8FMA = 1ULL << 60, - ARM_HWCAP2_A64_SME_SF8DP4 = 1ULL << 61, - ARM_HWCAP2_A64_SME_SF8DP2 = 1ULL << 62, - ARM_HWCAP2_A64_POE = 1ULL << 63, -}; - -#define ELF_HWCAP get_elf_hwcap() -#define ELF_HWCAP2 get_elf_hwcap2() - -#define GET_FEATURE_ID(feat, hwcap) \ - do { if (cpu_isar_feature(feat, cpu)) { hwcaps |= hwcap; } } while (0) - -uint32_t get_elf_hwcap(void) -{ - ARMCPU *cpu = ARM_CPU(thread_cpu); - uint32_t hwcaps = 0; - - hwcaps |= ARM_HWCAP_A64_FP; - hwcaps |= ARM_HWCAP_A64_ASIMD; - hwcaps |= ARM_HWCAP_A64_CPUID; - - /* probe for the extra features */ - - GET_FEATURE_ID(aa64_aes, ARM_HWCAP_A64_AES); - GET_FEATURE_ID(aa64_pmull, ARM_HWCAP_A64_PMULL); - GET_FEATURE_ID(aa64_sha1, ARM_HWCAP_A64_SHA1); - GET_FEATURE_ID(aa64_sha256, ARM_HWCAP_A64_SHA2); - GET_FEATURE_ID(aa64_sha512, ARM_HWCAP_A64_SHA512); - GET_FEATURE_ID(aa64_crc32, ARM_HWCAP_A64_CRC32); - GET_FEATURE_ID(aa64_sha3, ARM_HWCAP_A64_SHA3); - GET_FEATURE_ID(aa64_sm3, ARM_HWCAP_A64_SM3); - GET_FEATURE_ID(aa64_sm4, ARM_HWCAP_A64_SM4); - GET_FEATURE_ID(aa64_fp16, ARM_HWCAP_A64_FPHP | ARM_HWCAP_A64_ASIMDHP); - GET_FEATURE_ID(aa64_atomics, ARM_HWCAP_A64_ATOMICS); - GET_FEATURE_ID(aa64_lse2, ARM_HWCAP_A64_USCAT); - GET_FEATURE_ID(aa64_rdm, ARM_HWCAP_A64_ASIMDRDM); - GET_FEATURE_ID(aa64_dp, ARM_HWCAP_A64_ASIMDDP); - GET_FEATURE_ID(aa64_fcma, ARM_HWCAP_A64_FCMA); - GET_FEATURE_ID(aa64_sve, ARM_HWCAP_A64_SVE); - GET_FEATURE_ID(aa64_pauth, ARM_HWCAP_A64_PACA | ARM_HWCAP_A64_PACG); - GET_FEATURE_ID(aa64_fhm, ARM_HWCAP_A64_ASIMDFHM); - GET_FEATURE_ID(aa64_dit, ARM_HWCAP_A64_DIT); - GET_FEATURE_ID(aa64_jscvt, ARM_HWCAP_A64_JSCVT); - GET_FEATURE_ID(aa64_sb, ARM_HWCAP_A64_SB); - GET_FEATURE_ID(aa64_condm_4, ARM_HWCAP_A64_FLAGM); - GET_FEATURE_ID(aa64_dcpop, ARM_HWCAP_A64_DCPOP); - GET_FEATURE_ID(aa64_rcpc_8_3, ARM_HWCAP_A64_LRCPC); - GET_FEATURE_ID(aa64_rcpc_8_4, ARM_HWCAP_A64_ILRCPC); - - return hwcaps; -} - -uint64_t get_elf_hwcap2(void) -{ - ARMCPU *cpu = ARM_CPU(thread_cpu); - uint64_t hwcaps = 0; - - GET_FEATURE_ID(aa64_dcpodp, ARM_HWCAP2_A64_DCPODP); - GET_FEATURE_ID(aa64_sve2, ARM_HWCAP2_A64_SVE2); - GET_FEATURE_ID(aa64_sve2_aes, ARM_HWCAP2_A64_SVEAES); - GET_FEATURE_ID(aa64_sve2_pmull128, ARM_HWCAP2_A64_SVEPMULL); - GET_FEATURE_ID(aa64_sve2_bitperm, ARM_HWCAP2_A64_SVEBITPERM); - GET_FEATURE_ID(aa64_sve2_sha3, ARM_HWCAP2_A64_SVESHA3); - GET_FEATURE_ID(aa64_sve2_sm4, ARM_HWCAP2_A64_SVESM4); - GET_FEATURE_ID(aa64_condm_5, ARM_HWCAP2_A64_FLAGM2); - GET_FEATURE_ID(aa64_frint, ARM_HWCAP2_A64_FRINT); - GET_FEATURE_ID(aa64_sve_i8mm, ARM_HWCAP2_A64_SVEI8MM); - GET_FEATURE_ID(aa64_sve_f32mm, ARM_HWCAP2_A64_SVEF32MM); - GET_FEATURE_ID(aa64_sve_f64mm, ARM_HWCAP2_A64_SVEF64MM); - GET_FEATURE_ID(aa64_sve_bf16, ARM_HWCAP2_A64_SVEBF16); - GET_FEATURE_ID(aa64_i8mm, ARM_HWCAP2_A64_I8MM); - GET_FEATURE_ID(aa64_bf16, ARM_HWCAP2_A64_BF16); - GET_FEATURE_ID(aa64_rndr, ARM_HWCAP2_A64_RNG); - GET_FEATURE_ID(aa64_bti, ARM_HWCAP2_A64_BTI); - GET_FEATURE_ID(aa64_mte, ARM_HWCAP2_A64_MTE); - GET_FEATURE_ID(aa64_mte3, ARM_HWCAP2_A64_MTE3); - GET_FEATURE_ID(aa64_sme, (ARM_HWCAP2_A64_SME | - ARM_HWCAP2_A64_SME_F32F32 | - ARM_HWCAP2_A64_SME_B16F32 | - ARM_HWCAP2_A64_SME_F16F32 | - ARM_HWCAP2_A64_SME_I8I32)); - GET_FEATURE_ID(aa64_sme_f64f64, ARM_HWCAP2_A64_SME_F64F64); - GET_FEATURE_ID(aa64_sme_i16i64, ARM_HWCAP2_A64_SME_I16I64); - GET_FEATURE_ID(aa64_sme_fa64, ARM_HWCAP2_A64_SME_FA64); - GET_FEATURE_ID(aa64_hbc, ARM_HWCAP2_A64_HBC); - GET_FEATURE_ID(aa64_mops, ARM_HWCAP2_A64_MOPS); - GET_FEATURE_ID(aa64_sve2p1, ARM_HWCAP2_A64_SVE2P1); - GET_FEATURE_ID(aa64_sme2, (ARM_HWCAP2_A64_SME2 | - ARM_HWCAP2_A64_SME_I16I32 | - ARM_HWCAP2_A64_SME_BI32I32)); - GET_FEATURE_ID(aa64_sme2p1, ARM_HWCAP2_A64_SME2P1); - GET_FEATURE_ID(aa64_sme_b16b16, ARM_HWCAP2_A64_SME_B16B16); - GET_FEATURE_ID(aa64_sme_f16f16, ARM_HWCAP2_A64_SME_F16F16); - GET_FEATURE_ID(aa64_sve_b16b16, ARM_HWCAP2_A64_SVE_B16B16); - - return hwcaps; -} - -const char *elf_hwcap_str(uint32_t bit) -{ - static const char * const hwcap_str[] = { - [__builtin_ctz(ARM_HWCAP_A64_FP )] = "fp", - [__builtin_ctz(ARM_HWCAP_A64_ASIMD )] = "asimd", - [__builtin_ctz(ARM_HWCAP_A64_EVTSTRM )] = "evtstrm", - [__builtin_ctz(ARM_HWCAP_A64_AES )] = "aes", - [__builtin_ctz(ARM_HWCAP_A64_PMULL )] = "pmull", - [__builtin_ctz(ARM_HWCAP_A64_SHA1 )] = "sha1", - [__builtin_ctz(ARM_HWCAP_A64_SHA2 )] = "sha2", - [__builtin_ctz(ARM_HWCAP_A64_CRC32 )] = "crc32", - [__builtin_ctz(ARM_HWCAP_A64_ATOMICS )] = "atomics", - [__builtin_ctz(ARM_HWCAP_A64_FPHP )] = "fphp", - [__builtin_ctz(ARM_HWCAP_A64_ASIMDHP )] = "asimdhp", - [__builtin_ctz(ARM_HWCAP_A64_CPUID )] = "cpuid", - [__builtin_ctz(ARM_HWCAP_A64_ASIMDRDM)] = "asimdrdm", - [__builtin_ctz(ARM_HWCAP_A64_JSCVT )] = "jscvt", - [__builtin_ctz(ARM_HWCAP_A64_FCMA )] = "fcma", - [__builtin_ctz(ARM_HWCAP_A64_LRCPC )] = "lrcpc", - [__builtin_ctz(ARM_HWCAP_A64_DCPOP )] = "dcpop", - [__builtin_ctz(ARM_HWCAP_A64_SHA3 )] = "sha3", - [__builtin_ctz(ARM_HWCAP_A64_SM3 )] = "sm3", - [__builtin_ctz(ARM_HWCAP_A64_SM4 )] = "sm4", - [__builtin_ctz(ARM_HWCAP_A64_ASIMDDP )] = "asimddp", - [__builtin_ctz(ARM_HWCAP_A64_SHA512 )] = "sha512", - [__builtin_ctz(ARM_HWCAP_A64_SVE )] = "sve", - [__builtin_ctz(ARM_HWCAP_A64_ASIMDFHM)] = "asimdfhm", - [__builtin_ctz(ARM_HWCAP_A64_DIT )] = "dit", - [__builtin_ctz(ARM_HWCAP_A64_USCAT )] = "uscat", - [__builtin_ctz(ARM_HWCAP_A64_ILRCPC )] = "ilrcpc", - [__builtin_ctz(ARM_HWCAP_A64_FLAGM )] = "flagm", - [__builtin_ctz(ARM_HWCAP_A64_SSBS )] = "ssbs", - [__builtin_ctz(ARM_HWCAP_A64_SB )] = "sb", - [__builtin_ctz(ARM_HWCAP_A64_PACA )] = "paca", - [__builtin_ctz(ARM_HWCAP_A64_PACG )] = "pacg", - [__builtin_ctzll(ARM_HWCAP_A64_GCS )] = "gcs", - [__builtin_ctzll(ARM_HWCAP_A64_CMPBR )] = "cmpbr", - [__builtin_ctzll(ARM_HWCAP_A64_FPRCVT)] = "fprcvt", - [__builtin_ctzll(ARM_HWCAP_A64_F8MM8 )] = "f8mm8", - [__builtin_ctzll(ARM_HWCAP_A64_F8MM4 )] = "f8mm4", - [__builtin_ctzll(ARM_HWCAP_A64_SVE_F16MM)] = "svef16mm", - [__builtin_ctzll(ARM_HWCAP_A64_SVE_ELTPERM)] = "sveeltperm", - [__builtin_ctzll(ARM_HWCAP_A64_SVE_AES2)] = "sveaes2", - [__builtin_ctzll(ARM_HWCAP_A64_SVE_BFSCALE)] = "svebfscale", - [__builtin_ctzll(ARM_HWCAP_A64_SVE2P2)] = "sve2p2", - [__builtin_ctzll(ARM_HWCAP_A64_SME2P2)] = "sme2p2", - [__builtin_ctzll(ARM_HWCAP_A64_SME_SBITPERM)] = "smesbitperm", - [__builtin_ctzll(ARM_HWCAP_A64_SME_AES)] = "smeaes", - [__builtin_ctzll(ARM_HWCAP_A64_SME_SFEXPA)] = "smesfexpa", - [__builtin_ctzll(ARM_HWCAP_A64_SME_STMOP)] = "smestmop", - [__builtin_ctzll(ARM_HWCAP_A64_SME_SMOP4)] = "smesmop4", - }; - - return bit < ARRAY_SIZE(hwcap_str) ? hwcap_str[bit] : NULL; -} - -const char *elf_hwcap2_str(uint32_t bit) -{ - static const char * const hwcap_str[] = { - [__builtin_ctz(ARM_HWCAP2_A64_DCPODP )] = "dcpodp", - [__builtin_ctz(ARM_HWCAP2_A64_SVE2 )] = "sve2", - [__builtin_ctz(ARM_HWCAP2_A64_SVEAES )] = "sveaes", - [__builtin_ctz(ARM_HWCAP2_A64_SVEPMULL )] = "svepmull", - [__builtin_ctz(ARM_HWCAP2_A64_SVEBITPERM )] = "svebitperm", - [__builtin_ctz(ARM_HWCAP2_A64_SVESHA3 )] = "svesha3", - [__builtin_ctz(ARM_HWCAP2_A64_SVESM4 )] = "svesm4", - [__builtin_ctz(ARM_HWCAP2_A64_FLAGM2 )] = "flagm2", - [__builtin_ctz(ARM_HWCAP2_A64_FRINT )] = "frint", - [__builtin_ctz(ARM_HWCAP2_A64_SVEI8MM )] = "svei8mm", - [__builtin_ctz(ARM_HWCAP2_A64_SVEF32MM )] = "svef32mm", - [__builtin_ctz(ARM_HWCAP2_A64_SVEF64MM )] = "svef64mm", - [__builtin_ctz(ARM_HWCAP2_A64_SVEBF16 )] = "svebf16", - [__builtin_ctz(ARM_HWCAP2_A64_I8MM )] = "i8mm", - [__builtin_ctz(ARM_HWCAP2_A64_BF16 )] = "bf16", - [__builtin_ctz(ARM_HWCAP2_A64_DGH )] = "dgh", - [__builtin_ctz(ARM_HWCAP2_A64_RNG )] = "rng", - [__builtin_ctz(ARM_HWCAP2_A64_BTI )] = "bti", - [__builtin_ctz(ARM_HWCAP2_A64_MTE )] = "mte", - [__builtin_ctz(ARM_HWCAP2_A64_ECV )] = "ecv", - [__builtin_ctz(ARM_HWCAP2_A64_AFP )] = "afp", - [__builtin_ctz(ARM_HWCAP2_A64_RPRES )] = "rpres", - [__builtin_ctz(ARM_HWCAP2_A64_MTE3 )] = "mte3", - [__builtin_ctz(ARM_HWCAP2_A64_SME )] = "sme", - [__builtin_ctz(ARM_HWCAP2_A64_SME_I16I64 )] = "smei16i64", - [__builtin_ctz(ARM_HWCAP2_A64_SME_F64F64 )] = "smef64f64", - [__builtin_ctz(ARM_HWCAP2_A64_SME_I8I32 )] = "smei8i32", - [__builtin_ctz(ARM_HWCAP2_A64_SME_F16F32 )] = "smef16f32", - [__builtin_ctz(ARM_HWCAP2_A64_SME_B16F32 )] = "smeb16f32", - [__builtin_ctz(ARM_HWCAP2_A64_SME_F32F32 )] = "smef32f32", - [__builtin_ctz(ARM_HWCAP2_A64_SME_FA64 )] = "smefa64", - [__builtin_ctz(ARM_HWCAP2_A64_WFXT )] = "wfxt", - [__builtin_ctzll(ARM_HWCAP2_A64_EBF16 )] = "ebf16", - [__builtin_ctzll(ARM_HWCAP2_A64_SVE_EBF16 )] = "sveebf16", - [__builtin_ctzll(ARM_HWCAP2_A64_CSSC )] = "cssc", - [__builtin_ctzll(ARM_HWCAP2_A64_RPRFM )] = "rprfm", - [__builtin_ctzll(ARM_HWCAP2_A64_SVE2P1 )] = "sve2p1", - [__builtin_ctzll(ARM_HWCAP2_A64_SME2 )] = "sme2", - [__builtin_ctzll(ARM_HWCAP2_A64_SME2P1 )] = "sme2p1", - [__builtin_ctzll(ARM_HWCAP2_A64_SME_I16I32 )] = "smei16i32", - [__builtin_ctzll(ARM_HWCAP2_A64_SME_BI32I32)] = "smebi32i32", - [__builtin_ctzll(ARM_HWCAP2_A64_SME_B16B16 )] = "smeb16b16", - [__builtin_ctzll(ARM_HWCAP2_A64_SME_F16F16 )] = "smef16f16", - [__builtin_ctzll(ARM_HWCAP2_A64_MOPS )] = "mops", - [__builtin_ctzll(ARM_HWCAP2_A64_HBC )] = "hbc", - [__builtin_ctzll(ARM_HWCAP2_A64_SVE_B16B16 )] = "sveb16b16", - [__builtin_ctzll(ARM_HWCAP2_A64_LRCPC3 )] = "lrcpc3", - [__builtin_ctzll(ARM_HWCAP2_A64_LSE128 )] = "lse128", - [__builtin_ctzll(ARM_HWCAP2_A64_FPMR )] = "fpmr", - [__builtin_ctzll(ARM_HWCAP2_A64_LUT )] = "lut", - [__builtin_ctzll(ARM_HWCAP2_A64_FAMINMAX )] = "faminmax", - [__builtin_ctzll(ARM_HWCAP2_A64_F8CVT )] = "f8cvt", - [__builtin_ctzll(ARM_HWCAP2_A64_F8FMA )] = "f8fma", - [__builtin_ctzll(ARM_HWCAP2_A64_F8DP4 )] = "f8dp4", - [__builtin_ctzll(ARM_HWCAP2_A64_F8DP2 )] = "f8dp2", - [__builtin_ctzll(ARM_HWCAP2_A64_F8E4M3 )] = "f8e4m3", - [__builtin_ctzll(ARM_HWCAP2_A64_F8E5M2 )] = "f8e5m2", - [__builtin_ctzll(ARM_HWCAP2_A64_SME_LUTV2 )] = "smelutv2", - [__builtin_ctzll(ARM_HWCAP2_A64_SME_F8F16 )] = "smef8f16", - [__builtin_ctzll(ARM_HWCAP2_A64_SME_F8F32 )] = "smef8f32", - [__builtin_ctzll(ARM_HWCAP2_A64_SME_SF8DP4 )] = "smesf8dp4", - [__builtin_ctzll(ARM_HWCAP2_A64_SME_SF8DP2 )] = "smesf8dp2", - [__builtin_ctzll(ARM_HWCAP2_A64_POE )] = "poe", - }; - - return bit < ARRAY_SIZE(hwcap_str) ? hwcap_str[bit] : NULL; -} - -#undef GET_FEATURE_ID +#define ELF_HWCAP get_elf_hwcap(thread_cpu) +#define ELF_HWCAP2 get_elf_hwcap2(thread_cpu) #if TARGET_BIG_ENDIAN # define VDSO_HEADER "vdso-be.c.inc" diff --git a/linux-user/loader.h b/linux-user/loader.h index 457bb36daa..151a06f5db 100644 --- a/linux-user/loader.h +++ b/linux-user/loader.h @@ -101,16 +101,14 @@ extern unsigned long guest_stack_size; /* Note that Elf32 and Elf64 use uint32_t for e_flags. */ const char *get_elf_cpu_model(uint32_t eflags); -#if defined(TARGET_I386) || defined(TARGET_X86_64) +#if defined(TARGET_I386) || defined(TARGET_X86_64) || defined(TARGET_ARM) abi_ulong get_elf_hwcap(CPUState *cs); +abi_ulong get_elf_hwcap2(CPUState *cs); #endif -#if defined(TARGET_S390X) || defined(TARGET_AARCH64) || defined(TARGET_ARM) +#if defined(TARGET_S390X) uint32_t get_elf_hwcap(void); +#endif const char *elf_hwcap_str(uint32_t bit); -#endif -#if defined(TARGET_AARCH64) || defined(TARGET_ARM) -uint64_t get_elf_hwcap2(void); const char *elf_hwcap2_str(uint32_t bit); -#endif #endif /* LINUX_USER_LOADER_H */ From e0f62c5a5b5c80d0a6b163a8113e5938b0b40d54 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 27 Jul 2025 21:03:12 -1000 Subject: [PATCH 0054/2396] linux-user: Move get_elf_hwcap to sparc/elfload.c Change the return type to abi_ulong, and pass in the cpu. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 30 +----------------------------- linux-user/loader.h | 3 ++- linux-user/sparc/elfload.c | 27 +++++++++++++++++++++++++++ linux-user/sparc/target_elf.h | 2 ++ 4 files changed, 32 insertions(+), 30 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 149d1313c0..16709865f7 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -565,35 +565,7 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, # define ELF_ARCH EM_SPARCV9 #endif -#include "elf.h" - -#define ELF_HWCAP get_elf_hwcap() - -static uint32_t get_elf_hwcap(void) -{ - /* There are not many sparc32 hwcap bits -- we have all of them. */ - uint32_t r = HWCAP_SPARC_FLUSH | HWCAP_SPARC_STBAR | - HWCAP_SPARC_SWAP | HWCAP_SPARC_MULDIV; - -#ifdef TARGET_SPARC64 - CPUSPARCState *env = cpu_env(thread_cpu); - uint32_t features = env->def.features; - - r |= HWCAP_SPARC_V9 | HWCAP_SPARC_V8PLUS; - /* 32x32 multiply and divide are efficient. */ - r |= HWCAP_SPARC_MUL32 | HWCAP_SPARC_DIV32; - /* We don't have an internal feature bit for this. */ - r |= HWCAP_SPARC_POPC; - r |= features & CPU_FEATURE_FSMULD ? HWCAP_SPARC_FSMULD : 0; - r |= features & CPU_FEATURE_VIS1 ? HWCAP_SPARC_VIS : 0; - r |= features & CPU_FEATURE_VIS2 ? HWCAP_SPARC_VIS2 : 0; - r |= features & CPU_FEATURE_FMAF ? HWCAP_SPARC_FMAF : 0; - r |= features & CPU_FEATURE_VIS3 ? HWCAP_SPARC_VIS3 : 0; - r |= features & CPU_FEATURE_IMA ? HWCAP_SPARC_IMA : 0; -#endif - - return r; -} +#define ELF_HWCAP get_elf_hwcap(thread_cpu) static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop) diff --git a/linux-user/loader.h b/linux-user/loader.h index 151a06f5db..2c8414e0e5 100644 --- a/linux-user/loader.h +++ b/linux-user/loader.h @@ -101,7 +101,8 @@ extern unsigned long guest_stack_size; /* Note that Elf32 and Elf64 use uint32_t for e_flags. */ const char *get_elf_cpu_model(uint32_t eflags); -#if defined(TARGET_I386) || defined(TARGET_X86_64) || defined(TARGET_ARM) +#if defined(TARGET_I386) || defined(TARGET_X86_64) || defined(TARGET_ARM) \ + || defined(TARGET_SPARC) abi_ulong get_elf_hwcap(CPUState *cs); abi_ulong get_elf_hwcap2(CPUState *cs); #endif diff --git a/linux-user/sparc/elfload.c b/linux-user/sparc/elfload.c index 243e6f9b66..32ca1b05b1 100644 --- a/linux-user/sparc/elfload.c +++ b/linux-user/sparc/elfload.c @@ -3,6 +3,7 @@ #include "qemu/osdep.h" #include "qemu.h" #include "loader.h" +#include "elf.h" const char *get_elf_cpu_model(uint32_t eflags) @@ -13,3 +14,29 @@ const char *get_elf_cpu_model(uint32_t eflags) return "Fujitsu MB86904"; #endif } + +abi_ulong get_elf_hwcap(CPUState *cs) +{ + /* There are not many sparc32 hwcap bits -- we have all of them. */ + uint32_t r = HWCAP_SPARC_FLUSH | HWCAP_SPARC_STBAR | + HWCAP_SPARC_SWAP | HWCAP_SPARC_MULDIV; + +#ifdef TARGET_SPARC64 + CPUSPARCState *env = cpu_env(cs); + uint32_t features = env->def.features; + + r |= HWCAP_SPARC_V9 | HWCAP_SPARC_V8PLUS; + /* 32x32 multiply and divide are efficient. */ + r |= HWCAP_SPARC_MUL32 | HWCAP_SPARC_DIV32; + /* We don't have an internal feature bit for this. */ + r |= HWCAP_SPARC_POPC; + r |= features & CPU_FEATURE_FSMULD ? HWCAP_SPARC_FSMULD : 0; + r |= features & CPU_FEATURE_VIS1 ? HWCAP_SPARC_VIS : 0; + r |= features & CPU_FEATURE_VIS2 ? HWCAP_SPARC_VIS2 : 0; + r |= features & CPU_FEATURE_FMAF ? HWCAP_SPARC_FMAF : 0; + r |= features & CPU_FEATURE_VIS3 ? HWCAP_SPARC_VIS3 : 0; + r |= features & CPU_FEATURE_IMA ? HWCAP_SPARC_IMA : 0; +#endif + + return r; +} diff --git a/linux-user/sparc/target_elf.h b/linux-user/sparc/target_elf.h index 7e46748d26..b7544db0a1 100644 --- a/linux-user/sparc/target_elf.h +++ b/linux-user/sparc/target_elf.h @@ -8,4 +8,6 @@ #ifndef SPARC_TARGET_ELF_H #define SPARC_TARGET_ELF_H +#define HAVE_ELF_HWCAP 1 + #endif From ea9d69159e013a5757e20bea0266b7c17303d961 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 27 Jul 2025 21:12:54 -1000 Subject: [PATCH 0055/2396] linux-user: Move hwcap functions to ppc/elfload.c Change the return type to abi_ulong, and pass in the cpu. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 116 +----------------------------------- linux-user/loader.h | 2 +- linux-user/ppc/elfload.c | 116 ++++++++++++++++++++++++++++++++++++ linux-user/ppc/target_elf.h | 3 + 4 files changed, 122 insertions(+), 115 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 16709865f7..843b1f7b6c 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -598,120 +598,8 @@ static inline void init_thread(struct target_pt_regs *regs, #define ELF_ARCH EM_PPC -/* Feature masks for the Aux Vector Hardware Capabilities (AT_HWCAP). - See arch/powerpc/include/asm/cputable.h. */ -enum { - QEMU_PPC_FEATURE_32 = 0x80000000, - QEMU_PPC_FEATURE_64 = 0x40000000, - QEMU_PPC_FEATURE_601_INSTR = 0x20000000, - QEMU_PPC_FEATURE_HAS_ALTIVEC = 0x10000000, - QEMU_PPC_FEATURE_HAS_FPU = 0x08000000, - QEMU_PPC_FEATURE_HAS_MMU = 0x04000000, - QEMU_PPC_FEATURE_HAS_4xxMAC = 0x02000000, - QEMU_PPC_FEATURE_UNIFIED_CACHE = 0x01000000, - QEMU_PPC_FEATURE_HAS_SPE = 0x00800000, - QEMU_PPC_FEATURE_HAS_EFP_SINGLE = 0x00400000, - QEMU_PPC_FEATURE_HAS_EFP_DOUBLE = 0x00200000, - QEMU_PPC_FEATURE_NO_TB = 0x00100000, - QEMU_PPC_FEATURE_POWER4 = 0x00080000, - QEMU_PPC_FEATURE_POWER5 = 0x00040000, - QEMU_PPC_FEATURE_POWER5_PLUS = 0x00020000, - QEMU_PPC_FEATURE_CELL = 0x00010000, - QEMU_PPC_FEATURE_BOOKE = 0x00008000, - QEMU_PPC_FEATURE_SMT = 0x00004000, - QEMU_PPC_FEATURE_ICACHE_SNOOP = 0x00002000, - QEMU_PPC_FEATURE_ARCH_2_05 = 0x00001000, - QEMU_PPC_FEATURE_PA6T = 0x00000800, - QEMU_PPC_FEATURE_HAS_DFP = 0x00000400, - QEMU_PPC_FEATURE_POWER6_EXT = 0x00000200, - QEMU_PPC_FEATURE_ARCH_2_06 = 0x00000100, - QEMU_PPC_FEATURE_HAS_VSX = 0x00000080, - QEMU_PPC_FEATURE_PSERIES_PERFMON_COMPAT = 0x00000040, - - QEMU_PPC_FEATURE_TRUE_LE = 0x00000002, - QEMU_PPC_FEATURE_PPC_LE = 0x00000001, - - /* Feature definitions in AT_HWCAP2. */ - QEMU_PPC_FEATURE2_ARCH_2_07 = 0x80000000, /* ISA 2.07 */ - QEMU_PPC_FEATURE2_HAS_HTM = 0x40000000, /* Hardware Transactional Memory */ - QEMU_PPC_FEATURE2_HAS_DSCR = 0x20000000, /* Data Stream Control Register */ - QEMU_PPC_FEATURE2_HAS_EBB = 0x10000000, /* Event Base Branching */ - QEMU_PPC_FEATURE2_HAS_ISEL = 0x08000000, /* Integer Select */ - QEMU_PPC_FEATURE2_HAS_TAR = 0x04000000, /* Target Address Register */ - QEMU_PPC_FEATURE2_VEC_CRYPTO = 0x02000000, - QEMU_PPC_FEATURE2_HTM_NOSC = 0x01000000, - QEMU_PPC_FEATURE2_ARCH_3_00 = 0x00800000, /* ISA 3.00 */ - QEMU_PPC_FEATURE2_HAS_IEEE128 = 0x00400000, /* VSX IEEE Bin Float 128-bit */ - QEMU_PPC_FEATURE2_DARN = 0x00200000, /* darn random number insn */ - QEMU_PPC_FEATURE2_SCV = 0x00100000, /* scv syscall */ - QEMU_PPC_FEATURE2_HTM_NO_SUSPEND = 0x00080000, /* TM w/o suspended state */ - QEMU_PPC_FEATURE2_ARCH_3_1 = 0x00040000, /* ISA 3.1 */ - QEMU_PPC_FEATURE2_MMA = 0x00020000, /* Matrix-Multiply Assist */ -}; - -#define ELF_HWCAP get_elf_hwcap() - -static uint32_t get_elf_hwcap(void) -{ - PowerPCCPU *cpu = POWERPC_CPU(thread_cpu); - uint32_t features = 0; - - /* We don't have to be terribly complete here; the high points are - Altivec/FP/SPE support. Anything else is just a bonus. */ -#define GET_FEATURE(flag, feature) \ - do { if (cpu->env.insns_flags & flag) { features |= feature; } } while (0) -#define GET_FEATURE2(flags, feature) \ - do { \ - if ((cpu->env.insns_flags2 & flags) == flags) { \ - features |= feature; \ - } \ - } while (0) - GET_FEATURE(PPC_64B, QEMU_PPC_FEATURE_64); - GET_FEATURE(PPC_FLOAT, QEMU_PPC_FEATURE_HAS_FPU); - GET_FEATURE(PPC_ALTIVEC, QEMU_PPC_FEATURE_HAS_ALTIVEC); - GET_FEATURE(PPC_SPE, QEMU_PPC_FEATURE_HAS_SPE); - GET_FEATURE(PPC_SPE_SINGLE, QEMU_PPC_FEATURE_HAS_EFP_SINGLE); - GET_FEATURE(PPC_SPE_DOUBLE, QEMU_PPC_FEATURE_HAS_EFP_DOUBLE); - GET_FEATURE(PPC_BOOKE, QEMU_PPC_FEATURE_BOOKE); - GET_FEATURE(PPC_405_MAC, QEMU_PPC_FEATURE_HAS_4xxMAC); - GET_FEATURE2(PPC2_DFP, QEMU_PPC_FEATURE_HAS_DFP); - GET_FEATURE2(PPC2_VSX, QEMU_PPC_FEATURE_HAS_VSX); - GET_FEATURE2((PPC2_PERM_ISA206 | PPC2_DIVE_ISA206 | PPC2_ATOMIC_ISA206 | - PPC2_FP_CVT_ISA206 | PPC2_FP_TST_ISA206), - QEMU_PPC_FEATURE_ARCH_2_06); -#undef GET_FEATURE -#undef GET_FEATURE2 - - return features; -} - -#define ELF_HWCAP2 get_elf_hwcap2() - -static uint32_t get_elf_hwcap2(void) -{ - PowerPCCPU *cpu = POWERPC_CPU(thread_cpu); - uint32_t features = 0; - -#define GET_FEATURE(flag, feature) \ - do { if (cpu->env.insns_flags & flag) { features |= feature; } } while (0) -#define GET_FEATURE2(flag, feature) \ - do { if (cpu->env.insns_flags2 & flag) { features |= feature; } } while (0) - - GET_FEATURE(PPC_ISEL, QEMU_PPC_FEATURE2_HAS_ISEL); - GET_FEATURE2(PPC2_BCTAR_ISA207, QEMU_PPC_FEATURE2_HAS_TAR); - GET_FEATURE2((PPC2_BCTAR_ISA207 | PPC2_LSQ_ISA207 | PPC2_ALTIVEC_207 | - PPC2_ISA207S), QEMU_PPC_FEATURE2_ARCH_2_07 | - QEMU_PPC_FEATURE2_VEC_CRYPTO); - GET_FEATURE2(PPC2_ISA300, QEMU_PPC_FEATURE2_ARCH_3_00 | - QEMU_PPC_FEATURE2_DARN | QEMU_PPC_FEATURE2_HAS_IEEE128); - GET_FEATURE2(PPC2_ISA310, QEMU_PPC_FEATURE2_ARCH_3_1 | - QEMU_PPC_FEATURE2_MMA); - -#undef GET_FEATURE -#undef GET_FEATURE2 - - return features; -} +#define ELF_HWCAP get_elf_hwcap(thread_cpu) +#define ELF_HWCAP2 get_elf_hwcap2(thread_cpu) /* * The requirements here are: diff --git a/linux-user/loader.h b/linux-user/loader.h index 2c8414e0e5..818c5e6d7d 100644 --- a/linux-user/loader.h +++ b/linux-user/loader.h @@ -102,7 +102,7 @@ extern unsigned long guest_stack_size; const char *get_elf_cpu_model(uint32_t eflags); #if defined(TARGET_I386) || defined(TARGET_X86_64) || defined(TARGET_ARM) \ - || defined(TARGET_SPARC) + || defined(TARGET_SPARC) || defined(TARGET_PPC) abi_ulong get_elf_hwcap(CPUState *cs); abi_ulong get_elf_hwcap2(CPUState *cs); #endif diff --git a/linux-user/ppc/elfload.c b/linux-user/ppc/elfload.c index 7775dc06fa..a214675650 100644 --- a/linux-user/ppc/elfload.c +++ b/linux-user/ppc/elfload.c @@ -13,3 +13,119 @@ const char *get_elf_cpu_model(uint32_t eflags) return "750"; #endif } + +/* + * Feature masks for the Aux Vector Hardware Capabilities (AT_HWCAP). + * See arch/powerpc/include/asm/cputable.h. + */ +enum { + QEMU_PPC_FEATURE_32 = 0x80000000, + QEMU_PPC_FEATURE_64 = 0x40000000, + QEMU_PPC_FEATURE_601_INSTR = 0x20000000, + QEMU_PPC_FEATURE_HAS_ALTIVEC = 0x10000000, + QEMU_PPC_FEATURE_HAS_FPU = 0x08000000, + QEMU_PPC_FEATURE_HAS_MMU = 0x04000000, + QEMU_PPC_FEATURE_HAS_4xxMAC = 0x02000000, + QEMU_PPC_FEATURE_UNIFIED_CACHE = 0x01000000, + QEMU_PPC_FEATURE_HAS_SPE = 0x00800000, + QEMU_PPC_FEATURE_HAS_EFP_SINGLE = 0x00400000, + QEMU_PPC_FEATURE_HAS_EFP_DOUBLE = 0x00200000, + QEMU_PPC_FEATURE_NO_TB = 0x00100000, + QEMU_PPC_FEATURE_POWER4 = 0x00080000, + QEMU_PPC_FEATURE_POWER5 = 0x00040000, + QEMU_PPC_FEATURE_POWER5_PLUS = 0x00020000, + QEMU_PPC_FEATURE_CELL = 0x00010000, + QEMU_PPC_FEATURE_BOOKE = 0x00008000, + QEMU_PPC_FEATURE_SMT = 0x00004000, + QEMU_PPC_FEATURE_ICACHE_SNOOP = 0x00002000, + QEMU_PPC_FEATURE_ARCH_2_05 = 0x00001000, + QEMU_PPC_FEATURE_PA6T = 0x00000800, + QEMU_PPC_FEATURE_HAS_DFP = 0x00000400, + QEMU_PPC_FEATURE_POWER6_EXT = 0x00000200, + QEMU_PPC_FEATURE_ARCH_2_06 = 0x00000100, + QEMU_PPC_FEATURE_HAS_VSX = 0x00000080, + QEMU_PPC_FEATURE_PSERIES_PERFMON_COMPAT = 0x00000040, + + QEMU_PPC_FEATURE_TRUE_LE = 0x00000002, + QEMU_PPC_FEATURE_PPC_LE = 0x00000001, + + /* Feature definitions in AT_HWCAP2. */ + QEMU_PPC_FEATURE2_ARCH_2_07 = 0x80000000, /* ISA 2.07 */ + QEMU_PPC_FEATURE2_HAS_HTM = 0x40000000, /* Hardware Transactional Memory */ + QEMU_PPC_FEATURE2_HAS_DSCR = 0x20000000, /* Data Stream Control Register */ + QEMU_PPC_FEATURE2_HAS_EBB = 0x10000000, /* Event Base Branching */ + QEMU_PPC_FEATURE2_HAS_ISEL = 0x08000000, /* Integer Select */ + QEMU_PPC_FEATURE2_HAS_TAR = 0x04000000, /* Target Address Register */ + QEMU_PPC_FEATURE2_VEC_CRYPTO = 0x02000000, + QEMU_PPC_FEATURE2_HTM_NOSC = 0x01000000, + QEMU_PPC_FEATURE2_ARCH_3_00 = 0x00800000, /* ISA 3.00 */ + QEMU_PPC_FEATURE2_HAS_IEEE128 = 0x00400000, /* VSX IEEE Bin Float 128-bit */ + QEMU_PPC_FEATURE2_DARN = 0x00200000, /* darn random number insn */ + QEMU_PPC_FEATURE2_SCV = 0x00100000, /* scv syscall */ + QEMU_PPC_FEATURE2_HTM_NO_SUSPEND = 0x00080000, /* TM w/o suspended state */ + QEMU_PPC_FEATURE2_ARCH_3_1 = 0x00040000, /* ISA 3.1 */ + QEMU_PPC_FEATURE2_MMA = 0x00020000, /* Matrix-Multiply Assist */ +}; + +abi_ulong get_elf_hwcap(CPUState *cs) +{ + PowerPCCPU *cpu = POWERPC_CPU(cs); + uint32_t features = 0; + + /* + * We don't have to be terribly complete here; the high points are + * Altivec/FP/SPE support. Anything else is just a bonus. + */ +#define GET_FEATURE(flag, feature) \ + do { if (cpu->env.insns_flags & flag) { features |= feature; } } while (0) +#define GET_FEATURE2(flags, feature) \ + do { \ + if ((cpu->env.insns_flags2 & flags) == flags) { \ + features |= feature; \ + } \ + } while (0) + GET_FEATURE(PPC_64B, QEMU_PPC_FEATURE_64); + GET_FEATURE(PPC_FLOAT, QEMU_PPC_FEATURE_HAS_FPU); + GET_FEATURE(PPC_ALTIVEC, QEMU_PPC_FEATURE_HAS_ALTIVEC); + GET_FEATURE(PPC_SPE, QEMU_PPC_FEATURE_HAS_SPE); + GET_FEATURE(PPC_SPE_SINGLE, QEMU_PPC_FEATURE_HAS_EFP_SINGLE); + GET_FEATURE(PPC_SPE_DOUBLE, QEMU_PPC_FEATURE_HAS_EFP_DOUBLE); + GET_FEATURE(PPC_BOOKE, QEMU_PPC_FEATURE_BOOKE); + GET_FEATURE(PPC_405_MAC, QEMU_PPC_FEATURE_HAS_4xxMAC); + GET_FEATURE2(PPC2_DFP, QEMU_PPC_FEATURE_HAS_DFP); + GET_FEATURE2(PPC2_VSX, QEMU_PPC_FEATURE_HAS_VSX); + GET_FEATURE2((PPC2_PERM_ISA206 | PPC2_DIVE_ISA206 | PPC2_ATOMIC_ISA206 | + PPC2_FP_CVT_ISA206 | PPC2_FP_TST_ISA206), + QEMU_PPC_FEATURE_ARCH_2_06); + +#undef GET_FEATURE +#undef GET_FEATURE2 + + return features; +} + +abi_ulong get_elf_hwcap2(CPUState *cs) +{ + PowerPCCPU *cpu = POWERPC_CPU(cs); + uint32_t features = 0; + +#define GET_FEATURE(flag, feature) \ + do { if (cpu->env.insns_flags & flag) { features |= feature; } } while (0) +#define GET_FEATURE2(flag, feature) \ + do { if (cpu->env.insns_flags2 & flag) { features |= feature; } } while (0) + + GET_FEATURE(PPC_ISEL, QEMU_PPC_FEATURE2_HAS_ISEL); + GET_FEATURE2(PPC2_BCTAR_ISA207, QEMU_PPC_FEATURE2_HAS_TAR); + GET_FEATURE2((PPC2_BCTAR_ISA207 | PPC2_LSQ_ISA207 | PPC2_ALTIVEC_207 | + PPC2_ISA207S), QEMU_PPC_FEATURE2_ARCH_2_07 | + QEMU_PPC_FEATURE2_VEC_CRYPTO); + GET_FEATURE2(PPC2_ISA300, QEMU_PPC_FEATURE2_ARCH_3_00 | + QEMU_PPC_FEATURE2_DARN | QEMU_PPC_FEATURE2_HAS_IEEE128); + GET_FEATURE2(PPC2_ISA310, QEMU_PPC_FEATURE2_ARCH_3_1 | + QEMU_PPC_FEATURE2_MMA); + +#undef GET_FEATURE +#undef GET_FEATURE2 + + return features; +} diff --git a/linux-user/ppc/target_elf.h b/linux-user/ppc/target_elf.h index 8c0a8ea431..4203a89d66 100644 --- a/linux-user/ppc/target_elf.h +++ b/linux-user/ppc/target_elf.h @@ -8,4 +8,7 @@ #ifndef PPC_TARGET_ELF_H #define PPC_TARGET_ELF_H +#define HAVE_ELF_HWCAP 1 +#define HAVE_ELF_HWCAP2 1 + #endif From 184e74b236df9ad42ddbf92011713259cd17caf1 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 27 Jul 2025 21:17:08 -1000 Subject: [PATCH 0056/2396] linux-user: Move get_elf_hwcap to loongarch64/elfload.c Change the return type to abi_ulong, and pass in the cpu. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 49 +---------------------------- linux-user/loader.h | 3 +- linux-user/loongarch64/elfload.c | 47 +++++++++++++++++++++++++++ linux-user/loongarch64/target_elf.h | 2 ++ 4 files changed, 52 insertions(+), 49 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 843b1f7b6c..574b37a22c 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -725,54 +725,7 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, #define USE_ELF_CORE_DUMP #define ELF_EXEC_PAGESIZE 4096 -#define ELF_HWCAP get_elf_hwcap() - -/* See arch/loongarch/include/uapi/asm/hwcap.h */ -enum { - HWCAP_LOONGARCH_CPUCFG = (1 << 0), - HWCAP_LOONGARCH_LAM = (1 << 1), - HWCAP_LOONGARCH_UAL = (1 << 2), - HWCAP_LOONGARCH_FPU = (1 << 3), - HWCAP_LOONGARCH_LSX = (1 << 4), - HWCAP_LOONGARCH_LASX = (1 << 5), - HWCAP_LOONGARCH_CRC32 = (1 << 6), - HWCAP_LOONGARCH_COMPLEX = (1 << 7), - HWCAP_LOONGARCH_CRYPTO = (1 << 8), - HWCAP_LOONGARCH_LVZ = (1 << 9), - HWCAP_LOONGARCH_LBT_X86 = (1 << 10), - HWCAP_LOONGARCH_LBT_ARM = (1 << 11), - HWCAP_LOONGARCH_LBT_MIPS = (1 << 12), -}; - -static uint32_t get_elf_hwcap(void) -{ - LoongArchCPU *cpu = LOONGARCH_CPU(thread_cpu); - uint32_t hwcaps = 0; - - hwcaps |= HWCAP_LOONGARCH_CRC32; - - if (FIELD_EX32(cpu->env.cpucfg[1], CPUCFG1, UAL)) { - hwcaps |= HWCAP_LOONGARCH_UAL; - } - - if (FIELD_EX32(cpu->env.cpucfg[2], CPUCFG2, FP)) { - hwcaps |= HWCAP_LOONGARCH_FPU; - } - - if (FIELD_EX32(cpu->env.cpucfg[2], CPUCFG2, LAM)) { - hwcaps |= HWCAP_LOONGARCH_LAM; - } - - if (FIELD_EX32(cpu->env.cpucfg[2], CPUCFG2, LSX)) { - hwcaps |= HWCAP_LOONGARCH_LSX; - } - - if (FIELD_EX32(cpu->env.cpucfg[2], CPUCFG2, LASX)) { - hwcaps |= HWCAP_LOONGARCH_LASX; - } - - return hwcaps; -} +#define ELF_HWCAP get_elf_hwcap(thread_cpu) #define ELF_PLATFORM "loongarch" diff --git a/linux-user/loader.h b/linux-user/loader.h index 818c5e6d7d..92b6d41145 100644 --- a/linux-user/loader.h +++ b/linux-user/loader.h @@ -102,7 +102,8 @@ extern unsigned long guest_stack_size; const char *get_elf_cpu_model(uint32_t eflags); #if defined(TARGET_I386) || defined(TARGET_X86_64) || defined(TARGET_ARM) \ - || defined(TARGET_SPARC) || defined(TARGET_PPC) + || defined(TARGET_SPARC) || defined(TARGET_PPC) \ + || defined(TARGET_LOONGARCH64) abi_ulong get_elf_hwcap(CPUState *cs); abi_ulong get_elf_hwcap2(CPUState *cs); #endif diff --git a/linux-user/loongarch64/elfload.c b/linux-user/loongarch64/elfload.c index 874dc4c230..ee4a85b8d6 100644 --- a/linux-user/loongarch64/elfload.c +++ b/linux-user/loongarch64/elfload.c @@ -9,3 +9,50 @@ const char *get_elf_cpu_model(uint32_t eflags) { return "la464"; } + +/* See arch/loongarch/include/uapi/asm/hwcap.h */ +enum { + HWCAP_LOONGARCH_CPUCFG = (1 << 0), + HWCAP_LOONGARCH_LAM = (1 << 1), + HWCAP_LOONGARCH_UAL = (1 << 2), + HWCAP_LOONGARCH_FPU = (1 << 3), + HWCAP_LOONGARCH_LSX = (1 << 4), + HWCAP_LOONGARCH_LASX = (1 << 5), + HWCAP_LOONGARCH_CRC32 = (1 << 6), + HWCAP_LOONGARCH_COMPLEX = (1 << 7), + HWCAP_LOONGARCH_CRYPTO = (1 << 8), + HWCAP_LOONGARCH_LVZ = (1 << 9), + HWCAP_LOONGARCH_LBT_X86 = (1 << 10), + HWCAP_LOONGARCH_LBT_ARM = (1 << 11), + HWCAP_LOONGARCH_LBT_MIPS = (1 << 12), +}; + +abi_ulong get_elf_hwcap(CPUState *cs) +{ + LoongArchCPU *cpu = LOONGARCH_CPU(cs); + abi_ulong hwcaps = 0; + + hwcaps |= HWCAP_LOONGARCH_CRC32; + + if (FIELD_EX32(cpu->env.cpucfg[1], CPUCFG1, UAL)) { + hwcaps |= HWCAP_LOONGARCH_UAL; + } + + if (FIELD_EX32(cpu->env.cpucfg[2], CPUCFG2, FP)) { + hwcaps |= HWCAP_LOONGARCH_FPU; + } + + if (FIELD_EX32(cpu->env.cpucfg[2], CPUCFG2, LAM)) { + hwcaps |= HWCAP_LOONGARCH_LAM; + } + + if (FIELD_EX32(cpu->env.cpucfg[2], CPUCFG2, LSX)) { + hwcaps |= HWCAP_LOONGARCH_LSX; + } + + if (FIELD_EX32(cpu->env.cpucfg[2], CPUCFG2, LASX)) { + hwcaps |= HWCAP_LOONGARCH_LASX; + } + + return hwcaps; +} diff --git a/linux-user/loongarch64/target_elf.h b/linux-user/loongarch64/target_elf.h index 39a08d35d9..037740d36f 100644 --- a/linux-user/loongarch64/target_elf.h +++ b/linux-user/loongarch64/target_elf.h @@ -6,4 +6,6 @@ #ifndef LOONGARCH_TARGET_ELF_H #define LOONGARCH_TARGET_ELF_H +#define HAVE_ELF_HWCAP 1 + #endif From 48004ab0586ba01387efc37e7ada26607a1c183e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 27 Jul 2025 21:23:05 -1000 Subject: [PATCH 0057/2396] linux-user: Move get_elf_hwcap to mips/elfload.c Change the return type to abi_ulong, and pass in the cpu. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 52 +--------------------------------- linux-user/loader.h | 2 +- linux-user/mips/elfload.c | 50 ++++++++++++++++++++++++++++++++ linux-user/mips/target_elf.h | 2 ++ linux-user/mips64/target_elf.h | 2 ++ 5 files changed, 56 insertions(+), 52 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 574b37a22c..dc3f502277 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -834,57 +834,7 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUMIPSState *e #define USE_ELF_CORE_DUMP #define ELF_EXEC_PAGESIZE 4096 -/* See arch/mips/include/uapi/asm/hwcap.h. */ -enum { - HWCAP_MIPS_R6 = (1 << 0), - HWCAP_MIPS_MSA = (1 << 1), - HWCAP_MIPS_CRC32 = (1 << 2), - HWCAP_MIPS_MIPS16 = (1 << 3), - HWCAP_MIPS_MDMX = (1 << 4), - HWCAP_MIPS_MIPS3D = (1 << 5), - HWCAP_MIPS_SMARTMIPS = (1 << 6), - HWCAP_MIPS_DSP = (1 << 7), - HWCAP_MIPS_DSP2 = (1 << 8), - HWCAP_MIPS_DSP3 = (1 << 9), - HWCAP_MIPS_MIPS16E2 = (1 << 10), - HWCAP_LOONGSON_MMI = (1 << 11), - HWCAP_LOONGSON_EXT = (1 << 12), - HWCAP_LOONGSON_EXT2 = (1 << 13), - HWCAP_LOONGSON_CPUCFG = (1 << 14), -}; - -#define ELF_HWCAP get_elf_hwcap() - -#define GET_FEATURE_INSN(_flag, _hwcap) \ - do { if (cpu->env.insn_flags & (_flag)) { hwcaps |= _hwcap; } } while (0) - -#define GET_FEATURE_REG_SET(_reg, _mask, _hwcap) \ - do { if (cpu->env._reg & (_mask)) { hwcaps |= _hwcap; } } while (0) - -#define GET_FEATURE_REG_EQU(_reg, _start, _length, _val, _hwcap) \ - do { \ - if (extract32(cpu->env._reg, (_start), (_length)) == (_val)) { \ - hwcaps |= _hwcap; \ - } \ - } while (0) - -static uint32_t get_elf_hwcap(void) -{ - MIPSCPU *cpu = MIPS_CPU(thread_cpu); - uint32_t hwcaps = 0; - - GET_FEATURE_REG_EQU(CP0_Config0, CP0C0_AR, CP0C0_AR_LENGTH, - 2, HWCAP_MIPS_R6); - GET_FEATURE_REG_SET(CP0_Config3, 1 << CP0C3_MSAP, HWCAP_MIPS_MSA); - GET_FEATURE_INSN(ASE_LMMI, HWCAP_LOONGSON_MMI); - GET_FEATURE_INSN(ASE_LEXT, HWCAP_LOONGSON_EXT); - - return hwcaps; -} - -#undef GET_FEATURE_REG_EQU -#undef GET_FEATURE_REG_SET -#undef GET_FEATURE_INSN +#define ELF_HWCAP get_elf_hwcap(thread_cpu) #endif /* TARGET_MIPS */ diff --git a/linux-user/loader.h b/linux-user/loader.h index 92b6d41145..04457737dd 100644 --- a/linux-user/loader.h +++ b/linux-user/loader.h @@ -103,7 +103,7 @@ const char *get_elf_cpu_model(uint32_t eflags); #if defined(TARGET_I386) || defined(TARGET_X86_64) || defined(TARGET_ARM) \ || defined(TARGET_SPARC) || defined(TARGET_PPC) \ - || defined(TARGET_LOONGARCH64) + || defined(TARGET_LOONGARCH64) || defined(TARGET_MIPS) abi_ulong get_elf_hwcap(CPUState *cs); abi_ulong get_elf_hwcap2(CPUState *cs); #endif diff --git a/linux-user/mips/elfload.c b/linux-user/mips/elfload.c index 04e3b76740..739f71c21b 100644 --- a/linux-user/mips/elfload.c +++ b/linux-user/mips/elfload.c @@ -42,3 +42,53 @@ const char *get_elf_cpu_model(uint32_t eflags) return "24Kf"; #endif } + +/* See arch/mips/include/uapi/asm/hwcap.h. */ +enum { + HWCAP_MIPS_R6 = (1 << 0), + HWCAP_MIPS_MSA = (1 << 1), + HWCAP_MIPS_CRC32 = (1 << 2), + HWCAP_MIPS_MIPS16 = (1 << 3), + HWCAP_MIPS_MDMX = (1 << 4), + HWCAP_MIPS_MIPS3D = (1 << 5), + HWCAP_MIPS_SMARTMIPS = (1 << 6), + HWCAP_MIPS_DSP = (1 << 7), + HWCAP_MIPS_DSP2 = (1 << 8), + HWCAP_MIPS_DSP3 = (1 << 9), + HWCAP_MIPS_MIPS16E2 = (1 << 10), + HWCAP_LOONGSON_MMI = (1 << 11), + HWCAP_LOONGSON_EXT = (1 << 12), + HWCAP_LOONGSON_EXT2 = (1 << 13), + HWCAP_LOONGSON_CPUCFG = (1 << 14), +}; + +#define GET_FEATURE_INSN(_flag, _hwcap) \ + do { if (cpu->env.insn_flags & (_flag)) { hwcaps |= _hwcap; } } while (0) + +#define GET_FEATURE_REG_SET(_reg, _mask, _hwcap) \ + do { if (cpu->env._reg & (_mask)) { hwcaps |= _hwcap; } } while (0) + +#define GET_FEATURE_REG_EQU(_reg, _start, _length, _val, _hwcap) \ + do { \ + if (extract32(cpu->env._reg, (_start), (_length)) == (_val)) { \ + hwcaps |= _hwcap; \ + } \ + } while (0) + +abi_ulong get_elf_hwcap(CPUState *cs) +{ + MIPSCPU *cpu = MIPS_CPU(cs); + abi_ulong hwcaps = 0; + + GET_FEATURE_REG_EQU(CP0_Config0, CP0C0_AR, CP0C0_AR_LENGTH, + 2, HWCAP_MIPS_R6); + GET_FEATURE_REG_SET(CP0_Config3, 1 << CP0C3_MSAP, HWCAP_MIPS_MSA); + GET_FEATURE_INSN(ASE_LMMI, HWCAP_LOONGSON_MMI); + GET_FEATURE_INSN(ASE_LEXT, HWCAP_LOONGSON_EXT); + + return hwcaps; +} + +#undef GET_FEATURE_REG_EQU +#undef GET_FEATURE_REG_SET +#undef GET_FEATURE_INSN diff --git a/linux-user/mips/target_elf.h b/linux-user/mips/target_elf.h index febf710c7a..877f8347d7 100644 --- a/linux-user/mips/target_elf.h +++ b/linux-user/mips/target_elf.h @@ -8,4 +8,6 @@ #ifndef MIPS_TARGET_ELF_H #define MIPS_TARGET_ELF_H +#define HAVE_ELF_HWCAP 1 + #endif diff --git a/linux-user/mips64/target_elf.h b/linux-user/mips64/target_elf.h index 02e6d14840..c0347e5cb6 100644 --- a/linux-user/mips64/target_elf.h +++ b/linux-user/mips64/target_elf.h @@ -8,4 +8,6 @@ #ifndef MIPS64_TARGET_ELF_H #define MIPS64_TARGET_ELF_H +#define HAVE_ELF_HWCAP 1 + #endif From 92c9983c06fa0a55f120c71dda03e38d5220fccc Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 27 Jul 2025 21:28:05 -1000 Subject: [PATCH 0058/2396] linux-user: Move get_elf_hwcap to sh4/elfload.c Change the return type to abi_ulong, and pass in the cpu. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 29 +---------------------------- linux-user/loader.h | 3 ++- linux-user/sh4/elfload.c | 27 +++++++++++++++++++++++++++ linux-user/sh4/target_elf.h | 2 ++ 4 files changed, 32 insertions(+), 29 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index dc3f502277..7e1c11c39f 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -963,34 +963,7 @@ static inline void elf_core_copy_regs(target_elf_gregset_t *regs, #define USE_ELF_CORE_DUMP #define ELF_EXEC_PAGESIZE 4096 -enum { - SH_CPU_HAS_FPU = 0x0001, /* Hardware FPU support */ - SH_CPU_HAS_P2_FLUSH_BUG = 0x0002, /* Need to flush the cache in P2 area */ - SH_CPU_HAS_MMU_PAGE_ASSOC = 0x0004, /* SH3: TLB way selection bit support */ - SH_CPU_HAS_DSP = 0x0008, /* SH-DSP: DSP support */ - SH_CPU_HAS_PERF_COUNTER = 0x0010, /* Hardware performance counters */ - SH_CPU_HAS_PTEA = 0x0020, /* PTEA register */ - SH_CPU_HAS_LLSC = 0x0040, /* movli.l/movco.l */ - SH_CPU_HAS_L2_CACHE = 0x0080, /* Secondary cache / URAM */ - SH_CPU_HAS_OP32 = 0x0100, /* 32-bit instruction support */ - SH_CPU_HAS_PTEAEX = 0x0200, /* PTE ASID Extension support */ -}; - -#define ELF_HWCAP get_elf_hwcap() - -static uint32_t get_elf_hwcap(void) -{ - SuperHCPU *cpu = SUPERH_CPU(thread_cpu); - uint32_t hwcap = 0; - - hwcap |= SH_CPU_HAS_FPU; - - if (cpu->env.features & SH_FEATURE_SH4A) { - hwcap |= SH_CPU_HAS_LLSC; - } - - return hwcap; -} +#define ELF_HWCAP get_elf_hwcap(thread_cpu) #endif diff --git a/linux-user/loader.h b/linux-user/loader.h index 04457737dd..d8a9399807 100644 --- a/linux-user/loader.h +++ b/linux-user/loader.h @@ -103,7 +103,8 @@ const char *get_elf_cpu_model(uint32_t eflags); #if defined(TARGET_I386) || defined(TARGET_X86_64) || defined(TARGET_ARM) \ || defined(TARGET_SPARC) || defined(TARGET_PPC) \ - || defined(TARGET_LOONGARCH64) || defined(TARGET_MIPS) + || defined(TARGET_LOONGARCH64) || defined(TARGET_MIPS) \ + || defined(TARGET_SH4) abi_ulong get_elf_hwcap(CPUState *cs); abi_ulong get_elf_hwcap2(CPUState *cs); #endif diff --git a/linux-user/sh4/elfload.c b/linux-user/sh4/elfload.c index 546034ec07..99ad4f6334 100644 --- a/linux-user/sh4/elfload.c +++ b/linux-user/sh4/elfload.c @@ -9,3 +9,30 @@ const char *get_elf_cpu_model(uint32_t eflags) { return "sh7785"; } + +enum { + SH_CPU_HAS_FPU = 0x0001, /* Hardware FPU support */ + SH_CPU_HAS_P2_FLUSH_BUG = 0x0002, /* Need to flush the cache in P2 area */ + SH_CPU_HAS_MMU_PAGE_ASSOC = 0x0004, /* SH3: TLB way selection bit support */ + SH_CPU_HAS_DSP = 0x0008, /* SH-DSP: DSP support */ + SH_CPU_HAS_PERF_COUNTER = 0x0010, /* Hardware performance counters */ + SH_CPU_HAS_PTEA = 0x0020, /* PTEA register */ + SH_CPU_HAS_LLSC = 0x0040, /* movli.l/movco.l */ + SH_CPU_HAS_L2_CACHE = 0x0080, /* Secondary cache / URAM */ + SH_CPU_HAS_OP32 = 0x0100, /* 32-bit instruction support */ + SH_CPU_HAS_PTEAEX = 0x0200, /* PTE ASID Extension support */ +}; + +abi_ulong get_elf_hwcap(CPUState *cs) +{ + SuperHCPU *cpu = SUPERH_CPU(cs); + abi_ulong hwcap = 0; + + hwcap |= SH_CPU_HAS_FPU; + + if (cpu->env.features & SH_FEATURE_SH4A) { + hwcap |= SH_CPU_HAS_LLSC; + } + + return hwcap; +} diff --git a/linux-user/sh4/target_elf.h b/linux-user/sh4/target_elf.h index d17011bd75..badd0f5371 100644 --- a/linux-user/sh4/target_elf.h +++ b/linux-user/sh4/target_elf.h @@ -8,4 +8,6 @@ #ifndef SH4_TARGET_ELF_H #define SH4_TARGET_ELF_H +#define HAVE_ELF_HWCAP 1 + #endif From 1d4774b60e3dfdc9295e36c5fa298b457d10db10 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 27 Jul 2025 21:39:23 -1000 Subject: [PATCH 0059/2396] linux-user: Move hwcap functions to s390x/elfload.c For get_elf_hwcap, change the return type to abi_ulong and pass in the cpu. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 60 +--------------------------------- linux-user/loader.h | 5 +-- linux-user/s390x/elfload.c | 57 ++++++++++++++++++++++++++++++++ linux-user/s390x/target_elf.h | 2 ++ linux-user/s390x/target_proc.h | 2 +- 5 files changed, 62 insertions(+), 64 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 7e1c11c39f..ba8593368d 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -1039,65 +1039,7 @@ static inline void init_thread(struct target_pt_regs *regs, #define ELF_DATA ELFDATA2MSB #define ELF_ARCH EM_S390 -#include "elf.h" - -#define ELF_HWCAP get_elf_hwcap() - -#define GET_FEATURE(_feat, _hwcap) \ - do { if (s390_has_feat(_feat)) { hwcap |= _hwcap; } } while (0) - -uint32_t get_elf_hwcap(void) -{ - /* - * Let's assume we always have esan3 and zarch. - * 31-bit processes can use 64-bit registers (high gprs). - */ - uint32_t hwcap = HWCAP_S390_ESAN3 | HWCAP_S390_ZARCH | HWCAP_S390_HIGH_GPRS; - - GET_FEATURE(S390_FEAT_STFLE, HWCAP_S390_STFLE); - GET_FEATURE(S390_FEAT_MSA, HWCAP_S390_MSA); - GET_FEATURE(S390_FEAT_LONG_DISPLACEMENT, HWCAP_S390_LDISP); - GET_FEATURE(S390_FEAT_EXTENDED_IMMEDIATE, HWCAP_S390_EIMM); - if (s390_has_feat(S390_FEAT_EXTENDED_TRANSLATION_3) && - s390_has_feat(S390_FEAT_ETF3_ENH)) { - hwcap |= HWCAP_S390_ETF3EH; - } - GET_FEATURE(S390_FEAT_VECTOR, HWCAP_S390_VXRS); - GET_FEATURE(S390_FEAT_VECTOR_ENH, HWCAP_S390_VXRS_EXT); - GET_FEATURE(S390_FEAT_VECTOR_ENH2, HWCAP_S390_VXRS_EXT2); - - return hwcap; -} - -const char *elf_hwcap_str(uint32_t bit) -{ - static const char *hwcap_str[] = { - [HWCAP_S390_NR_ESAN3] = "esan3", - [HWCAP_S390_NR_ZARCH] = "zarch", - [HWCAP_S390_NR_STFLE] = "stfle", - [HWCAP_S390_NR_MSA] = "msa", - [HWCAP_S390_NR_LDISP] = "ldisp", - [HWCAP_S390_NR_EIMM] = "eimm", - [HWCAP_S390_NR_DFP] = "dfp", - [HWCAP_S390_NR_HPAGE] = "edat", - [HWCAP_S390_NR_ETF3EH] = "etf3eh", - [HWCAP_S390_NR_HIGH_GPRS] = "highgprs", - [HWCAP_S390_NR_TE] = "te", - [HWCAP_S390_NR_VXRS] = "vx", - [HWCAP_S390_NR_VXRS_BCD] = "vxd", - [HWCAP_S390_NR_VXRS_EXT] = "vxe", - [HWCAP_S390_NR_GS] = "gs", - [HWCAP_S390_NR_VXRS_EXT2] = "vxe2", - [HWCAP_S390_NR_VXRS_PDE] = "vxp", - [HWCAP_S390_NR_SORT] = "sort", - [HWCAP_S390_NR_DFLT] = "dflt", - [HWCAP_S390_NR_NNPA] = "nnpa", - [HWCAP_S390_NR_PCI_MIO] = "pcimio", - [HWCAP_S390_NR_SIE] = "sie", - }; - - return bit < ARRAY_SIZE(hwcap_str) ? hwcap_str[bit] : NULL; -} +#define ELF_HWCAP get_elf_hwcap(thread_cpu) static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop) { diff --git a/linux-user/loader.h b/linux-user/loader.h index d8a9399807..c14e69f551 100644 --- a/linux-user/loader.h +++ b/linux-user/loader.h @@ -104,13 +104,10 @@ const char *get_elf_cpu_model(uint32_t eflags); #if defined(TARGET_I386) || defined(TARGET_X86_64) || defined(TARGET_ARM) \ || defined(TARGET_SPARC) || defined(TARGET_PPC) \ || defined(TARGET_LOONGARCH64) || defined(TARGET_MIPS) \ - || defined(TARGET_SH4) + || defined(TARGET_SH4) || defined(TARGET_S390X) abi_ulong get_elf_hwcap(CPUState *cs); abi_ulong get_elf_hwcap2(CPUState *cs); #endif -#if defined(TARGET_S390X) -uint32_t get_elf_hwcap(void); -#endif const char *elf_hwcap_str(uint32_t bit); const char *elf_hwcap2_str(uint32_t bit); diff --git a/linux-user/s390x/elfload.c b/linux-user/s390x/elfload.c index 989953a247..79ceaba51d 100644 --- a/linux-user/s390x/elfload.c +++ b/linux-user/s390x/elfload.c @@ -3,9 +3,66 @@ #include "qemu/osdep.h" #include "qemu.h" #include "loader.h" +#include "elf.h" const char *get_elf_cpu_model(uint32_t eflags) { return "qemu"; } + +#define GET_FEATURE(_feat, _hwcap) \ + do { if (s390_has_feat(_feat)) { hwcap |= _hwcap; } } while (0) + +abi_ulong get_elf_hwcap(CPUState *cs) +{ + /* + * Let's assume we always have esan3 and zarch. + * 31-bit processes can use 64-bit registers (high gprs). + */ + uint32_t hwcap = HWCAP_S390_ESAN3 | HWCAP_S390_ZARCH | HWCAP_S390_HIGH_GPRS; + + GET_FEATURE(S390_FEAT_STFLE, HWCAP_S390_STFLE); + GET_FEATURE(S390_FEAT_MSA, HWCAP_S390_MSA); + GET_FEATURE(S390_FEAT_LONG_DISPLACEMENT, HWCAP_S390_LDISP); + GET_FEATURE(S390_FEAT_EXTENDED_IMMEDIATE, HWCAP_S390_EIMM); + if (s390_has_feat(S390_FEAT_EXTENDED_TRANSLATION_3) && + s390_has_feat(S390_FEAT_ETF3_ENH)) { + hwcap |= HWCAP_S390_ETF3EH; + } + GET_FEATURE(S390_FEAT_VECTOR, HWCAP_S390_VXRS); + GET_FEATURE(S390_FEAT_VECTOR_ENH, HWCAP_S390_VXRS_EXT); + GET_FEATURE(S390_FEAT_VECTOR_ENH2, HWCAP_S390_VXRS_EXT2); + + return hwcap; +} + +const char *elf_hwcap_str(uint32_t bit) +{ + static const char *hwcap_str[] = { + [HWCAP_S390_NR_ESAN3] = "esan3", + [HWCAP_S390_NR_ZARCH] = "zarch", + [HWCAP_S390_NR_STFLE] = "stfle", + [HWCAP_S390_NR_MSA] = "msa", + [HWCAP_S390_NR_LDISP] = "ldisp", + [HWCAP_S390_NR_EIMM] = "eimm", + [HWCAP_S390_NR_DFP] = "dfp", + [HWCAP_S390_NR_HPAGE] = "edat", + [HWCAP_S390_NR_ETF3EH] = "etf3eh", + [HWCAP_S390_NR_HIGH_GPRS] = "highgprs", + [HWCAP_S390_NR_TE] = "te", + [HWCAP_S390_NR_VXRS] = "vx", + [HWCAP_S390_NR_VXRS_BCD] = "vxd", + [HWCAP_S390_NR_VXRS_EXT] = "vxe", + [HWCAP_S390_NR_GS] = "gs", + [HWCAP_S390_NR_VXRS_EXT2] = "vxe2", + [HWCAP_S390_NR_VXRS_PDE] = "vxp", + [HWCAP_S390_NR_SORT] = "sort", + [HWCAP_S390_NR_DFLT] = "dflt", + [HWCAP_S390_NR_NNPA] = "nnpa", + [HWCAP_S390_NR_PCI_MIO] = "pcimio", + [HWCAP_S390_NR_SIE] = "sie", + }; + + return bit < ARRAY_SIZE(hwcap_str) ? hwcap_str[bit] : NULL; +} diff --git a/linux-user/s390x/target_elf.h b/linux-user/s390x/target_elf.h index e51b053339..cebace949a 100644 --- a/linux-user/s390x/target_elf.h +++ b/linux-user/s390x/target_elf.h @@ -8,4 +8,6 @@ #ifndef S390X_TARGET_ELF_H #define S390X_TARGET_ELF_H +#define HAVE_ELF_HWCAP 1 + #endif diff --git a/linux-user/s390x/target_proc.h b/linux-user/s390x/target_proc.h index a4a4821ea5..60cc22d3b4 100644 --- a/linux-user/s390x/target_proc.h +++ b/linux-user/s390x/target_proc.h @@ -48,7 +48,7 @@ static void show_cpu_summary(CPUArchState *cpu_env, int fd) { S390CPUModel *model = env_archcpu(cpu_env)->model; int num_cpus = sysconf(_SC_NPROCESSORS_ONLN); - uint32_t elf_hwcap = get_elf_hwcap(); + uint32_t elf_hwcap = get_elf_hwcap(env_cpu(cpu_env)); const char *hwcap_str; int i; From 50e59ad0b7b1c71e858939504a749bf1a1313e2e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 27 Jul 2025 21:44:02 -1000 Subject: [PATCH 0060/2396] linux-user: Move get_elf_hwcap to riscv/elfload.c Change the return type to abi_ulong, and pass in the cpu. As this is the last instance of get_elf_hwcap to be converted, remove the ifdef around the declaration in loader.h. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 14 +------------- linux-user/loader.h | 5 ----- linux-user/riscv/elfload.c | 12 ++++++++++++ linux-user/riscv/target_elf.h | 2 ++ 4 files changed, 15 insertions(+), 18 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index ba8593368d..ce4055b0e9 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -1099,19 +1099,7 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, #define VDSO_HEADER "vdso-64.c.inc" #endif -#define ELF_HWCAP get_elf_hwcap() - -static uint32_t get_elf_hwcap(void) -{ -#define MISA_BIT(EXT) (1 << (EXT - 'A')) - RISCVCPU *cpu = RISCV_CPU(thread_cpu); - uint32_t mask = MISA_BIT('I') | MISA_BIT('M') | MISA_BIT('A') - | MISA_BIT('F') | MISA_BIT('D') | MISA_BIT('C') - | MISA_BIT('V'); - - return cpu->env.misa_ext & mask; -#undef MISA_BIT -} +#define ELF_HWCAP get_elf_hwcap(thread_cpu) static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop) diff --git a/linux-user/loader.h b/linux-user/loader.h index c14e69f551..729723cc06 100644 --- a/linux-user/loader.h +++ b/linux-user/loader.h @@ -101,13 +101,8 @@ extern unsigned long guest_stack_size; /* Note that Elf32 and Elf64 use uint32_t for e_flags. */ const char *get_elf_cpu_model(uint32_t eflags); -#if defined(TARGET_I386) || defined(TARGET_X86_64) || defined(TARGET_ARM) \ - || defined(TARGET_SPARC) || defined(TARGET_PPC) \ - || defined(TARGET_LOONGARCH64) || defined(TARGET_MIPS) \ - || defined(TARGET_SH4) || defined(TARGET_S390X) abi_ulong get_elf_hwcap(CPUState *cs); abi_ulong get_elf_hwcap2(CPUState *cs); -#endif const char *elf_hwcap_str(uint32_t bit); const char *elf_hwcap2_str(uint32_t bit); diff --git a/linux-user/riscv/elfload.c b/linux-user/riscv/elfload.c index f92adb7308..2e7d622232 100644 --- a/linux-user/riscv/elfload.c +++ b/linux-user/riscv/elfload.c @@ -9,3 +9,15 @@ const char *get_elf_cpu_model(uint32_t eflags) { return "max"; } + +abi_ulong get_elf_hwcap(CPUState *cs) +{ +#define MISA_BIT(EXT) (1 << (EXT - 'A')) + RISCVCPU *cpu = RISCV_CPU(cs); + uint32_t mask = MISA_BIT('I') | MISA_BIT('M') | MISA_BIT('A') + | MISA_BIT('F') | MISA_BIT('D') | MISA_BIT('C') + | MISA_BIT('V'); + + return cpu->env.misa_ext & mask; +#undef MISA_BIT +} diff --git a/linux-user/riscv/target_elf.h b/linux-user/riscv/target_elf.h index bfe86105d0..48d9af557b 100644 --- a/linux-user/riscv/target_elf.h +++ b/linux-user/riscv/target_elf.h @@ -8,4 +8,6 @@ #ifndef RISCV_TARGET_ELF_H #define RISCV_TARGET_ELF_H +#define HAVE_ELF_HWCAP 1 + #endif From 0dbb0ba870a11366b061905b1a06baa973b642d6 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 2 Aug 2025 09:05:41 +1000 Subject: [PATCH 0061/2396] linux-user: Remove ELF_HWCAP All real definitions of ELF_HWCAP are now identical, and the stub definitions are 0. Provide zero stub as a fallback definition. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 35 ++++++++++++----------------------- 1 file changed, 12 insertions(+), 23 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index ce4055b0e9..88d439f348 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -28,6 +28,7 @@ #include "qemu/lockable.h" #include "qapi/error.h" #include "qemu/error-report.h" +#include "target_elf.h" #include "target_signal.h" #include "tcg/debuginfo.h" @@ -148,8 +149,6 @@ typedef abi_int target_pid_t; #ifdef TARGET_I386 -#define ELF_HWCAP get_elf_hwcap(thread_cpu) - #ifdef TARGET_X86_64 #define ELF_CLASS ELFCLASS64 #define ELF_ARCH EM_X86_64 @@ -449,7 +448,6 @@ static bool init_guest_commpage(void) return true; } -#define ELF_HWCAP get_elf_hwcap(thread_cpu) #define ELF_HWCAP2 get_elf_hwcap2(thread_cpu) #define ELF_PLATFORM get_elf_platform() @@ -539,7 +537,6 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, #define USE_ELF_CORE_DUMP #define ELF_EXEC_PAGESIZE 4096 -#define ELF_HWCAP get_elf_hwcap(thread_cpu) #define ELF_HWCAP2 get_elf_hwcap2(thread_cpu) #if TARGET_BIG_ENDIAN @@ -565,8 +562,6 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, # define ELF_ARCH EM_SPARCV9 #endif -#define ELF_HWCAP get_elf_hwcap(thread_cpu) - static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop) { @@ -598,7 +593,6 @@ static inline void init_thread(struct target_pt_regs *regs, #define ELF_ARCH EM_PPC -#define ELF_HWCAP get_elf_hwcap(thread_cpu) #define ELF_HWCAP2 get_elf_hwcap2(thread_cpu) /* @@ -725,8 +719,6 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, #define USE_ELF_CORE_DUMP #define ELF_EXEC_PAGESIZE 4096 -#define ELF_HWCAP get_elf_hwcap(thread_cpu) - #define ELF_PLATFORM "loongarch" #endif /* TARGET_LOONGARCH64 */ @@ -834,8 +826,6 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUMIPSState *e #define USE_ELF_CORE_DUMP #define ELF_EXEC_PAGESIZE 4096 -#define ELF_HWCAP get_elf_hwcap(thread_cpu) - #endif /* TARGET_MIPS */ #ifdef TARGET_MICROBLAZE @@ -909,7 +899,7 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, (*regs)[32] = tswapreg(env->pc); (*regs)[33] = tswapreg(cpu_get_sr(env)); } -#define ELF_HWCAP 0 + #define ELF_PLATFORM NULL #endif /* TARGET_OPENRISC */ @@ -963,8 +953,6 @@ static inline void elf_core_copy_regs(target_elf_gregset_t *regs, #define USE_ELF_CORE_DUMP #define ELF_EXEC_PAGESIZE 4096 -#define ELF_HWCAP get_elf_hwcap(thread_cpu) - #endif #ifdef TARGET_M68K @@ -1039,8 +1027,6 @@ static inline void init_thread(struct target_pt_regs *regs, #define ELF_DATA ELFDATA2MSB #define ELF_ARCH EM_S390 -#define ELF_HWCAP get_elf_hwcap(thread_cpu) - static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop) { regs->psw.addr = infop->entry; @@ -1099,8 +1085,6 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, #define VDSO_HEADER "vdso-64.c.inc" #endif -#define ELF_HWCAP get_elf_hwcap(thread_cpu) - static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop) { @@ -1268,10 +1252,6 @@ static inline void init_thread(struct target_pt_regs *regs, #define elf_check_abi(x) (1) #endif -#ifndef ELF_HWCAP -#define ELF_HWCAP 0 -#endif - #ifndef STACK_GROWS_DOWN #define STACK_GROWS_DOWN 1 #endif @@ -1291,6 +1271,15 @@ static inline void init_thread(struct target_pt_regs *regs, #define EXSTACK_DEFAULT false #endif +/* + * Provide fallback definitions that the target may omit. + * One way or another, we'll get a link error if the setting of + * HAVE_* doesn't match the implementation. + */ +#ifndef HAVE_ELF_HWCAP +abi_ulong get_elf_hwcap(CPUState *cs) { return 0; } +#endif + #include "elf.h" /* We must delay the following stanzas until after "elf.h". */ @@ -1868,7 +1857,7 @@ static abi_ulong create_elf_tables(abi_ulong p, int argc, int envc, NEW_AUX_ENT(AT_EUID, (abi_ulong) geteuid()); NEW_AUX_ENT(AT_GID, (abi_ulong) getgid()); NEW_AUX_ENT(AT_EGID, (abi_ulong) getegid()); - NEW_AUX_ENT(AT_HWCAP, (abi_ulong) ELF_HWCAP); + NEW_AUX_ENT(AT_HWCAP, get_elf_hwcap(thread_cpu)); NEW_AUX_ENT(AT_CLKTCK, (abi_ulong) sysconf(_SC_CLK_TCK)); NEW_AUX_ENT(AT_RANDOM, (abi_ulong) u_rand_bytes); NEW_AUX_ENT(AT_SECURE, (abi_ulong) qemu_getauxval(AT_SECURE)); From fcac98d0ba8b5f4c311c1059d897d74d1276af9d Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 2 Aug 2025 09:13:23 +1000 Subject: [PATCH 0062/2396] linux-user: Remove ELF_HWCAP2 All definitions of ELF_HWCAP2 are now identical. Provide a not-reached stub as a fallback definition of get_elf_hwcap2. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 88d439f348..7a41917b49 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -448,8 +448,6 @@ static bool init_guest_commpage(void) return true; } -#define ELF_HWCAP2 get_elf_hwcap2(thread_cpu) - #define ELF_PLATFORM get_elf_platform() static const char *get_elf_platform(void) @@ -537,8 +535,6 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, #define USE_ELF_CORE_DUMP #define ELF_EXEC_PAGESIZE 4096 -#define ELF_HWCAP2 get_elf_hwcap2(thread_cpu) - #if TARGET_BIG_ENDIAN # define VDSO_HEADER "vdso-be.c.inc" #else @@ -593,8 +589,6 @@ static inline void init_thread(struct target_pt_regs *regs, #define ELF_ARCH EM_PPC -#define ELF_HWCAP2 get_elf_hwcap2(thread_cpu) - /* * The requirements here are: * - keep the final alignment of sp (sp & 0xf) @@ -1279,6 +1273,10 @@ static inline void init_thread(struct target_pt_regs *regs, #ifndef HAVE_ELF_HWCAP abi_ulong get_elf_hwcap(CPUState *cs) { return 0; } #endif +#ifndef HAVE_ELF_HWCAP2 +abi_ulong get_elf_hwcap2(CPUState *cs) { g_assert_not_reached(); } +#define HAVE_ELF_HWCAP2 0 +#endif #include "elf.h" @@ -1801,9 +1799,9 @@ static abi_ulong create_elf_tables(abi_ulong p, int argc, int envc, #ifdef DLINFO_ARCH_ITEMS size += DLINFO_ARCH_ITEMS * 2; #endif -#ifdef ELF_HWCAP2 - size += 2; -#endif + if (HAVE_ELF_HWCAP2) { + size += 2; + } info->auxv_len = size * n; size += envc + argc + 2; @@ -1863,10 +1861,9 @@ static abi_ulong create_elf_tables(abi_ulong p, int argc, int envc, NEW_AUX_ENT(AT_SECURE, (abi_ulong) qemu_getauxval(AT_SECURE)); NEW_AUX_ENT(AT_EXECFN, info->file_string); -#ifdef ELF_HWCAP2 - NEW_AUX_ENT(AT_HWCAP2, (abi_ulong) ELF_HWCAP2); -#endif - + if (HAVE_ELF_HWCAP2) { + NEW_AUX_ENT(AT_HWCAP2, get_elf_hwcap(thread_cpu)); + } if (u_base_platform) { NEW_AUX_ENT(AT_BASE_PLATFORM, u_base_platform); } From 3c907dec45b66fff084fbddf18ee5bb84ddc5314 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 28 Jul 2025 08:41:43 -1000 Subject: [PATCH 0063/2396] linux-user: Move get_elf_platform to {i386,x86_64}/elfload.c Move get_elf_platform to i386/elfload.c; pass in CPUState. Create a simple get_elf_platform for x86_64. Introduce HAVE_ELF_PLATFORM. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 18 ++---------------- linux-user/i386/elfload.c | 13 +++++++++++++ linux-user/i386/target_elf.h | 1 + linux-user/loader.h | 3 +++ linux-user/x86_64/elfload.c | 5 +++++ linux-user/x86_64/target_elf.h | 1 + 6 files changed, 25 insertions(+), 16 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 7a41917b49..e6e509c0a6 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -149,12 +149,12 @@ typedef abi_int target_pid_t; #ifdef TARGET_I386 +#define ELF_PLATFORM get_elf_platform(thread_cpu) + #ifdef TARGET_X86_64 #define ELF_CLASS ELFCLASS64 #define ELF_ARCH EM_X86_64 -#define ELF_PLATFORM "x86_64" - static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop) { regs->rax = 0; @@ -237,22 +237,8 @@ static bool init_guest_commpage(void) #define ELF_CLASS ELFCLASS32 #define ELF_ARCH EM_386 -#define ELF_PLATFORM get_elf_platform() #define EXSTACK_DEFAULT true -static const char *get_elf_platform(void) -{ - static char elf_platform[] = "i386"; - int family = object_property_get_int(OBJECT(thread_cpu), "family", NULL); - if (family > 6) { - family = 6; - } - if (family >= 3) { - elf_platform[1] = '0' + family; - } - return elf_platform; -} - static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop) { diff --git a/linux-user/i386/elfload.c b/linux-user/i386/elfload.c index f99336e73c..1b759098ca 100644 --- a/linux-user/i386/elfload.c +++ b/linux-user/i386/elfload.c @@ -14,3 +14,16 @@ abi_ulong get_elf_hwcap(CPUState *cs) { return cpu_env(cs)->features[FEAT_1_EDX]; } + +const char *get_elf_platform(CPUState *cs) +{ + static char elf_platform[] = "i386"; + int family = object_property_get_int(OBJECT(cs), "family", NULL); + if (family > 6) { + family = 6; + } + if (family >= 3) { + elf_platform[1] = '0' + family; + } + return elf_platform; +} diff --git a/linux-user/i386/target_elf.h b/linux-user/i386/target_elf.h index 802395af3a..44dde1ac4a 100644 --- a/linux-user/i386/target_elf.h +++ b/linux-user/i386/target_elf.h @@ -9,5 +9,6 @@ #define I386_TARGET_ELF_H #define HAVE_ELF_HWCAP 1 +#define HAVE_ELF_PLATFORM 1 #endif diff --git a/linux-user/loader.h b/linux-user/loader.h index 729723cc06..44bb4cbfd3 100644 --- a/linux-user/loader.h +++ b/linux-user/loader.h @@ -105,5 +105,8 @@ abi_ulong get_elf_hwcap(CPUState *cs); abi_ulong get_elf_hwcap2(CPUState *cs); const char *elf_hwcap_str(uint32_t bit); const char *elf_hwcap2_str(uint32_t bit); +#if defined(TARGET_I386) +const char *get_elf_platform(CPUState *cs); +#endif #endif /* LINUX_USER_LOADER_H */ diff --git a/linux-user/x86_64/elfload.c b/linux-user/x86_64/elfload.c index f99336e73c..88541ea45e 100644 --- a/linux-user/x86_64/elfload.c +++ b/linux-user/x86_64/elfload.c @@ -14,3 +14,8 @@ abi_ulong get_elf_hwcap(CPUState *cs) { return cpu_env(cs)->features[FEAT_1_EDX]; } + +const char *get_elf_platform(CPUState *cs) +{ + return "x86_64"; +} diff --git a/linux-user/x86_64/target_elf.h b/linux-user/x86_64/target_elf.h index 03483bad57..498c3f7e4e 100644 --- a/linux-user/x86_64/target_elf.h +++ b/linux-user/x86_64/target_elf.h @@ -9,5 +9,6 @@ #define X86_64_TARGET_ELF_H #define HAVE_ELF_HWCAP 1 +#define HAVE_ELF_PLATFORM 1 #endif From e03b6ad483c087c8cf0357b7859d98bd2b109071 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 28 Jul 2025 08:43:35 -1000 Subject: [PATCH 0064/2396] linux-user/i386: Return const data from get_elf_platform Rather than modify a static buffer, index into an array of const data. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/i386/elfload.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/linux-user/i386/elfload.c b/linux-user/i386/elfload.c index 1b759098ca..ef3a6c35d2 100644 --- a/linux-user/i386/elfload.c +++ b/linux-user/i386/elfload.c @@ -17,13 +17,9 @@ abi_ulong get_elf_hwcap(CPUState *cs) const char *get_elf_platform(CPUState *cs) { - static char elf_platform[] = "i386"; + static const char elf_platform[4][5] = { "i386", "i486", "i586", "i686" }; int family = object_property_get_int(OBJECT(cs), "family", NULL); - if (family > 6) { - family = 6; - } - if (family >= 3) { - elf_platform[1] = '0' + family; - } - return elf_platform; + + family = MAX(MIN(family, 6), 3); + return elf_platform[family - 3]; } From d6b8c5dbd9b0fd05c3f8ac9427729082eecb9fbb Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 28 Jul 2025 08:47:57 -1000 Subject: [PATCH 0065/2396] linux-user: Move get_elf_platform to arm/elfload.c Move the aarch32 get_elf_platform to arm/elfload.c; pass in CPUState. Create a simple version in aarch64/elfload.c, which we must do at the same time because of the ifdef dependency between TARGET_AARCH64 and TARGET_ARM. Since all versions of get_elf_platform now have the same signature, remove the ifdef from the declaration in loader.h. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/aarch64/elfload.c | 5 +++++ linux-user/aarch64/target_elf.h | 1 + linux-user/arm/elfload.c | 29 +++++++++++++++++++++++++ linux-user/arm/target_elf.h | 1 + linux-user/elfload.c | 38 ++------------------------------- linux-user/loader.h | 2 -- 6 files changed, 38 insertions(+), 38 deletions(-) diff --git a/linux-user/aarch64/elfload.c b/linux-user/aarch64/elfload.c index 92c8ea62c6..1030cb8094 100644 --- a/linux-user/aarch64/elfload.c +++ b/linux-user/aarch64/elfload.c @@ -342,3 +342,8 @@ const char *elf_hwcap2_str(uint32_t bit) return bit < ARRAY_SIZE(hwcap_str) ? hwcap_str[bit] : NULL; } + +const char *get_elf_platform(CPUState *cs) +{ + return TARGET_BIG_ENDIAN ? "aarch64_be" : "aarch64"; +} diff --git a/linux-user/aarch64/target_elf.h b/linux-user/aarch64/target_elf.h index 77108f3cb0..dee79ce0c6 100644 --- a/linux-user/aarch64/target_elf.h +++ b/linux-user/aarch64/target_elf.h @@ -10,5 +10,6 @@ #define HAVE_ELF_HWCAP 1 #define HAVE_ELF_HWCAP2 1 +#define HAVE_ELF_PLATFORM 1 #endif diff --git a/linux-user/arm/elfload.c b/linux-user/arm/elfload.c index c7561b005b..7de1f13f4b 100644 --- a/linux-user/arm/elfload.c +++ b/linux-user/arm/elfload.c @@ -170,3 +170,32 @@ const char *elf_hwcap2_str(uint32_t bit) return bit < ARRAY_SIZE(hwcap_str) ? hwcap_str[bit] : NULL; } + +const char *get_elf_platform(CPUState *cs) +{ + CPUARMState *env = cpu_env(cs); + +#if TARGET_BIG_ENDIAN +# define END "b" +#else +# define END "l" +#endif + + if (arm_feature(env, ARM_FEATURE_V8)) { + return "v8" END; + } else if (arm_feature(env, ARM_FEATURE_V7)) { + if (arm_feature(env, ARM_FEATURE_M)) { + return "v7m" END; + } else { + return "v7" END; + } + } else if (arm_feature(env, ARM_FEATURE_V6)) { + return "v6" END; + } else if (arm_feature(env, ARM_FEATURE_V5)) { + return "v5" END; + } else { + return "v4" END; + } + +#undef END +} diff --git a/linux-user/arm/target_elf.h b/linux-user/arm/target_elf.h index 90470bd87b..856ca41b16 100644 --- a/linux-user/arm/target_elf.h +++ b/linux-user/arm/target_elf.h @@ -10,5 +10,6 @@ #define HAVE_ELF_HWCAP 1 #define HAVE_ELF_HWCAP2 1 +#define HAVE_ELF_PLATFORM 1 #endif diff --git a/linux-user/elfload.c b/linux-user/elfload.c index e6e509c0a6..0e41737cf1 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -309,6 +309,8 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUX86State *en #ifdef TARGET_ARM +#define ELF_PLATFORM get_elf_platform(thread_cpu) + #ifndef TARGET_AARCH64 /* 32 bit ARM definitions */ @@ -434,37 +436,6 @@ static bool init_guest_commpage(void) return true; } -#define ELF_PLATFORM get_elf_platform() - -static const char *get_elf_platform(void) -{ - CPUARMState *env = cpu_env(thread_cpu); - -#if TARGET_BIG_ENDIAN -# define END "b" -#else -# define END "l" -#endif - - if (arm_feature(env, ARM_FEATURE_V8)) { - return "v8" END; - } else if (arm_feature(env, ARM_FEATURE_V7)) { - if (arm_feature(env, ARM_FEATURE_M)) { - return "v7m" END; - } else { - return "v7" END; - } - } else if (arm_feature(env, ARM_FEATURE_V6)) { - return "v6" END; - } else if (arm_feature(env, ARM_FEATURE_V5)) { - return "v5" END; - } else { - return "v4" END; - } - -#undef END -} - #if TARGET_BIG_ENDIAN #include "elf.h" #include "vdso-be8.c.inc" @@ -487,11 +458,6 @@ static const VdsoImageInfo *vdso_image_info(uint32_t elf_flags) #define ELF_ARCH EM_AARCH64 #define ELF_CLASS ELFCLASS64 -#if TARGET_BIG_ENDIAN -# define ELF_PLATFORM "aarch64_be" -#else -# define ELF_PLATFORM "aarch64" -#endif static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop) diff --git a/linux-user/loader.h b/linux-user/loader.h index 44bb4cbfd3..440871466c 100644 --- a/linux-user/loader.h +++ b/linux-user/loader.h @@ -105,8 +105,6 @@ abi_ulong get_elf_hwcap(CPUState *cs); abi_ulong get_elf_hwcap2(CPUState *cs); const char *elf_hwcap_str(uint32_t bit); const char *elf_hwcap2_str(uint32_t bit); -#if defined(TARGET_I386) const char *get_elf_platform(CPUState *cs); -#endif #endif /* LINUX_USER_LOADER_H */ From 084b3247a08da16c541efba059c308b0d9299bdc Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 28 Jul 2025 09:00:52 -1000 Subject: [PATCH 0066/2396] linux-user/loongarch64: Create get_elf_platform Move the string literal to a new function. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 2 +- linux-user/loongarch64/elfload.c | 5 +++++ linux-user/loongarch64/target_elf.h | 1 + 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 0e41737cf1..9d61feae30 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -665,7 +665,7 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, #define USE_ELF_CORE_DUMP #define ELF_EXEC_PAGESIZE 4096 -#define ELF_PLATFORM "loongarch" +#define ELF_PLATFORM get_elf_platform(thread_cpu) #endif /* TARGET_LOONGARCH64 */ diff --git a/linux-user/loongarch64/elfload.c b/linux-user/loongarch64/elfload.c index ee4a85b8d6..911352840f 100644 --- a/linux-user/loongarch64/elfload.c +++ b/linux-user/loongarch64/elfload.c @@ -56,3 +56,8 @@ abi_ulong get_elf_hwcap(CPUState *cs) return hwcaps; } + +const char *get_elf_platform(CPUState *cs) +{ + return "loongarch"; +} diff --git a/linux-user/loongarch64/target_elf.h b/linux-user/loongarch64/target_elf.h index 037740d36f..eb17927325 100644 --- a/linux-user/loongarch64/target_elf.h +++ b/linux-user/loongarch64/target_elf.h @@ -7,5 +7,6 @@ #define LOONGARCH_TARGET_ELF_H #define HAVE_ELF_HWCAP 1 +#define HAVE_ELF_PLATFORM 1 #endif From eaf983e04b342ab4c3401ad8add649ec626a0744 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 28 Jul 2025 09:06:39 -1000 Subject: [PATCH 0067/2396] linux-user/hppa: Create get_elf_platform Move the string literal to a new function. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 2 +- linux-user/hppa/elfload.c | 5 +++++ linux-user/hppa/target_elf.h | 2 ++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 9d61feae30..83cb6731ec 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -1046,7 +1046,7 @@ static inline void init_thread(struct target_pt_regs *regs, #define ELF_CLASS ELFCLASS32 #define ELF_ARCH EM_PARISC -#define ELF_PLATFORM "PARISC" +#define ELF_PLATFORM get_elf_platform(thread_cpu) #define STACK_GROWS_DOWN 0 #define STACK_ALIGNMENT 64 diff --git a/linux-user/hppa/elfload.c b/linux-user/hppa/elfload.c index 2274fcbde4..9dd3fe092a 100644 --- a/linux-user/hppa/elfload.c +++ b/linux-user/hppa/elfload.c @@ -9,3 +9,8 @@ const char *get_elf_cpu_model(uint32_t eflags) { return "hppa"; } + +const char *get_elf_platform(CPUState *cs) +{ + return "PARISC"; +} diff --git a/linux-user/hppa/target_elf.h b/linux-user/hppa/target_elf.h index 5826ca2cd2..85be00584d 100644 --- a/linux-user/hppa/target_elf.h +++ b/linux-user/hppa/target_elf.h @@ -8,4 +8,6 @@ #ifndef HPPA_TARGET_ELF_H #define HPPA_TARGET_ELF_H +#define HAVE_ELF_PLATFORM 1 + #endif From f10c3d90849da63916905f7bd155d5737aa4378f Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 28 Jul 2025 09:18:03 -1000 Subject: [PATCH 0068/2396] linux-user: Remove ELF_PLATFORM All real definitions of ELF_PLATFORM are now identical, and the stub definitions are NULL. Use HAVE_ELF_PLATFORM and provide a stub as a fallback definition of get_elf_platform. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 83cb6731ec..d2d73b06fc 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -149,8 +149,6 @@ typedef abi_int target_pid_t; #ifdef TARGET_I386 -#define ELF_PLATFORM get_elf_platform(thread_cpu) - #ifdef TARGET_X86_64 #define ELF_CLASS ELFCLASS64 #define ELF_ARCH EM_X86_64 @@ -309,8 +307,6 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUX86State *en #ifdef TARGET_ARM -#define ELF_PLATFORM get_elf_platform(thread_cpu) - #ifndef TARGET_AARCH64 /* 32 bit ARM definitions */ @@ -665,8 +661,6 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, #define USE_ELF_CORE_DUMP #define ELF_EXEC_PAGESIZE 4096 -#define ELF_PLATFORM get_elf_platform(thread_cpu) - #endif /* TARGET_LOONGARCH64 */ #ifdef TARGET_MIPS @@ -846,8 +840,6 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, (*regs)[33] = tswapreg(cpu_get_sr(env)); } -#define ELF_PLATFORM NULL - #endif /* TARGET_OPENRISC */ #ifdef TARGET_SH4 @@ -1046,7 +1038,6 @@ static inline void init_thread(struct target_pt_regs *regs, #define ELF_CLASS ELFCLASS32 #define ELF_ARCH EM_PARISC -#define ELF_PLATFORM get_elf_platform(thread_cpu) #define STACK_GROWS_DOWN 0 #define STACK_ALIGNMENT 64 @@ -1182,10 +1173,6 @@ static inline void init_thread(struct target_pt_regs *regs, #define ELF_BASE_PLATFORM (NULL) #endif -#ifndef ELF_PLATFORM -#define ELF_PLATFORM (NULL) -#endif - #ifndef ELF_MACHINE #define ELF_MACHINE ELF_ARCH #endif @@ -1229,6 +1216,9 @@ abi_ulong get_elf_hwcap(CPUState *cs) { return 0; } abi_ulong get_elf_hwcap2(CPUState *cs) { g_assert_not_reached(); } #define HAVE_ELF_HWCAP2 0 #endif +#ifndef HAVE_ELF_PLATFORM +const char *get_elf_platform(CPUState *cs) { return NULL; } +#endif #include "elf.h" @@ -1699,7 +1689,7 @@ static abi_ulong create_elf_tables(abi_ulong p, int argc, int envc, } u_platform = 0; - k_platform = ELF_PLATFORM; + k_platform = get_elf_platform(thread_cpu); if (k_platform) { size_t len = strlen(k_platform) + 1; if (STACK_GROWS_DOWN) { From f8498e084eaa7f78d455d5f2f7a308a4d0d5f39b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 28 Jul 2025 09:24:31 -1000 Subject: [PATCH 0069/2396] linux-user: Move get_elf_base_platform to mips/elfload.c Pass in CPUState; define HAVE_ELF_BASE_PLATFORM. Since this was the only instance of ELF_BASE_PLATFORM, go ahead and provide the stub definition for other platforms. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 40 ++++------------------------------ linux-user/loader.h | 1 + linux-user/mips/elfload.c | 30 +++++++++++++++++++++++++ linux-user/mips/target_elf.h | 1 + linux-user/mips64/target_elf.h | 1 + 5 files changed, 37 insertions(+), 36 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index d2d73b06fc..4facaa7e27 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -679,37 +679,6 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, #define elf_check_abi(x) (!((x) & EF_MIPS_ABI2)) #endif -#define ELF_BASE_PLATFORM get_elf_base_platform() - -#define MATCH_PLATFORM_INSN(_flags, _base_platform) \ - do { if ((cpu->env.insn_flags & (_flags)) == _flags) \ - { return _base_platform; } } while (0) - -static const char *get_elf_base_platform(void) -{ - MIPSCPU *cpu = MIPS_CPU(thread_cpu); - - /* 64 bit ISAs goes first */ - MATCH_PLATFORM_INSN(CPU_MIPS64R6, "mips64r6"); - MATCH_PLATFORM_INSN(CPU_MIPS64R5, "mips64r5"); - MATCH_PLATFORM_INSN(CPU_MIPS64R2, "mips64r2"); - MATCH_PLATFORM_INSN(CPU_MIPS64R1, "mips64"); - MATCH_PLATFORM_INSN(CPU_MIPS5, "mips5"); - MATCH_PLATFORM_INSN(CPU_MIPS4, "mips4"); - MATCH_PLATFORM_INSN(CPU_MIPS3, "mips3"); - - /* 32 bit ISAs */ - MATCH_PLATFORM_INSN(CPU_MIPS32R6, "mips32r6"); - MATCH_PLATFORM_INSN(CPU_MIPS32R5, "mips32r5"); - MATCH_PLATFORM_INSN(CPU_MIPS32R2, "mips32r2"); - MATCH_PLATFORM_INSN(CPU_MIPS32R1, "mips32"); - MATCH_PLATFORM_INSN(CPU_MIPS2, "mips2"); - - /* Fallback */ - return "mips"; -} -#undef MATCH_PLATFORM_INSN - static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop) { @@ -1169,10 +1138,6 @@ static inline void init_thread(struct target_pt_regs *regs, #endif /* TARGET_HEXAGON */ -#ifndef ELF_BASE_PLATFORM -#define ELF_BASE_PLATFORM (NULL) -#endif - #ifndef ELF_MACHINE #define ELF_MACHINE ELF_ARCH #endif @@ -1219,6 +1184,9 @@ abi_ulong get_elf_hwcap2(CPUState *cs) { g_assert_not_reached(); } #ifndef HAVE_ELF_PLATFORM const char *get_elf_platform(CPUState *cs) { return NULL; } #endif +#ifndef HAVE_ELF_BASE_PLATFORM +const char *get_elf_base_platform(CPUState *cs) { return NULL; } +#endif #include "elf.h" @@ -1673,7 +1641,7 @@ static abi_ulong create_elf_tables(abi_ulong p, int argc, int envc, } u_base_platform = 0; - k_base_platform = ELF_BASE_PLATFORM; + k_base_platform = get_elf_base_platform(thread_cpu); if (k_base_platform) { size_t len = strlen(k_base_platform) + 1; if (STACK_GROWS_DOWN) { diff --git a/linux-user/loader.h b/linux-user/loader.h index 440871466c..42cba90dea 100644 --- a/linux-user/loader.h +++ b/linux-user/loader.h @@ -106,5 +106,6 @@ abi_ulong get_elf_hwcap2(CPUState *cs); const char *elf_hwcap_str(uint32_t bit); const char *elf_hwcap2_str(uint32_t bit); const char *get_elf_platform(CPUState *cs); +const char *get_elf_base_platform(CPUState *cs); #endif /* LINUX_USER_LOADER_H */ diff --git a/linux-user/mips/elfload.c b/linux-user/mips/elfload.c index 739f71c21b..c353ccc1ad 100644 --- a/linux-user/mips/elfload.c +++ b/linux-user/mips/elfload.c @@ -92,3 +92,33 @@ abi_ulong get_elf_hwcap(CPUState *cs) #undef GET_FEATURE_REG_EQU #undef GET_FEATURE_REG_SET #undef GET_FEATURE_INSN + +#define MATCH_PLATFORM_INSN(_flags, _base_platform) \ + do { if ((cpu->env.insn_flags & (_flags)) == _flags) \ + { return _base_platform; } } while (0) + +const char *get_elf_base_platform(CPUState *cs) +{ + MIPSCPU *cpu = MIPS_CPU(cs); + + /* 64 bit ISAs goes first */ + MATCH_PLATFORM_INSN(CPU_MIPS64R6, "mips64r6"); + MATCH_PLATFORM_INSN(CPU_MIPS64R5, "mips64r5"); + MATCH_PLATFORM_INSN(CPU_MIPS64R2, "mips64r2"); + MATCH_PLATFORM_INSN(CPU_MIPS64R1, "mips64"); + MATCH_PLATFORM_INSN(CPU_MIPS5, "mips5"); + MATCH_PLATFORM_INSN(CPU_MIPS4, "mips4"); + MATCH_PLATFORM_INSN(CPU_MIPS3, "mips3"); + + /* 32 bit ISAs */ + MATCH_PLATFORM_INSN(CPU_MIPS32R6, "mips32r6"); + MATCH_PLATFORM_INSN(CPU_MIPS32R5, "mips32r5"); + MATCH_PLATFORM_INSN(CPU_MIPS32R2, "mips32r2"); + MATCH_PLATFORM_INSN(CPU_MIPS32R1, "mips32"); + MATCH_PLATFORM_INSN(CPU_MIPS2, "mips2"); + + /* Fallback */ + return "mips"; +} + +#undef MATCH_PLATFORM_INSN diff --git a/linux-user/mips/target_elf.h b/linux-user/mips/target_elf.h index 877f8347d7..08e699c085 100644 --- a/linux-user/mips/target_elf.h +++ b/linux-user/mips/target_elf.h @@ -9,5 +9,6 @@ #define MIPS_TARGET_ELF_H #define HAVE_ELF_HWCAP 1 +#define HAVE_ELF_BASE_PLATFORM 1 #endif diff --git a/linux-user/mips64/target_elf.h b/linux-user/mips64/target_elf.h index c0347e5cb6..24bb7fcd3f 100644 --- a/linux-user/mips64/target_elf.h +++ b/linux-user/mips64/target_elf.h @@ -9,5 +9,6 @@ #define MIPS64_TARGET_ELF_H #define HAVE_ELF_HWCAP 1 +#define HAVE_ELF_BASE_PLATFORM 1 #endif From d8329660b2e103ac2bbbf9ae933c0c742d44864e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 28 Jul 2025 09:49:39 -1000 Subject: [PATCH 0070/2396] linux-user: Move target_cpu_copy_regs decl to qemu.h The function is not used by bsd-user, so placement within include/user/cpu_loop.h is not ideal. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- include/user/cpu_loop.h | 4 ---- linux-user/qemu.h | 3 +++ 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/include/user/cpu_loop.h b/include/user/cpu_loop.h index ad8a1d711f..346e37ede8 100644 --- a/include/user/cpu_loop.h +++ b/include/user/cpu_loop.h @@ -81,8 +81,4 @@ void target_exception_dump(CPUArchState *env, const char *fmt, int code); #define EXCP_DUMP(env, fmt, code) \ target_exception_dump(env, fmt, code) -typedef struct target_pt_regs target_pt_regs; - -void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs); - #endif diff --git a/linux-user/qemu.h b/linux-user/qemu.h index 4d6fad28c6..0c3cfe93a1 100644 --- a/linux-user/qemu.h +++ b/linux-user/qemu.h @@ -359,4 +359,7 @@ void *lock_user_string(abi_ulong guest_addr); /* Clone cpu state */ CPUArchState *cpu_copy(CPUArchState *env); +typedef struct target_pt_regs target_pt_regs; +void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs); + #endif /* QEMU_H */ From 1f2f4c0fbcc527f47e2f9d5708ae7df824bd574f Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 28 Jul 2025 10:00:36 -1000 Subject: [PATCH 0071/2396] linux-user: Create do_init_main_thread Provide a unified function to initialize the main thread. Keep target_pt_regs isolated to this function. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 7 +++++-- linux-user/linuxload.c | 6 ++---- linux-user/loader.h | 5 ++--- linux-user/main.c | 10 +++------- 4 files changed, 12 insertions(+), 16 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 4facaa7e27..6fce74f45a 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -3619,7 +3619,10 @@ static int elf_core_dump(int signr, const CPUArchState *env) } #endif /* USE_ELF_CORE_DUMP */ -void do_init_thread(struct target_pt_regs *regs, struct image_info *infop) +void do_init_main_thread(CPUState *cs, struct image_info *infop) { - init_thread(regs, infop); + target_pt_regs regs = { }; + + init_thread(®s, infop); + target_cpu_copy_regs(cpu_env(cs), ®s); } diff --git a/linux-user/linuxload.c b/linux-user/linuxload.c index 37f132be4a..85d700953e 100644 --- a/linux-user/linuxload.c +++ b/linux-user/linuxload.c @@ -139,8 +139,7 @@ abi_ulong loader_build_argptr(int envc, int argc, abi_ulong sp, } int loader_exec(int fdexec, const char *filename, char **argv, char **envp, - struct target_pt_regs *regs, struct image_info *infop, - struct linux_binprm *bprm) + struct image_info *infop, struct linux_binprm *bprm) { int retval; @@ -175,8 +174,7 @@ int loader_exec(int fdexec, const char *filename, char **argv, char **envp, return retval; } - /* Success. Initialize important registers. */ - do_init_thread(regs, infop); + /* Success. */ return 0; } diff --git a/linux-user/loader.h b/linux-user/loader.h index 42cba90dea..e0291cc3b0 100644 --- a/linux-user/loader.h +++ b/linux-user/loader.h @@ -82,12 +82,11 @@ struct linux_binprm { int (*core_dump)(int, const CPUArchState *); /* coredump routine */ }; -void do_init_thread(struct target_pt_regs *regs, struct image_info *infop); +void do_init_main_thread(CPUState *cs, struct image_info *infop); abi_ulong loader_build_argptr(int envc, int argc, abi_ulong sp, abi_ulong stringp, int push_ptr); int loader_exec(int fdexec, const char *filename, char **argv, char **envp, - struct target_pt_regs *regs, struct image_info *infop, - struct linux_binprm *); + struct image_info *infop, struct linux_binprm *); uint32_t get_elf_eflags(int fd); int load_elf_binary(struct linux_binprm *bprm, struct image_info *info); diff --git a/linux-user/main.c b/linux-user/main.c index ad1a29d198..e21842bde9 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -696,7 +696,6 @@ static int parse_args(int argc, char **argv) int main(int argc, char **argv, char **envp) { - struct target_pt_regs regs1, *regs = ®s1; struct image_info info1, *info = &info1; struct linux_binprm bprm; TaskState *ts; @@ -762,9 +761,6 @@ int main(int argc, char **argv, char **envp) trace_init_file(); qemu_plugin_load_list(&plugins, &error_fatal); - /* Zero out regs */ - memset(regs, 0, sizeof(struct target_pt_regs)); - /* Zero out image_info */ memset(info, 0, sizeof(struct image_info)); @@ -988,8 +984,8 @@ int main(int argc, char **argv, char **envp) fd_trans_init(); - ret = loader_exec(execfd, exec_path, target_argv, target_environ, regs, - info, &bprm); + ret = loader_exec(execfd, exec_path, target_argv, target_environ, + info, &bprm); if (ret != 0) { printf("Error while loading %s: %s\n", exec_path, strerror(-ret)); _exit(EXIT_FAILURE); @@ -1041,7 +1037,7 @@ int main(int argc, char **argv, char **envp) the real value of GUEST_BASE into account. */ tcg_prologue_init(); - target_cpu_copy_regs(env, regs); + do_init_main_thread(cpu, info); if (gdbstub) { gdbserver_start(gdbstub, &error_fatal); From 8d4020dd025dc37d9003c6a514e758d8c6ef99b9 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 28 Jul 2025 10:17:44 -1000 Subject: [PATCH 0072/2396] linux-user/i386: Create init_main_thread Merge init_thread and target_cpu_copy_regs. There's no point going through a target_pt_regs intermediate. Temporarily introduce HAVE_INIT_MAIN_THREAD during conversion. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 29 +++++------------------ linux-user/i386/cpu_loop.c | 48 +++++++++++++++++--------------------- linux-user/qemu.h | 1 + 3 files changed, 29 insertions(+), 49 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 6fce74f45a..89f3972253 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -149,17 +149,12 @@ typedef abi_int target_pid_t; #ifdef TARGET_I386 +#define HAVE_INIT_MAIN_THREAD + #ifdef TARGET_X86_64 #define ELF_CLASS ELFCLASS64 #define ELF_ARCH EM_X86_64 -static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop) -{ - regs->rax = 0; - regs->rsp = infop->start_stack; - regs->rip = infop->entry; -} - #define ELF_NREG 27 typedef target_elf_greg_t target_elf_gregset_t[ELF_NREG]; @@ -237,22 +232,6 @@ static bool init_guest_commpage(void) #define EXSTACK_DEFAULT true -static inline void init_thread(struct target_pt_regs *regs, - struct image_info *infop) -{ - regs->esp = infop->start_stack; - regs->eip = infop->entry; - - /* SVR4/i386 ABI (pages 3-31, 3-32) says that when the program - starts %edx contains a pointer to a function which might be - registered using `atexit'. This provides a mean for the - dynamic linker to call DT_FINI functions for shared libraries - that have been loaded before the code runs. - - A value of 0 tells we have no such handler. */ - regs->edx = 0; -} - #define ELF_NREG 17 typedef target_elf_greg_t target_elf_gregset_t[ELF_NREG]; @@ -3621,8 +3600,12 @@ static int elf_core_dump(int signr, const CPUArchState *env) void do_init_main_thread(CPUState *cs, struct image_info *infop) { +#ifdef HAVE_INIT_MAIN_THREAD + init_main_thread(cs, infop); +#else target_pt_regs regs = { }; init_thread(®s, infop); target_cpu_copy_regs(cpu_env(cs), ®s); +#endif } diff --git a/linux-user/i386/cpu_loop.c b/linux-user/i386/cpu_loop.c index d96d5553fa..7b2d8b03d8 100644 --- a/linux-user/i386/cpu_loop.c +++ b/linux-user/i386/cpu_loop.c @@ -331,11 +331,10 @@ static void target_cpu_free(void *obj) g_free(obj); } -void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) +void init_main_thread(CPUState *cpu, struct image_info *info) { - CPUState *cpu = env_cpu(env); + CPUArchState *env = cpu_env(cpu); bool is64 = (env->features[FEAT_8000_0001_EDX] & CPUID_EXT2_LM) != 0; - int i; OBJECT(cpu)->free = target_cpu_free; env->cr[0] = CR0_PG_MASK | CR0_WP_MASK | CR0_PE_MASK; @@ -361,28 +360,25 @@ void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) /* flags setup : we activate the IRQs by default as in user mode */ env->eflags |= IF_MASK; - /* linux register setup */ -#ifndef TARGET_ABI32 - env->regs[R_EAX] = regs->rax; - env->regs[R_EBX] = regs->rbx; - env->regs[R_ECX] = regs->rcx; - env->regs[R_EDX] = regs->rdx; - env->regs[R_ESI] = regs->rsi; - env->regs[R_EDI] = regs->rdi; - env->regs[R_EBP] = regs->rbp; - env->regs[R_ESP] = regs->rsp; - env->eip = regs->rip; -#else - env->regs[R_EAX] = regs->eax; - env->regs[R_EBX] = regs->ebx; - env->regs[R_ECX] = regs->ecx; - env->regs[R_EDX] = regs->edx; - env->regs[R_ESI] = regs->esi; - env->regs[R_EDI] = regs->edi; - env->regs[R_EBP] = regs->ebp; - env->regs[R_ESP] = regs->esp; - env->eip = regs->eip; -#endif + /* + * Linux register setup. + * + * SVR4/i386 ABI (pages 3-31, 3-32) says that when the program + * starts %edx contains a pointer to a function which might be + * registered using `atexit'. This provides a mean for the + * dynamic linker to call DT_FINI functions for shared libraries + * that have been loaded before the code runs. + * A value of 0 tells we have no such handler. + * + * This applies to x86_64 as well as i386. + * + * That said, the kernel's ELF_PLAT_INIT simply zeros all of the general + * registers. Note that x86_cpu_reset_hold will set %edx to cpuid_version; + * clear all general registers defensively. + */ + memset(env->regs, 0, sizeof(env->regs)); + env->regs[R_ESP] = info->start_stack; + env->eip = info->entry; /* linux interrupt setup */ #ifndef TARGET_ABI32 @@ -394,7 +390,7 @@ void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0); idt_table = g2h_untagged(env->idt.base); - for (i = 0; i < 20; i++) { + for (int i = 0; i < 20; i++) { set_idt(i, 0, is64); } set_idt(3, 3, is64); diff --git a/linux-user/qemu.h b/linux-user/qemu.h index 0c3cfe93a1..8a9500d4f4 100644 --- a/linux-user/qemu.h +++ b/linux-user/qemu.h @@ -361,5 +361,6 @@ CPUArchState *cpu_copy(CPUArchState *env); typedef struct target_pt_regs target_pt_regs; void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs); +void init_main_thread(CPUState *cs, struct image_info *info); #endif /* QEMU_H */ From ea8683d2b3777fcb934a075df8b63d45642097d6 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 28 Jul 2025 10:43:26 -1000 Subject: [PATCH 0073/2396] linux-user/arm: Create init_main_thread Merge init_thread and target_cpu_copy_regs. There's no point going through a target_pt_regs intermediate. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/arm/cpu_loop.c | 51 ++++++++++++++++++++++++++++++++------- linux-user/elfload.c | 41 +------------------------------ 2 files changed, 43 insertions(+), 49 deletions(-) diff --git a/linux-user/arm/cpu_loop.c b/linux-user/arm/cpu_loop.c index 9d54422736..739e1607e3 100644 --- a/linux-user/arm/cpu_loop.c +++ b/linux-user/arm/cpu_loop.c @@ -480,17 +480,50 @@ void cpu_loop(CPUARMState *env) } } -void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) +void init_main_thread(CPUState *cs, struct image_info *info) { - CPUState *cpu = env_cpu(env); - TaskState *ts = get_task_state(cpu); - struct image_info *info = ts->info; - int i; + CPUARMState *env = cpu_env(cs); + abi_ptr stack = info->start_stack; + abi_ptr entry = info->entry; - cpsr_write(env, regs->uregs[16], CPSR_USER | CPSR_EXEC, - CPSRWriteByInstr); - for(i = 0; i < 16; i++) { - env->regs[i] = regs->uregs[i]; + cpsr_write(env, ARM_CPU_MODE_USR | (entry & 1 ? CPSR_T : 0), + CPSR_USER | CPSR_EXEC, CPSRWriteByInstr); + + env->regs[15] = entry & 0xfffffffe; + env->regs[13] = stack; + + /* FIXME - what to for failure of get_user()? */ + get_user_ual(env->regs[2], stack + 8); /* envp */ + get_user_ual(env->regs[1], stack + 4); /* envp */ + + /* + * Per the SVR4 ABI, r0 contains a pointer to a function to be + * registered with atexit. A value of 0 means we have no such handler. + */ + env->regs[0] = 0; + + /* For uClinux PIC binaries. */ + /* XXX: Linux does this only on ARM with no MMU (do we care?) */ + env->regs[10] = info->start_data; + + /* Support ARM FDPIC. */ + if (info_is_fdpic(info)) { + /* + * As described in the ABI document, r7 points to the loadmap info + * prepared by the kernel. If an interpreter is needed, r8 points + * to the interpreter loadmap and r9 points to the interpreter + * PT_DYNAMIC info. If no interpreter is needed, r8 is zero, and + * r9 points to the main program PT_DYNAMIC info. + */ + env->regs[7] = info->loadmap_addr; + if (info->interpreter_loadmap_addr) { + /* Executable is dynamically loaded. */ + env->regs[8] = info->interpreter_loadmap_addr; + env->regs[9] = info->interpreter_pt_dynamic_addr; + } else { + env->regs[8] = 0; + env->regs[9] = info->pt_dynamic_addr; + } } if (TARGET_BIG_ENDIAN) { diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 89f3972253..9586873954 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -293,46 +293,7 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUX86State *en #define ELF_CLASS ELFCLASS32 #define EXSTACK_DEFAULT true -static inline void init_thread(struct target_pt_regs *regs, - struct image_info *infop) -{ - abi_long stack = infop->start_stack; - memset(regs, 0, sizeof(*regs)); - - regs->uregs[16] = ARM_CPU_MODE_USR; - if (infop->entry & 1) { - regs->uregs[16] |= CPSR_T; - } - regs->uregs[15] = infop->entry & 0xfffffffe; - regs->uregs[13] = infop->start_stack; - /* FIXME - what to for failure of get_user()? */ - get_user_ual(regs->uregs[2], stack + 8); /* envp */ - get_user_ual(regs->uregs[1], stack + 4); /* envp */ - /* XXX: it seems that r0 is zeroed after ! */ - regs->uregs[0] = 0; - /* For uClinux PIC binaries. */ - /* XXX: Linux does this only on ARM with no MMU (do we care ?) */ - regs->uregs[10] = infop->start_data; - - /* Support ARM FDPIC. */ - if (info_is_fdpic(infop)) { - /* As described in the ABI document, r7 points to the loadmap info - * prepared by the kernel. If an interpreter is needed, r8 points - * to the interpreter loadmap and r9 points to the interpreter - * PT_DYNAMIC info. If no interpreter is needed, r8 is zero, and - * r9 points to the main program PT_DYNAMIC info. - */ - regs->uregs[7] = infop->loadmap_addr; - if (infop->interpreter_loadmap_addr) { - /* Executable is dynamically loaded. */ - regs->uregs[8] = infop->interpreter_loadmap_addr; - regs->uregs[9] = infop->interpreter_pt_dynamic_addr; - } else { - regs->uregs[8] = 0; - regs->uregs[9] = infop->pt_dynamic_addr; - } - } -} +#define HAVE_INIT_MAIN_THREAD #define ELF_NREG 18 typedef target_elf_greg_t target_elf_gregset_t[ELF_NREG]; From e872342837ed61af5f2ed15541375adf0c23e0eb Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 3 Aug 2025 07:51:51 +1000 Subject: [PATCH 0074/2396] linux-user/arm: Remove a.out startup remenents The setting of r1/r2 was removed in kernel commit acfdd4b1f7590d0 ("ARM: 7791/1: a.out: remove partial a.out support"), and the kernel commit message explains the history. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/arm/cpu_loop.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/linux-user/arm/cpu_loop.c b/linux-user/arm/cpu_loop.c index 739e1607e3..9aeb9b0087 100644 --- a/linux-user/arm/cpu_loop.c +++ b/linux-user/arm/cpu_loop.c @@ -492,10 +492,6 @@ void init_main_thread(CPUState *cs, struct image_info *info) env->regs[15] = entry & 0xfffffffe; env->regs[13] = stack; - /* FIXME - what to for failure of get_user()? */ - get_user_ual(env->regs[2], stack + 8); /* envp */ - get_user_ual(env->regs[1], stack + 4); /* envp */ - /* * Per the SVR4 ABI, r0 contains a pointer to a function to be * registered with atexit. A value of 0 means we have no such handler. From 2eaaf04ad7f9eb5bd578f3f225d118c9eb0bef1d Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 28 Jul 2025 11:04:34 -1000 Subject: [PATCH 0075/2396] linux-user/aarch64: Create init_main_thread Merge init_thread and target_cpu_copy_regs. There's no point going through a target_pt_regs intermediate. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/aarch64/cpu_loop.c | 14 ++++++-------- linux-user/elfload.c | 10 +--------- 2 files changed, 7 insertions(+), 17 deletions(-) diff --git a/linux-user/aarch64/cpu_loop.c b/linux-user/aarch64/cpu_loop.c index 030a630c93..4c4921152e 100644 --- a/linux-user/aarch64/cpu_loop.c +++ b/linux-user/aarch64/cpu_loop.c @@ -137,10 +137,10 @@ void cpu_loop(CPUARMState *env) } } -void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) +void init_main_thread(CPUState *cs, struct image_info *info) { + CPUARMState *env = cpu_env(cs); ARMCPU *cpu = env_archcpu(env); - int i; if (!(arm_feature(env, ARM_FEATURE_AARCH64))) { fprintf(stderr, @@ -148,14 +148,12 @@ void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) exit(EXIT_FAILURE); } - for (i = 0; i < 31; i++) { - env->xregs[i] = regs->regs[i]; - } - env->pc = regs->pc; - env->xregs[31] = regs->sp; + env->pc = info->entry & ~0x3ULL; + env->xregs[31] = info->start_stack; + #if TARGET_BIG_ENDIAN env->cp15.sctlr_el[1] |= SCTLR_E0E; - for (i = 1; i < 4; ++i) { + for (int i = 1; i < 4; ++i) { env->cp15.sctlr_el[i] |= SCTLR_EE; } arm_rebuild_hflags(env); diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 9586873954..f93afbdcea 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -395,15 +395,7 @@ static const VdsoImageInfo *vdso_image_info(uint32_t elf_flags) #define ELF_ARCH EM_AARCH64 #define ELF_CLASS ELFCLASS64 -static inline void init_thread(struct target_pt_regs *regs, - struct image_info *infop) -{ - abi_long stack = infop->start_stack; - memset(regs, 0, sizeof(*regs)); - - regs->pc = infop->entry & ~0x3ULL; - regs->sp = stack; -} +#define HAVE_INIT_MAIN_THREAD #define ELF_NREG 34 typedef target_elf_greg_t target_elf_gregset_t[ELF_NREG]; From 7b9efb7aaef9b43b22fce7051861f59fe15ff51a Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 28 Jul 2025 12:29:11 -1000 Subject: [PATCH 0076/2396] linux-user/sparc: Create init_main_thread Merge init_thread and target_cpu_copy_regs. There's no point going through a target_pt_regs intermediate. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 12 ++---------- linux-user/sparc/cpu_loop.c | 16 +++++++--------- 2 files changed, 9 insertions(+), 19 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index f93afbdcea..887a3a1cb2 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -438,16 +438,8 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, # define ELF_ARCH EM_SPARCV9 #endif -static inline void init_thread(struct target_pt_regs *regs, - struct image_info *infop) -{ - /* Note that target_cpu_copy_regs does not read psr/tstate. */ - regs->pc = infop->entry; - regs->npc = regs->pc + 4; - regs->y = 0; - regs->u_regs[14] = (infop->start_stack - 16 * sizeof(abi_ulong) - - TARGET_STACK_BIAS); -} +#define HAVE_INIT_MAIN_THREAD + #endif /* TARGET_SPARC */ #ifdef TARGET_PPC diff --git a/linux-user/sparc/cpu_loop.c b/linux-user/sparc/cpu_loop.c index 68f1e8ecd4..7d30cd1ff2 100644 --- a/linux-user/sparc/cpu_loop.c +++ b/linux-user/sparc/cpu_loop.c @@ -357,14 +357,12 @@ void cpu_loop (CPUSPARCState *env) } } -void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) +void init_main_thread(CPUState *cs, struct image_info *info) { - int i; - env->pc = regs->pc; - env->npc = regs->npc; - env->y = regs->y; - for(i = 0; i < 8; i++) - env->gregs[i] = regs->u_regs[i]; - for(i = 0; i < 8; i++) - env->regwptr[i] = regs->u_regs[i + 8]; + CPUArchState *env = cpu_env(cs); + + env->pc = info->entry; + env->npc = env->pc + 4; + env->regwptr[WREG_SP] = (info->start_stack - 16 * sizeof(abi_ulong) + - TARGET_STACK_BIAS); } From 88c9adef2b0fff84f0e9cbc728e860cfbad52008 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 28 Jul 2025 13:09:29 -1000 Subject: [PATCH 0077/2396] linux-user/ppc: Create init_main_thread Merge init_thread and target_cpu_copy_regs. There's no point going through a target_pt_regs intermediate. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 17 +---------------- linux-user/ppc/cpu_loop.c | 26 ++++++++++++++++++-------- 2 files changed, 19 insertions(+), 24 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 887a3a1cb2..a30431c7a2 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -485,22 +485,7 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, NEW_AUX_ENT(AT_UCACHEBSIZE, 0); \ } while (0) -static inline void init_thread(struct target_pt_regs *_regs, struct image_info *infop) -{ - _regs->gpr[1] = infop->start_stack; -#if defined(TARGET_PPC64) - if (get_ppc64_abi(infop) < 2) { - uint64_t val; - get_user_u64(val, infop->entry + 8); - _regs->gpr[2] = val + infop->load_bias; - get_user_u64(val, infop->entry); - infop->entry = val + infop->load_bias; - } else { - _regs->gpr[12] = infop->entry; /* r12 set to global entry address */ - } -#endif - _regs->nip = infop->entry; -} +#define HAVE_INIT_MAIN_THREAD /* See linux kernel: arch/powerpc/include/asm/elf.h. */ #define ELF_NREG 48 diff --git a/linux-user/ppc/cpu_loop.c b/linux-user/ppc/cpu_loop.c index 2a0efaffcd..22885ffd90 100644 --- a/linux-user/ppc/cpu_loop.c +++ b/linux-user/ppc/cpu_loop.c @@ -378,21 +378,31 @@ void cpu_loop(CPUPPCState *env) } } -void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) +void init_main_thread(CPUState *cs, struct image_info *info) { - int i; + CPUArchState *env = cpu_env(cs); + abi_ptr entry = info->entry; + + env->gpr[1] = info->start_stack; + +#ifdef TARGET_PPC64 + if (get_ppc64_abi(info) < 2) { + uint64_t val; + get_user_u64(val, entry + 8); + env->gpr[2] = val + info->load_bias; + get_user_u64(val, entry); + entry = val + info->load_bias; + } else { + env->gpr[12] = entry; /* r12 set to global entry address */ + } -#if defined(TARGET_PPC64) int flag = (env->insns_flags2 & PPC2_BOOKE206) ? MSR_CM : MSR_SF; #if defined(TARGET_ABI32) ppc_store_msr(env, env->msr & ~((target_ulong)1 << flag)); #else ppc_store_msr(env, env->msr | (target_ulong)1 << flag); #endif -#endif +#endif /* TARGET_PPC64 */ - env->nip = regs->nip; - for(i = 0; i < 32; i++) { - env->gpr[i] = regs->gpr[i]; - } + env->nip = entry; } From a2c83f5156324b88cf5c92ed777648fc1aaccf7f Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 28 Jul 2025 13:22:48 -1000 Subject: [PATCH 0078/2396] linux-user/loongarch64: Create init_main_thread Merge init_thread and target_cpu_copy_regs. There's no point going through a target_pt_regs intermediate. Note that init_thread had set crmd in target_pt_regs, but target_cpu_copy_regs did not copy to env. This turns out to be ok because loongarch_cpu_reset_hold initializes CRMD properly. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 9 +-------- linux-user/loongarch64/cpu_loop.c | 11 ++++------- 2 files changed, 5 insertions(+), 15 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index a30431c7a2..0feccfbe91 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -533,14 +533,7 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUPPCState *en #define VDSO_HEADER "vdso.c.inc" -static inline void init_thread(struct target_pt_regs *regs, - struct image_info *infop) -{ - /*Set crmd PG,DA = 1,0 */ - regs->csr.crmd = 2 << 3; - regs->csr.era = infop->entry; - regs->regs[3] = infop->start_stack; -} +#define HAVE_INIT_MAIN_THREAD /* See linux kernel: arch/loongarch/include/asm/elf.h */ #define ELF_NREG 45 diff --git a/linux-user/loongarch64/cpu_loop.c b/linux-user/loongarch64/cpu_loop.c index ec8a06c88c..a0a4cbb7cc 100644 --- a/linux-user/loongarch64/cpu_loop.c +++ b/linux-user/loongarch64/cpu_loop.c @@ -120,13 +120,10 @@ void cpu_loop(CPULoongArchState *env) } } -void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) +void init_main_thread(CPUState *cs, struct image_info *info) { - int i; - - for (i = 0; i < 32; i++) { - env->gpr[i] = regs->regs[i]; - } - env->pc = regs->csr.era; + CPUArchState *env = cpu_env(cs); + env->pc = info->entry; + env->gpr[3] = info->start_stack; } From e17cc00f7195f387720cd839dcb83fd17c2147c9 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 28 Jul 2025 13:33:18 -1000 Subject: [PATCH 0079/2396] linux-user/mips: Create init_main_thread Merge init_thread and target_cpu_copy_regs. There's no point going through a target_pt_regs intermediate. Note that init_thread had set cp0_status in target_pt_regs, but target_cpu_copy_regs did not copy to env. This turns out to be ok because mips_cpu_reset_hold initializes CP0_Status properly. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 8 +------- linux-user/mips/cpu_loop.c | 16 ++++++---------- 2 files changed, 7 insertions(+), 17 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 0feccfbe91..ac96755b06 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -581,13 +581,7 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, #define elf_check_abi(x) (!((x) & EF_MIPS_ABI2)) #endif -static inline void init_thread(struct target_pt_regs *regs, - struct image_info *infop) -{ - regs->cp0_status = 2 << CP0St_KSU; - regs->cp0_epc = infop->entry; - regs->regs[29] = infop->start_stack; -} +#define HAVE_INIT_MAIN_THREAD /* See linux kernel: arch/mips/include/asm/elf.h. */ #define ELF_NREG 45 diff --git a/linux-user/mips/cpu_loop.c b/linux-user/mips/cpu_loop.c index 6405806eb0..e67b8a2e46 100644 --- a/linux-user/mips/cpu_loop.c +++ b/linux-user/mips/cpu_loop.c @@ -211,12 +211,9 @@ done_syscall: } } -void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) +void init_main_thread(CPUState *cs, struct image_info *info) { - CPUState *cpu = env_cpu(env); - TaskState *ts = get_task_state(cpu); - struct image_info *info = ts->info; - int i; + CPUArchState *env = cpu_env(cs); struct mode_req { bool single; @@ -245,12 +242,11 @@ void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) struct mode_req prog_req; struct mode_req interp_req; + target_ulong entry = info->entry; - for(i = 0; i < 32; i++) { - env->active_tc.gpr[i] = regs->regs[i]; - } - env->active_tc.PC = regs->cp0_epc & ~(target_ulong)1; - if (regs->cp0_epc & 1) { + env->active_tc.gpr[29] = info->start_stack; + env->active_tc.PC = entry & ~(target_ulong)1; + if (entry & 1) { env->hflags |= MIPS_HFLAG_M16; } From 2ffaa3f70dfbf07a3bdb1035fb4ba4e0f7f94de1 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 28 Jul 2025 21:44:06 -1000 Subject: [PATCH 0080/2396] linux-user/microblaze: Create init_main_thread Merge init_thread and target_cpu_copy_regs. There's no point going through a target_pt_regs intermediate. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 8 +------ linux-user/microblaze/cpu_loop.c | 39 ++++---------------------------- 2 files changed, 6 insertions(+), 41 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index ac96755b06..3f9ec49359 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -640,13 +640,7 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUMIPSState *e #define ELF_CLASS ELFCLASS32 #define ELF_ARCH EM_MICROBLAZE -static inline void init_thread(struct target_pt_regs *regs, - struct image_info *infop) -{ - regs->pc = infop->entry; - regs->r1 = infop->start_stack; - -} +#define HAVE_INIT_MAIN_THREAD #define ELF_EXEC_PAGESIZE 4096 diff --git a/linux-user/microblaze/cpu_loop.c b/linux-user/microblaze/cpu_loop.c index 87236c166f..d8277961c7 100644 --- a/linux-user/microblaze/cpu_loop.c +++ b/linux-user/microblaze/cpu_loop.c @@ -127,39 +127,10 @@ void cpu_loop(CPUMBState *env) } } -void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) +void init_main_thread(CPUState *cs, struct image_info *info) { - env->regs[0] = regs->r0; - env->regs[1] = regs->r1; - env->regs[2] = regs->r2; - env->regs[3] = regs->r3; - env->regs[4] = regs->r4; - env->regs[5] = regs->r5; - env->regs[6] = regs->r6; - env->regs[7] = regs->r7; - env->regs[8] = regs->r8; - env->regs[9] = regs->r9; - env->regs[10] = regs->r10; - env->regs[11] = regs->r11; - env->regs[12] = regs->r12; - env->regs[13] = regs->r13; - env->regs[14] = regs->r14; - env->regs[15] = regs->r15; - env->regs[16] = regs->r16; - env->regs[17] = regs->r17; - env->regs[18] = regs->r18; - env->regs[19] = regs->r19; - env->regs[20] = regs->r20; - env->regs[21] = regs->r21; - env->regs[22] = regs->r22; - env->regs[23] = regs->r23; - env->regs[24] = regs->r24; - env->regs[25] = regs->r25; - env->regs[26] = regs->r26; - env->regs[27] = regs->r27; - env->regs[28] = regs->r28; - env->regs[29] = regs->r29; - env->regs[30] = regs->r30; - env->regs[31] = regs->r31; - env->pc = regs->pc; + CPUArchState *env = cpu_env(cs); + + env->pc = info->entry; + env->regs[1] = info->start_stack; } From 83411d840cfd80d7288ea096d905413754a87c66 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 28 Jul 2025 21:48:48 -1000 Subject: [PATCH 0081/2396] linux-user/openrisc: Create init_main_thread Merge init_thread and target_cpu_copy_regs. There's no point going through a target_pt_regs intermediate. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 7 +------ linux-user/openrisc/cpu_loop.c | 11 ++++------- 2 files changed, 5 insertions(+), 13 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 3f9ec49359..03c9539774 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -673,12 +673,7 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUMBState *env #define ELF_CLASS ELFCLASS32 #define ELF_DATA ELFDATA2MSB -static inline void init_thread(struct target_pt_regs *regs, - struct image_info *infop) -{ - regs->pc = infop->entry; - regs->gpr[1] = infop->start_stack; -} +#define HAVE_INIT_MAIN_THREAD #define USE_ELF_CORE_DUMP #define ELF_EXEC_PAGESIZE 8192 diff --git a/linux-user/openrisc/cpu_loop.c b/linux-user/openrisc/cpu_loop.c index 306b4f8eb4..8c72347a99 100644 --- a/linux-user/openrisc/cpu_loop.c +++ b/linux-user/openrisc/cpu_loop.c @@ -83,13 +83,10 @@ void cpu_loop(CPUOpenRISCState *env) } } -void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) +void init_main_thread(CPUState *cs, struct image_info *info) { - int i; + CPUArchState *env = cpu_env(cs); - for (i = 0; i < 32; i++) { - cpu_set_gpr(env, i, regs->gpr[i]); - } - env->pc = regs->pc; - cpu_set_sr(env, regs->sr); + env->pc = info->entry; + cpu_set_gpr(env, 1, info->start_stack); } From 011480ff599d76e62a0e49ddd21ba9023cdb0ec3 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 28 Jul 2025 21:51:02 -1000 Subject: [PATCH 0082/2396] linux-user/sh4: Create init_main_thread Merge init_thread and target_cpu_copy_regs. There's no point going through a target_pt_regs intermediate. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 8 +------- linux-user/sh4/cpu_loop.c | 10 ++++------ 2 files changed, 5 insertions(+), 13 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 03c9539774..8604308a31 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -701,13 +701,7 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, #define ELF_CLASS ELFCLASS32 #define ELF_ARCH EM_SH -static inline void init_thread(struct target_pt_regs *regs, - struct image_info *infop) -{ - /* Check other registers XXXXX */ - regs->pc = infop->entry; - regs->regs[15] = infop->start_stack; -} +#define HAVE_INIT_MAIN_THREAD /* See linux kernel: arch/sh/include/asm/elf.h. */ #define ELF_NREG 23 diff --git a/linux-user/sh4/cpu_loop.c b/linux-user/sh4/cpu_loop.c index ee9eff3428..259ea1cc8b 100644 --- a/linux-user/sh4/cpu_loop.c +++ b/linux-user/sh4/cpu_loop.c @@ -81,12 +81,10 @@ void cpu_loop(CPUSH4State *env) } } -void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) +void init_main_thread(CPUState *cs, struct image_info *info) { - int i; + CPUArchState *env = cpu_env(cs); - for(i = 0; i < 16; i++) { - env->gregs[i] = regs->regs[i]; - } - env->pc = regs->pc; + env->pc = info->entry; + env->gregs[15] = info->start_stack; } From ff053f9c5a4df1f0fb49ab7d7a0886a43f18a4f4 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 28 Jul 2025 21:53:30 -1000 Subject: [PATCH 0083/2396] linux-user/m68k: Create init_main_thread Merge init_thread and target_cpu_copy_regs. There's no point going through a target_pt_regs intermediate. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 11 +---------- linux-user/m68k/cpu_loop.c | 25 ++++++------------------- 2 files changed, 7 insertions(+), 29 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 8604308a31..46150586af 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -746,16 +746,7 @@ static inline void elf_core_copy_regs(target_elf_gregset_t *regs, #define ELF_CLASS ELFCLASS32 #define ELF_ARCH EM_68K -/* ??? Does this need to do anything? - #define ELF_PLAT_INIT(_r) */ - -static inline void init_thread(struct target_pt_regs *regs, - struct image_info *infop) -{ - regs->usp = infop->start_stack; - regs->sr = 0; - regs->pc = infop->entry; -} +#define HAVE_INIT_MAIN_THREAD /* See linux kernel: arch/m68k/include/asm/elf.h. */ #define ELF_NREG 20 diff --git a/linux-user/m68k/cpu_loop.c b/linux-user/m68k/cpu_loop.c index 23693f3358..aca0bf23dc 100644 --- a/linux-user/m68k/cpu_loop.c +++ b/linux-user/m68k/cpu_loop.c @@ -92,24 +92,11 @@ void cpu_loop(CPUM68KState *env) } } -void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) +void init_main_thread(CPUState *cs, struct image_info *info) { - env->pc = regs->pc; - env->dregs[0] = regs->d0; - env->dregs[1] = regs->d1; - env->dregs[2] = regs->d2; - env->dregs[3] = regs->d3; - env->dregs[4] = regs->d4; - env->dregs[5] = regs->d5; - env->dregs[6] = regs->d6; - env->dregs[7] = regs->d7; - env->aregs[0] = regs->a0; - env->aregs[1] = regs->a1; - env->aregs[2] = regs->a2; - env->aregs[3] = regs->a3; - env->aregs[4] = regs->a4; - env->aregs[5] = regs->a5; - env->aregs[6] = regs->a6; - env->aregs[7] = regs->usp; - env->sr = regs->sr; + CPUArchState *env = cpu_env(cs); + + env->pc = info->entry; + env->aregs[7] = info->start_stack; + env->sr = 0; } From 6b523112baac10747446af9074d1281974767004 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 28 Jul 2025 21:59:47 -1000 Subject: [PATCH 0084/2396] linux-user/alpha: Create init_main_thread Merge init_thread and target_cpu_copy_regs. There's no point going through a target_pt_regs intermediate. Note that init_thread had set ps in target_pt_regs, but target_cpu_copy_regs did not copy to env. This turns out to be ok because alpha_cpu_initfn initializes flags properly. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/alpha/cpu_loop.c | 11 ++++------- linux-user/elfload.c | 8 +------- 2 files changed, 5 insertions(+), 14 deletions(-) diff --git a/linux-user/alpha/cpu_loop.c b/linux-user/alpha/cpu_loop.c index 80ad536c5f..728b64906d 100644 --- a/linux-user/alpha/cpu_loop.c +++ b/linux-user/alpha/cpu_loop.c @@ -173,13 +173,10 @@ void cpu_loop(CPUAlphaState *env) } } -void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) +void init_main_thread(CPUState *cs, struct image_info *info) { - int i; + CPUArchState *env = cpu_env(cs); - for(i = 0; i < 28; i++) { - env->ir[i] = ((abi_ulong *)regs)[i]; - } - env->ir[IR_SP] = regs->usp; - env->pc = regs->pc; + env->pc = info->entry; + env->ir[IR_SP] = info->start_stack; } diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 46150586af..a7de852d4d 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -786,13 +786,7 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUM68KState *e #define ELF_CLASS ELFCLASS64 #define ELF_ARCH EM_ALPHA -static inline void init_thread(struct target_pt_regs *regs, - struct image_info *infop) -{ - regs->pc = infop->entry; - regs->ps = 8; - regs->usp = infop->start_stack; -} +#define HAVE_INIT_MAIN_THREAD #define ELF_EXEC_PAGESIZE 8192 From 592f36d13dd32d0a90f71266931a68f522617026 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 28 Jul 2025 22:03:03 -1000 Subject: [PATCH 0085/2396] linux-user/s390x: Create init_main_thread Merge init_thread and target_cpu_copy_regs. There's no point going through a target_pt_regs intermediate. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 9 +-------- linux-user/s390x/cpu_loop.c | 15 ++++++++------- 2 files changed, 9 insertions(+), 15 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index a7de852d4d..16aa09214e 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -798,14 +798,7 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUM68KState *e #define ELF_DATA ELFDATA2MSB #define ELF_ARCH EM_S390 -static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop) -{ - regs->psw.addr = infop->entry; - regs->psw.mask = PSW_MASK_DAT | PSW_MASK_IO | PSW_MASK_EXT | \ - PSW_MASK_MCHECK | PSW_MASK_PSTATE | PSW_MASK_64 | \ - PSW_MASK_32; - regs->gprs[15] = infop->start_stack; -} +#define HAVE_INIT_MAIN_THREAD /* See linux kernel: arch/s390/include/uapi/asm/ptrace.h (s390_regs). */ #define ELF_NREG 27 diff --git a/linux-user/s390x/cpu_loop.c b/linux-user/s390x/cpu_loop.c index c9124444ed..49e44548f8 100644 --- a/linux-user/s390x/cpu_loop.c +++ b/linux-user/s390x/cpu_loop.c @@ -180,12 +180,13 @@ void cpu_loop(CPUS390XState *env) } } -void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) +void init_main_thread(CPUState *cs, struct image_info *info) { - int i; - for (i = 0; i < 16; i++) { - env->regs[i] = regs->gprs[i]; - } - env->psw.mask = regs->psw.mask; - env->psw.addr = regs->psw.addr; + CPUArchState *env = cpu_env(cs); + + env->psw.addr = info->entry; + env->psw.mask = PSW_MASK_DAT | PSW_MASK_IO | PSW_MASK_EXT | + PSW_MASK_MCHECK | PSW_MASK_PSTATE | PSW_MASK_64 | + PSW_MASK_32; + env->regs[15] = info->start_stack; } From a56cf00bc06a11627ec7b695618532d0d58cf8de Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 28 Jul 2025 22:06:12 -1000 Subject: [PATCH 0086/2396] linux-user/riscv: Create init_main_thread Merge init_thread and target_cpu_copy_regs. There's no point going through a target_pt_regs intermediate. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 7 +------ linux-user/riscv/cpu_loop.c | 10 ++++------ 2 files changed, 5 insertions(+), 12 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 16aa09214e..556f11d720 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -849,12 +849,7 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, #define VDSO_HEADER "vdso-64.c.inc" #endif -static inline void init_thread(struct target_pt_regs *regs, - struct image_info *infop) -{ - regs->sepc = infop->entry; - regs->sp = infop->start_stack; -} +#define HAVE_INIT_MAIN_THREAD #define ELF_EXEC_PAGESIZE 4096 diff --git a/linux-user/riscv/cpu_loop.c b/linux-user/riscv/cpu_loop.c index 2dd30c7b28..b316281532 100644 --- a/linux-user/riscv/cpu_loop.c +++ b/linux-user/riscv/cpu_loop.c @@ -94,14 +94,12 @@ void cpu_loop(CPURISCVState *env) } } -void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) +void init_main_thread(CPUState *cs, struct image_info *info) { - CPUState *cpu = env_cpu(env); - TaskState *ts = get_task_state(cpu); - struct image_info *info = ts->info; + CPUArchState *env = cpu_env(cs); - env->pc = regs->sepc; - env->gpr[xSP] = regs->sp; + env->pc = info->entry; + env->gpr[xSP] = info->start_stack; env->elf_flags = info->elf_flags; if ((env->misa_ext & RVE) && !(env->elf_flags & EF_RISCV_RVE)) { From 1aec088719c5646b3117fd3b9c4507830f99f432 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 28 Jul 2025 22:09:19 -1000 Subject: [PATCH 0087/2396] linux-user/hppa: Create init_main_thread Merge init_thread and target_cpu_copy_regs. There's no point going through a target_pt_regs intermediate. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 13 +------------ linux-user/hppa/cpu_loop.c | 18 +++++++++++------- 2 files changed, 12 insertions(+), 19 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 556f11d720..4876e4b0a8 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -864,18 +864,7 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, #define VDSO_HEADER "vdso.c.inc" -static inline void init_thread(struct target_pt_regs *regs, - struct image_info *infop) -{ - regs->iaoq[0] = infop->entry | PRIV_USER; - regs->iaoq[1] = regs->iaoq[0] + 4; - regs->gr[23] = 0; - regs->gr[24] = infop->argv; - regs->gr[25] = infop->argc; - /* The top-of-stack contains a linkage buffer. */ - regs->gr[30] = infop->start_stack + 64; - regs->gr[31] = infop->entry; -} +#define HAVE_INIT_MAIN_THREAD #define LO_COMMPAGE 0 diff --git a/linux-user/hppa/cpu_loop.c b/linux-user/hppa/cpu_loop.c index 9abaad5ef8..3af50653bb 100644 --- a/linux-user/hppa/cpu_loop.c +++ b/linux-user/hppa/cpu_loop.c @@ -196,12 +196,16 @@ void cpu_loop(CPUHPPAState *env) } } -void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) +void init_main_thread(CPUState *cs, struct image_info *info) { - int i; - for (i = 1; i < 32; i++) { - env->gr[i] = regs->gr[i]; - } - env->iaoq_f = regs->iaoq[0]; - env->iaoq_b = regs->iaoq[1]; + CPUArchState *env = cpu_env(cs); + + env->iaoq_f = info->entry | PRIV_USER; + env->iaoq_b = env->iaoq_f + 4; + env->gr[23] = 0; + env->gr[24] = info->argv; + env->gr[25] = info->argc; + /* The top-of-stack contains a linkage buffer. */ + env->gr[30] = info->start_stack + 64; + env->gr[31] = info->entry; } From ceff7f9ae996ec9bb78d463f054241c338dc979c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 28 Jul 2025 22:16:22 -1000 Subject: [PATCH 0088/2396] linux-user/xtensa: Create init_main_thread Merge init_thread and target_cpu_copy_regs. There's no point going through a target_pt_regs intermediate. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 18 +----------------- linux-user/xtensa/cpu_loop.c | 22 ++++++++++++++++------ 2 files changed, 17 insertions(+), 23 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 4876e4b0a8..447a9be11d 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -905,23 +905,7 @@ static bool init_guest_commpage(void) #define ELF_CLASS ELFCLASS32 #define ELF_ARCH EM_XTENSA -static inline void init_thread(struct target_pt_regs *regs, - struct image_info *infop) -{ - regs->windowbase = 0; - regs->windowstart = 1; - regs->areg[1] = infop->start_stack; - regs->pc = infop->entry; - if (info_is_fdpic(infop)) { - regs->areg[4] = infop->loadmap_addr; - regs->areg[5] = infop->interpreter_loadmap_addr; - if (infop->interpreter_loadmap_addr) { - regs->areg[6] = infop->interpreter_pt_dynamic_addr; - } else { - regs->areg[6] = infop->pt_dynamic_addr; - } - } -} +#define HAVE_INIT_MAIN_THREAD /* See linux kernel: arch/xtensa/include/asm/elf.h. */ #define ELF_NREG 128 diff --git a/linux-user/xtensa/cpu_loop.c b/linux-user/xtensa/cpu_loop.c index c0fcf743e7..43a194fc4a 100644 --- a/linux-user/xtensa/cpu_loop.c +++ b/linux-user/xtensa/cpu_loop.c @@ -238,12 +238,22 @@ void cpu_loop(CPUXtensaState *env) } } -void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) +void init_main_thread(CPUState *cs, struct image_info *info) { - int i; - for (i = 0; i < 16; ++i) { - env->regs[i] = regs->areg[i]; + CPUArchState *env = cpu_env(cs); + + env->sregs[WINDOW_BASE] = 0; + env->sregs[WINDOW_START] = 1; + env->regs[1] = info->start_stack; + env->pc = info->entry; + + if (info_is_fdpic(info)) { + env->regs[4] = info->loadmap_addr; + env->regs[5] = info->interpreter_loadmap_addr; + if (info->interpreter_loadmap_addr) { + env->regs[6] = info->interpreter_pt_dynamic_addr; + } else { + env->regs[6] = info->pt_dynamic_addr; + } } - env->sregs[WINDOW_START] = regs->windowstart; - env->pc = regs->pc; } From f61c88c571e5e68cdf1d935d7524493a8f404556 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 28 Jul 2025 22:19:49 -1000 Subject: [PATCH 0089/2396] linux-user/hexagon: Create init_main_thread Merge init_thread and target_cpu_copy_regs. There's no point going through a target_pt_regs intermediate. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 7 +------ linux-user/hexagon/cpu_loop.c | 8 +++++--- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 447a9be11d..4417c2d99a 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -954,12 +954,7 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, #define ELF_CLASS ELFCLASS32 #define ELF_ARCH EM_HEXAGON -static inline void init_thread(struct target_pt_regs *regs, - struct image_info *infop) -{ - regs->sepc = infop->entry; - regs->sp = infop->start_stack; -} +#define HAVE_INIT_MAIN_THREAD #endif /* TARGET_HEXAGON */ diff --git a/linux-user/hexagon/cpu_loop.c b/linux-user/hexagon/cpu_loop.c index e18a0183b5..25c97edcae 100644 --- a/linux-user/hexagon/cpu_loop.c +++ b/linux-user/hexagon/cpu_loop.c @@ -79,9 +79,11 @@ void cpu_loop(CPUHexagonState *env) } } -void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs) +void init_main_thread(CPUState *cs, struct image_info *info) { - env->gpr[HEX_REG_PC] = regs->sepc; - env->gpr[HEX_REG_SP] = regs->sp; + CPUArchState *env = cpu_env(cs); + + env->gpr[HEX_REG_PC] = info->entry; + env->gpr[HEX_REG_SP] = info->start_stack; env->gpr[HEX_REG_USR] = 0x56000; } From e191623fb0694a08c372fb68c5634969addbcb7b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 28 Jul 2025 22:24:19 -1000 Subject: [PATCH 0090/2396] linux-user: Remove do_init_main_thread All targets have been converted, so we can call init_main_thread directly. Remove do_init_main_thread and HAVE_INIT_MAIN_THREAD. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 46 -------------------------------------------- linux-user/loader.h | 1 - linux-user/main.c | 2 +- linux-user/qemu.h | 2 -- 4 files changed, 1 insertion(+), 50 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 4417c2d99a..fce4c05674 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -149,8 +149,6 @@ typedef abi_int target_pid_t; #ifdef TARGET_I386 -#define HAVE_INIT_MAIN_THREAD - #ifdef TARGET_X86_64 #define ELF_CLASS ELFCLASS64 #define ELF_ARCH EM_X86_64 @@ -293,8 +291,6 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUX86State *en #define ELF_CLASS ELFCLASS32 #define EXSTACK_DEFAULT true -#define HAVE_INIT_MAIN_THREAD - #define ELF_NREG 18 typedef target_elf_greg_t target_elf_gregset_t[ELF_NREG]; @@ -395,8 +391,6 @@ static const VdsoImageInfo *vdso_image_info(uint32_t elf_flags) #define ELF_ARCH EM_AARCH64 #define ELF_CLASS ELFCLASS64 -#define HAVE_INIT_MAIN_THREAD - #define ELF_NREG 34 typedef target_elf_greg_t target_elf_gregset_t[ELF_NREG]; @@ -438,8 +432,6 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, # define ELF_ARCH EM_SPARCV9 #endif -#define HAVE_INIT_MAIN_THREAD - #endif /* TARGET_SPARC */ #ifdef TARGET_PPC @@ -485,8 +477,6 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, NEW_AUX_ENT(AT_UCACHEBSIZE, 0); \ } while (0) -#define HAVE_INIT_MAIN_THREAD - /* See linux kernel: arch/powerpc/include/asm/elf.h. */ #define ELF_NREG 48 typedef target_elf_greg_t target_elf_gregset_t[ELF_NREG]; @@ -533,8 +523,6 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUPPCState *en #define VDSO_HEADER "vdso.c.inc" -#define HAVE_INIT_MAIN_THREAD - /* See linux kernel: arch/loongarch/include/asm/elf.h */ #define ELF_NREG 45 typedef target_elf_greg_t target_elf_gregset_t[ELF_NREG]; @@ -581,8 +569,6 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, #define elf_check_abi(x) (!((x) & EF_MIPS_ABI2)) #endif -#define HAVE_INIT_MAIN_THREAD - /* See linux kernel: arch/mips/include/asm/elf.h. */ #define ELF_NREG 45 typedef target_elf_greg_t target_elf_gregset_t[ELF_NREG]; @@ -640,8 +626,6 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUMIPSState *e #define ELF_CLASS ELFCLASS32 #define ELF_ARCH EM_MICROBLAZE -#define HAVE_INIT_MAIN_THREAD - #define ELF_EXEC_PAGESIZE 4096 #define USE_ELF_CORE_DUMP @@ -673,8 +657,6 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUMBState *env #define ELF_CLASS ELFCLASS32 #define ELF_DATA ELFDATA2MSB -#define HAVE_INIT_MAIN_THREAD - #define USE_ELF_CORE_DUMP #define ELF_EXEC_PAGESIZE 8192 @@ -701,8 +683,6 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, #define ELF_CLASS ELFCLASS32 #define ELF_ARCH EM_SH -#define HAVE_INIT_MAIN_THREAD - /* See linux kernel: arch/sh/include/asm/elf.h. */ #define ELF_NREG 23 typedef target_elf_greg_t target_elf_gregset_t[ELF_NREG]; @@ -746,8 +726,6 @@ static inline void elf_core_copy_regs(target_elf_gregset_t *regs, #define ELF_CLASS ELFCLASS32 #define ELF_ARCH EM_68K -#define HAVE_INIT_MAIN_THREAD - /* See linux kernel: arch/m68k/include/asm/elf.h. */ #define ELF_NREG 20 typedef target_elf_greg_t target_elf_gregset_t[ELF_NREG]; @@ -786,8 +764,6 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUM68KState *e #define ELF_CLASS ELFCLASS64 #define ELF_ARCH EM_ALPHA -#define HAVE_INIT_MAIN_THREAD - #define ELF_EXEC_PAGESIZE 8192 #endif /* TARGET_ALPHA */ @@ -798,8 +774,6 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUM68KState *e #define ELF_DATA ELFDATA2MSB #define ELF_ARCH EM_S390 -#define HAVE_INIT_MAIN_THREAD - /* See linux kernel: arch/s390/include/uapi/asm/ptrace.h (s390_regs). */ #define ELF_NREG 27 typedef target_elf_greg_t target_elf_gregset_t[ELF_NREG]; @@ -849,8 +823,6 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, #define VDSO_HEADER "vdso-64.c.inc" #endif -#define HAVE_INIT_MAIN_THREAD - #define ELF_EXEC_PAGESIZE 4096 #endif /* TARGET_RISCV */ @@ -864,8 +836,6 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, #define VDSO_HEADER "vdso.c.inc" -#define HAVE_INIT_MAIN_THREAD - #define LO_COMMPAGE 0 static bool init_guest_commpage(void) @@ -905,8 +875,6 @@ static bool init_guest_commpage(void) #define ELF_CLASS ELFCLASS32 #define ELF_ARCH EM_XTENSA -#define HAVE_INIT_MAIN_THREAD - /* See linux kernel: arch/xtensa/include/asm/elf.h. */ #define ELF_NREG 128 typedef target_elf_greg_t target_elf_gregset_t[ELF_NREG]; @@ -954,8 +922,6 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, #define ELF_CLASS ELFCLASS32 #define ELF_ARCH EM_HEXAGON -#define HAVE_INIT_MAIN_THREAD - #endif /* TARGET_HEXAGON */ #ifndef ELF_MACHINE @@ -3438,15 +3404,3 @@ static int elf_core_dump(int signr, const CPUArchState *env) return ret; } #endif /* USE_ELF_CORE_DUMP */ - -void do_init_main_thread(CPUState *cs, struct image_info *infop) -{ -#ifdef HAVE_INIT_MAIN_THREAD - init_main_thread(cs, infop); -#else - target_pt_regs regs = { }; - - init_thread(®s, infop); - target_cpu_copy_regs(cpu_env(cs), ®s); -#endif -} diff --git a/linux-user/loader.h b/linux-user/loader.h index e0291cc3b0..6482c7c90c 100644 --- a/linux-user/loader.h +++ b/linux-user/loader.h @@ -82,7 +82,6 @@ struct linux_binprm { int (*core_dump)(int, const CPUArchState *); /* coredump routine */ }; -void do_init_main_thread(CPUState *cs, struct image_info *infop); abi_ulong loader_build_argptr(int envc, int argc, abi_ulong sp, abi_ulong stringp, int push_ptr); int loader_exec(int fdexec, const char *filename, char **argv, char **envp, diff --git a/linux-user/main.c b/linux-user/main.c index e21842bde9..6edeeecef3 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -1037,7 +1037,7 @@ int main(int argc, char **argv, char **envp) the real value of GUEST_BASE into account. */ tcg_prologue_init(); - do_init_main_thread(cpu, info); + init_main_thread(cpu, info); if (gdbstub) { gdbserver_start(gdbstub, &error_fatal); diff --git a/linux-user/qemu.h b/linux-user/qemu.h index 8a9500d4f4..e4dca0c20f 100644 --- a/linux-user/qemu.h +++ b/linux-user/qemu.h @@ -359,8 +359,6 @@ void *lock_user_string(abi_ulong guest_addr); /* Clone cpu state */ CPUArchState *cpu_copy(CPUArchState *env); -typedef struct target_pt_regs target_pt_regs; -void target_cpu_copy_regs(CPUArchState *env, target_pt_regs *regs); void init_main_thread(CPUState *cs, struct image_info *info); #endif /* QEMU_H */ From f91563d011a0439cd6709e169cdfac268779d562 Mon Sep 17 00:00:00 2001 From: Joel Stanley Date: Tue, 26 Aug 2025 15:33:40 +0930 Subject: [PATCH 0091/2396] linux-user: Add strace for rseq build/qemu-riscv64 -cpu rv64,v=on -d strace build/tests/tcg/riscv64-linux-user/test-vstart-overflow 1118081 riscv_hwprobe(0xffffbc038200,1,0,0,0,0) = 0 1118081 brk(NULL) = 0x0000000000085000 1118081 brk(0x0000000000085b00) = 0x0000000000085b00 1118081 set_tid_address(0x850f0) = 1118081 1118081 set_robust_list(0x85100,24) = -1 errno=38 (Function not implemented) 1118081 rseq(0x857c0,32,0,0xf1401073) = -1 errno=38 (Function not implemented) Signed-off-by: Joel Stanley Signed-off-by: Richard Henderson Reviewed-by: Richard Henderson Message-ID: <20250826060341.1118670-1-joel@jms.id.au> --- linux-user/strace.list | 3 +++ 1 file changed, 3 insertions(+) diff --git a/linux-user/strace.list b/linux-user/strace.list index ab818352a9..51b5ead969 100644 --- a/linux-user/strace.list +++ b/linux-user/strace.list @@ -1719,3 +1719,6 @@ #ifdef TARGET_NR_riscv_hwprobe { TARGET_NR_riscv_hwprobe, "riscv_hwprobe" , "%s(%p,%d,%d,%d,%d,%d)", NULL, NULL }, #endif +#ifdef TARGET_NR_rseq +{ TARGET_NR_rseq, "rseq" , "%s(%p,%u,%d,%#x)", NULL, NULL }, +#endif From a5fbf1c617c5b51082d317601e0d4cf5eea5c140 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Stelmach?= Date: Wed, 27 Aug 2025 11:54:12 +0200 Subject: [PATCH 0092/2396] linux-user: do not print IP socket options by default MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit IP protocols should not be printed unless the socket is an IPv4 or IPv6 one. Current arrangement erroneously prints IPPROTO_IP for Unix domain sockets. Signed-off-by: Łukasz Stelmach Signed-off-by: Richard Henderson Reviewed-by: Richard Henderson Message-ID: <20250827095412.2348821-1-l.stelmach@samsung.com> --- linux-user/strace.c | 36 ++++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/linux-user/strace.c b/linux-user/strace.c index 3b744ccd4a..786354627a 100644 --- a/linux-user/strace.c +++ b/linux-user/strace.c @@ -586,23 +586,27 @@ print_socket_protocol(int domain, int type, int protocol) return; } - switch (protocol) { - case IPPROTO_IP: - qemu_log("IPPROTO_IP"); - break; - case IPPROTO_TCP: - qemu_log("IPPROTO_TCP"); - break; - case IPPROTO_UDP: - qemu_log("IPPROTO_UDP"); - break; - case IPPROTO_RAW: - qemu_log("IPPROTO_RAW"); - break; - default: - qemu_log("%d", protocol); - break; + if (domain == AF_INET || domain == AF_INET6) { + switch (protocol) { + case IPPROTO_IP: + qemu_log("IPPROTO_IP"); + break; + case IPPROTO_TCP: + qemu_log("IPPROTO_TCP"); + break; + case IPPROTO_UDP: + qemu_log("IPPROTO_UDP"); + break; + case IPPROTO_RAW: + qemu_log("IPPROTO_RAW"); + break; + default: + qemu_log("%d", protocol); + break; + } + return; } + qemu_log("%d", protocol); } From 96e7448c1f820c56caea8447c01f5227b0c95c79 Mon Sep 17 00:00:00 2001 From: WANG Rui Date: Fri, 25 Jul 2025 11:12:32 +0800 Subject: [PATCH 0093/2396] target/loongarch: Guard 64-bit-only insn translation with TRANS64 macro This patch replaces uses of the generic TRANS macro with TRANS64 for instructions that are only valid when 64-bit support is available. This improves correctness and avoids potential assertion failures or undefined behavior during translation on 32-bit-only configurations. Signed-off-by: WANG Rui Reviewed-by: Bibo Mao Reviewed-by: Song Gao Signed-off-by: Song Gao --- .../tcg/insn_trans/trans_atomic.c.inc | 36 +++++++++---------- .../tcg/insn_trans/trans_extra.c.inc | 8 +++-- .../tcg/insn_trans/trans_farith.c.inc | 8 ++--- .../loongarch/tcg/insn_trans/trans_fcnv.c.inc | 4 +-- .../tcg/insn_trans/trans_fmemory.c.inc | 16 ++++----- .../tcg/insn_trans/trans_privileged.c.inc | 4 +-- .../tcg/insn_trans/trans_shift.c.inc | 4 +-- target/loongarch/translate.h | 4 +++ 8 files changed, 46 insertions(+), 38 deletions(-) diff --git a/target/loongarch/tcg/insn_trans/trans_atomic.c.inc b/target/loongarch/tcg/insn_trans/trans_atomic.c.inc index 3d70d75941..77eeedbc42 100644 --- a/target/loongarch/tcg/insn_trans/trans_atomic.c.inc +++ b/target/loongarch/tcg/insn_trans/trans_atomic.c.inc @@ -74,38 +74,38 @@ TRANS(sc_w, ALL, gen_sc, MO_TESL) TRANS(ll_d, 64, gen_ll, MO_TEUQ) TRANS(sc_d, 64, gen_sc, MO_TEUQ) TRANS(amswap_w, LAM, gen_am, tcg_gen_atomic_xchg_tl, MO_TESL) -TRANS(amswap_d, LAM, gen_am, tcg_gen_atomic_xchg_tl, MO_TEUQ) +TRANS64(amswap_d, LAM, gen_am, tcg_gen_atomic_xchg_tl, MO_TEUQ) TRANS(amadd_w, LAM, gen_am, tcg_gen_atomic_fetch_add_tl, MO_TESL) -TRANS(amadd_d, LAM, gen_am, tcg_gen_atomic_fetch_add_tl, MO_TEUQ) +TRANS64(amadd_d, LAM, gen_am, tcg_gen_atomic_fetch_add_tl, MO_TEUQ) TRANS(amand_w, LAM, gen_am, tcg_gen_atomic_fetch_and_tl, MO_TESL) -TRANS(amand_d, LAM, gen_am, tcg_gen_atomic_fetch_and_tl, MO_TEUQ) +TRANS64(amand_d, LAM, gen_am, tcg_gen_atomic_fetch_and_tl, MO_TEUQ) TRANS(amor_w, LAM, gen_am, tcg_gen_atomic_fetch_or_tl, MO_TESL) -TRANS(amor_d, LAM, gen_am, tcg_gen_atomic_fetch_or_tl, MO_TEUQ) +TRANS64(amor_d, LAM, gen_am, tcg_gen_atomic_fetch_or_tl, MO_TEUQ) TRANS(amxor_w, LAM, gen_am, tcg_gen_atomic_fetch_xor_tl, MO_TESL) -TRANS(amxor_d, LAM, gen_am, tcg_gen_atomic_fetch_xor_tl, MO_TEUQ) +TRANS64(amxor_d, LAM, gen_am, tcg_gen_atomic_fetch_xor_tl, MO_TEUQ) TRANS(ammax_w, LAM, gen_am, tcg_gen_atomic_fetch_smax_tl, MO_TESL) -TRANS(ammax_d, LAM, gen_am, tcg_gen_atomic_fetch_smax_tl, MO_TEUQ) +TRANS64(ammax_d, LAM, gen_am, tcg_gen_atomic_fetch_smax_tl, MO_TEUQ) TRANS(ammin_w, LAM, gen_am, tcg_gen_atomic_fetch_smin_tl, MO_TESL) -TRANS(ammin_d, LAM, gen_am, tcg_gen_atomic_fetch_smin_tl, MO_TEUQ) +TRANS64(ammin_d, LAM, gen_am, tcg_gen_atomic_fetch_smin_tl, MO_TEUQ) TRANS(ammax_wu, LAM, gen_am, tcg_gen_atomic_fetch_umax_tl, MO_TESL) -TRANS(ammax_du, LAM, gen_am, tcg_gen_atomic_fetch_umax_tl, MO_TEUQ) +TRANS64(ammax_du, LAM, gen_am, tcg_gen_atomic_fetch_umax_tl, MO_TEUQ) TRANS(ammin_wu, LAM, gen_am, tcg_gen_atomic_fetch_umin_tl, MO_TESL) -TRANS(ammin_du, LAM, gen_am, tcg_gen_atomic_fetch_umin_tl, MO_TEUQ) +TRANS64(ammin_du, LAM, gen_am, tcg_gen_atomic_fetch_umin_tl, MO_TEUQ) TRANS(amswap_db_w, LAM, gen_am, tcg_gen_atomic_xchg_tl, MO_TESL) -TRANS(amswap_db_d, LAM, gen_am, tcg_gen_atomic_xchg_tl, MO_TEUQ) +TRANS64(amswap_db_d, LAM, gen_am, tcg_gen_atomic_xchg_tl, MO_TEUQ) TRANS(amadd_db_w, LAM, gen_am, tcg_gen_atomic_fetch_add_tl, MO_TESL) -TRANS(amadd_db_d, LAM, gen_am, tcg_gen_atomic_fetch_add_tl, MO_TEUQ) +TRANS64(amadd_db_d, LAM, gen_am, tcg_gen_atomic_fetch_add_tl, MO_TEUQ) TRANS(amand_db_w, LAM, gen_am, tcg_gen_atomic_fetch_and_tl, MO_TESL) -TRANS(amand_db_d, LAM, gen_am, tcg_gen_atomic_fetch_and_tl, MO_TEUQ) +TRANS64(amand_db_d, LAM, gen_am, tcg_gen_atomic_fetch_and_tl, MO_TEUQ) TRANS(amor_db_w, LAM, gen_am, tcg_gen_atomic_fetch_or_tl, MO_TESL) -TRANS(amor_db_d, LAM, gen_am, tcg_gen_atomic_fetch_or_tl, MO_TEUQ) +TRANS64(amor_db_d, LAM, gen_am, tcg_gen_atomic_fetch_or_tl, MO_TEUQ) TRANS(amxor_db_w, LAM, gen_am, tcg_gen_atomic_fetch_xor_tl, MO_TESL) -TRANS(amxor_db_d, LAM, gen_am, tcg_gen_atomic_fetch_xor_tl, MO_TEUQ) +TRANS64(amxor_db_d, LAM, gen_am, tcg_gen_atomic_fetch_xor_tl, MO_TEUQ) TRANS(ammax_db_w, LAM, gen_am, tcg_gen_atomic_fetch_smax_tl, MO_TESL) -TRANS(ammax_db_d, LAM, gen_am, tcg_gen_atomic_fetch_smax_tl, MO_TEUQ) +TRANS64(ammax_db_d, LAM, gen_am, tcg_gen_atomic_fetch_smax_tl, MO_TEUQ) TRANS(ammin_db_w, LAM, gen_am, tcg_gen_atomic_fetch_smin_tl, MO_TESL) -TRANS(ammin_db_d, LAM, gen_am, tcg_gen_atomic_fetch_smin_tl, MO_TEUQ) +TRANS64(ammin_db_d, LAM, gen_am, tcg_gen_atomic_fetch_smin_tl, MO_TEUQ) TRANS(ammax_db_wu, LAM, gen_am, tcg_gen_atomic_fetch_umax_tl, MO_TESL) -TRANS(ammax_db_du, LAM, gen_am, tcg_gen_atomic_fetch_umax_tl, MO_TEUQ) +TRANS64(ammax_db_du, LAM, gen_am, tcg_gen_atomic_fetch_umax_tl, MO_TEUQ) TRANS(ammin_db_wu, LAM, gen_am, tcg_gen_atomic_fetch_umin_tl, MO_TESL) -TRANS(ammin_db_du, LAM, gen_am, tcg_gen_atomic_fetch_umin_tl, MO_TEUQ) +TRANS64(ammin_db_du, LAM, gen_am, tcg_gen_atomic_fetch_umin_tl, MO_TEUQ) diff --git a/target/loongarch/tcg/insn_trans/trans_extra.c.inc b/target/loongarch/tcg/insn_trans/trans_extra.c.inc index eda3d6e561..298a80cff5 100644 --- a/target/loongarch/tcg/insn_trans/trans_extra.c.inc +++ b/target/loongarch/tcg/insn_trans/trans_extra.c.inc @@ -69,6 +69,10 @@ static bool trans_rdtimeh_w(DisasContext *ctx, arg_rdtimeh_w *a) static bool trans_rdtime_d(DisasContext *ctx, arg_rdtime_d *a) { + if (!avail_64(ctx)) { + return false; + } + return gen_rdtime(ctx, a, 0, 0); } @@ -100,8 +104,8 @@ static bool gen_crc(DisasContext *ctx, arg_rrr *a, TRANS(crc_w_b_w, CRC, gen_crc, gen_helper_crc32, tcg_constant_tl(1)) TRANS(crc_w_h_w, CRC, gen_crc, gen_helper_crc32, tcg_constant_tl(2)) TRANS(crc_w_w_w, CRC, gen_crc, gen_helper_crc32, tcg_constant_tl(4)) -TRANS(crc_w_d_w, CRC, gen_crc, gen_helper_crc32, tcg_constant_tl(8)) +TRANS64(crc_w_d_w, CRC, gen_crc, gen_helper_crc32, tcg_constant_tl(8)) TRANS(crcc_w_b_w, CRC, gen_crc, gen_helper_crc32c, tcg_constant_tl(1)) TRANS(crcc_w_h_w, CRC, gen_crc, gen_helper_crc32c, tcg_constant_tl(2)) TRANS(crcc_w_w_w, CRC, gen_crc, gen_helper_crc32c, tcg_constant_tl(4)) -TRANS(crcc_w_d_w, CRC, gen_crc, gen_helper_crc32c, tcg_constant_tl(8)) +TRANS64(crcc_w_d_w, CRC, gen_crc, gen_helper_crc32c, tcg_constant_tl(8)) diff --git a/target/loongarch/tcg/insn_trans/trans_farith.c.inc b/target/loongarch/tcg/insn_trans/trans_farith.c.inc index f4a0dea727..ff6cf3448e 100644 --- a/target/loongarch/tcg/insn_trans/trans_farith.c.inc +++ b/target/loongarch/tcg/insn_trans/trans_farith.c.inc @@ -183,16 +183,16 @@ TRANS(fmaxa_s, FP_SP, gen_fff, gen_helper_fmaxa_s) TRANS(fmaxa_d, FP_DP, gen_fff, gen_helper_fmaxa_d) TRANS(fmina_s, FP_SP, gen_fff, gen_helper_fmina_s) TRANS(fmina_d, FP_DP, gen_fff, gen_helper_fmina_d) -TRANS(fscaleb_s, FP_SP, gen_fff, gen_helper_fscaleb_s) -TRANS(fscaleb_d, FP_DP, gen_fff, gen_helper_fscaleb_d) +TRANS64(fscaleb_s, FP_SP, gen_fff, gen_helper_fscaleb_s) +TRANS64(fscaleb_d, FP_DP, gen_fff, gen_helper_fscaleb_d) TRANS(fsqrt_s, FP_SP, gen_ff, gen_helper_fsqrt_s) TRANS(fsqrt_d, FP_DP, gen_ff, gen_helper_fsqrt_d) TRANS(frecip_s, FP_SP, gen_ff, gen_helper_frecip_s) TRANS(frecip_d, FP_DP, gen_ff, gen_helper_frecip_d) TRANS(frsqrt_s, FP_SP, gen_ff, gen_helper_frsqrt_s) TRANS(frsqrt_d, FP_DP, gen_ff, gen_helper_frsqrt_d) -TRANS(flogb_s, FP_SP, gen_ff, gen_helper_flogb_s) -TRANS(flogb_d, FP_DP, gen_ff, gen_helper_flogb_d) +TRANS64(flogb_s, FP_SP, gen_ff, gen_helper_flogb_s) +TRANS64(flogb_d, FP_DP, gen_ff, gen_helper_flogb_d) TRANS(fclass_s, FP_SP, gen_ff, gen_helper_fclass_s) TRANS(fclass_d, FP_DP, gen_ff, gen_helper_fclass_d) TRANS(fmadd_s, FP_SP, gen_muladd, gen_helper_fmuladd_s, 0) diff --git a/target/loongarch/tcg/insn_trans/trans_fcnv.c.inc b/target/loongarch/tcg/insn_trans/trans_fcnv.c.inc index 833c059d6d..ca1d76a366 100644 --- a/target/loongarch/tcg/insn_trans/trans_fcnv.c.inc +++ b/target/loongarch/tcg/insn_trans/trans_fcnv.c.inc @@ -29,5 +29,5 @@ TRANS(ffint_s_w, FP_SP, gen_ff, gen_helper_ffint_s_w) TRANS(ffint_s_l, FP_SP, gen_ff, gen_helper_ffint_s_l) TRANS(ffint_d_w, FP_DP, gen_ff, gen_helper_ffint_d_w) TRANS(ffint_d_l, FP_DP, gen_ff, gen_helper_ffint_d_l) -TRANS(frint_s, FP_SP, gen_ff, gen_helper_frint_s) -TRANS(frint_d, FP_DP, gen_ff, gen_helper_frint_d) +TRANS64(frint_s, FP_SP, gen_ff, gen_helper_frint_s) +TRANS64(frint_d, FP_DP, gen_ff, gen_helper_frint_d) diff --git a/target/loongarch/tcg/insn_trans/trans_fmemory.c.inc b/target/loongarch/tcg/insn_trans/trans_fmemory.c.inc index 13452bc7e5..79da4718a5 100644 --- a/target/loongarch/tcg/insn_trans/trans_fmemory.c.inc +++ b/target/loongarch/tcg/insn_trans/trans_fmemory.c.inc @@ -148,11 +148,11 @@ TRANS(fldx_s, FP_SP, gen_floadx, MO_TEUL) TRANS(fldx_d, FP_DP, gen_floadx, MO_TEUQ) TRANS(fstx_s, FP_SP, gen_fstorex, MO_TEUL) TRANS(fstx_d, FP_DP, gen_fstorex, MO_TEUQ) -TRANS(fldgt_s, FP_SP, gen_fload_gt, MO_TEUL) -TRANS(fldgt_d, FP_DP, gen_fload_gt, MO_TEUQ) -TRANS(fldle_s, FP_SP, gen_fload_le, MO_TEUL) -TRANS(fldle_d, FP_DP, gen_fload_le, MO_TEUQ) -TRANS(fstgt_s, FP_SP, gen_fstore_gt, MO_TEUL) -TRANS(fstgt_d, FP_DP, gen_fstore_gt, MO_TEUQ) -TRANS(fstle_s, FP_SP, gen_fstore_le, MO_TEUL) -TRANS(fstle_d, FP_DP, gen_fstore_le, MO_TEUQ) +TRANS64(fldgt_s, FP_SP, gen_fload_gt, MO_TEUL) +TRANS64(fldgt_d, FP_DP, gen_fload_gt, MO_TEUQ) +TRANS64(fldle_s, FP_SP, gen_fload_le, MO_TEUL) +TRANS64(fldle_d, FP_DP, gen_fload_le, MO_TEUQ) +TRANS64(fstgt_s, FP_SP, gen_fstore_gt, MO_TEUL) +TRANS64(fstgt_d, FP_DP, gen_fstore_gt, MO_TEUQ) +TRANS64(fstle_s, FP_SP, gen_fstore_le, MO_TEUL) +TRANS64(fstle_d, FP_DP, gen_fstore_le, MO_TEUQ) diff --git a/target/loongarch/tcg/insn_trans/trans_privileged.c.inc b/target/loongarch/tcg/insn_trans/trans_privileged.c.inc index ecbfe23b63..34cfab8879 100644 --- a/target/loongarch/tcg/insn_trans/trans_privileged.c.inc +++ b/target/loongarch/tcg/insn_trans/trans_privileged.c.inc @@ -233,11 +233,11 @@ static bool gen_iocsrwr(DisasContext *ctx, arg_rr *a, TRANS(iocsrrd_b, IOCSR, gen_iocsrrd, gen_helper_iocsrrd_b) TRANS(iocsrrd_h, IOCSR, gen_iocsrrd, gen_helper_iocsrrd_h) TRANS(iocsrrd_w, IOCSR, gen_iocsrrd, gen_helper_iocsrrd_w) -TRANS(iocsrrd_d, IOCSR, gen_iocsrrd, gen_helper_iocsrrd_d) +TRANS64(iocsrrd_d, IOCSR, gen_iocsrrd, gen_helper_iocsrrd_d) TRANS(iocsrwr_b, IOCSR, gen_iocsrwr, gen_helper_iocsrwr_b) TRANS(iocsrwr_h, IOCSR, gen_iocsrwr, gen_helper_iocsrwr_h) TRANS(iocsrwr_w, IOCSR, gen_iocsrwr, gen_helper_iocsrwr_w) -TRANS(iocsrwr_d, IOCSR, gen_iocsrwr, gen_helper_iocsrwr_d) +TRANS64(iocsrwr_d, IOCSR, gen_iocsrwr, gen_helper_iocsrwr_d) static void check_mmu_idx(DisasContext *ctx) { diff --git a/target/loongarch/tcg/insn_trans/trans_shift.c.inc b/target/loongarch/tcg/insn_trans/trans_shift.c.inc index 377307785a..136c4c8455 100644 --- a/target/loongarch/tcg/insn_trans/trans_shift.c.inc +++ b/target/loongarch/tcg/insn_trans/trans_shift.c.inc @@ -78,7 +78,7 @@ TRANS(sra_w, ALL, gen_rrr, EXT_SIGN, EXT_NONE, EXT_SIGN, gen_sra_w) TRANS(sll_d, 64, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, gen_sll_d) TRANS(srl_d, 64, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, gen_srl_d) TRANS(sra_d, 64, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, gen_sra_d) -TRANS(rotr_w, 64, gen_rrr, EXT_ZERO, EXT_NONE, EXT_SIGN, gen_rotr_w) +TRANS(rotr_w, ALL, gen_rrr, EXT_ZERO, EXT_NONE, EXT_SIGN, gen_rotr_w) TRANS(rotr_d, 64, gen_rrr, EXT_NONE, EXT_NONE, EXT_NONE, gen_rotr_d) TRANS(slli_w, ALL, gen_rri_c, EXT_NONE, EXT_SIGN, tcg_gen_shli_tl) TRANS(slli_d, 64, gen_rri_c, EXT_NONE, EXT_NONE, tcg_gen_shli_tl) @@ -86,5 +86,5 @@ TRANS(srli_w, ALL, gen_rri_c, EXT_ZERO, EXT_SIGN, tcg_gen_shri_tl) TRANS(srli_d, 64, gen_rri_c, EXT_NONE, EXT_NONE, tcg_gen_shri_tl) TRANS(srai_w, ALL, gen_rri_c, EXT_NONE, EXT_NONE, gen_sari_w) TRANS(srai_d, 64, gen_rri_c, EXT_NONE, EXT_NONE, tcg_gen_sari_tl) -TRANS(rotri_w, 64, gen_rri_v, EXT_NONE, EXT_NONE, gen_rotr_w) +TRANS(rotri_w, ALL, gen_rri_v, EXT_NONE, EXT_NONE, gen_rotr_w) TRANS(rotri_d, 64, gen_rri_c, EXT_NONE, EXT_NONE, tcg_gen_rotri_tl) diff --git a/target/loongarch/translate.h b/target/loongarch/translate.h index 018dc5eb17..bbe015ba57 100644 --- a/target/loongarch/translate.h +++ b/target/loongarch/translate.h @@ -14,6 +14,10 @@ static bool trans_##NAME(DisasContext *ctx, arg_##NAME * a) \ { return avail_##AVAIL(ctx) && FUNC(ctx, a, __VA_ARGS__); } +#define TRANS64(NAME, AVAIL, FUNC, ...) \ + static bool trans_##NAME(DisasContext *ctx, arg_##NAME * a) \ + { return avail_64(ctx) && avail_##AVAIL(ctx) && FUNC(ctx, a, __VA_ARGS__); } + #define avail_ALL(C) true #define avail_64(C) (FIELD_EX32((C)->cpucfg1, CPUCFG1, ARCH) == \ CPUCFG1_ARCH_LA64) From 86bca40402316891b8b9a920c2e3bf8cf37ba9a4 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Fri, 1 Aug 2025 08:01:52 +0200 Subject: [PATCH 0094/2396] hw/intc/loongarch_pch_pic: Fix ubsan warning and endianness issue MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When booting the Linux kernel from tests/functional/test_loongarch64_virt.py with a QEMU that has been compiled with --enable-ubsan, there is a warning like this: .../hw/intc/loongarch_pch_pic.c:171:46: runtime error: index 512 out of bounds for type 'uint8_t[64]' (aka 'unsigned char[64]') SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior .../hw/intc/loongarch_pch_pic.c:171:46 .../hw/intc/loongarch_pch_pic.c:175:45: runtime error: index 256 out of bounds for type 'uint8_t[64]' (aka 'unsigned char[64]') SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior .../hw/intc/loongarch_pch_pic.c:175:45 It happens because "addr" is added first before substracting the base (PCH_PIC_HTMSI_VEC or PCH_PIC_ROUTE_ENTRY). Additionally, this code looks like it is not endianness safe, since it uses a 64-bit pointer to write values into an array of 8-bit values. Thus rework the code to use the stq_le_p / ldq_le_p helpers here and make sure that we do not create pointers with undefined behavior by accident. Signed-off-by: Thomas Huth Reviewed-by: Bibo Mao Reviewed-by: Philippe Mathieu-Daudé Tested-by: Song Gao Signed-off-by: Song Gao --- hw/intc/loongarch_pch_pic.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/hw/intc/loongarch_pch_pic.c b/hw/intc/loongarch_pch_pic.c index c4b242dbf4..32f01aabf0 100644 --- a/hw/intc/loongarch_pch_pic.c +++ b/hw/intc/loongarch_pch_pic.c @@ -110,10 +110,10 @@ static uint64_t pch_pic_read(void *opaque, hwaddr addr, uint64_t field_mask) val = s->int_polarity; break; case PCH_PIC_HTMSI_VEC ... PCH_PIC_HTMSI_VEC_END: - val = *(uint64_t *)(s->htmsi_vector + addr - PCH_PIC_HTMSI_VEC); + val = ldq_le_p(&s->htmsi_vector[addr - PCH_PIC_HTMSI_VEC]); break; case PCH_PIC_ROUTE_ENTRY ... PCH_PIC_ROUTE_ENTRY_END: - val = *(uint64_t *)(s->route_entry + addr - PCH_PIC_ROUTE_ENTRY); + val = ldq_le_p(&s->route_entry[addr - PCH_PIC_ROUTE_ENTRY]); break; default: qemu_log_mask(LOG_GUEST_ERROR, @@ -129,7 +129,8 @@ static void pch_pic_write(void *opaque, hwaddr addr, uint64_t value, { LoongArchPICCommonState *s = LOONGARCH_PIC_COMMON(opaque); uint32_t offset; - uint64_t old, mask, data, *ptemp; + uint64_t old, mask, data; + void *ptemp; offset = addr & 7; addr -= offset; @@ -168,12 +169,12 @@ static void pch_pic_write(void *opaque, hwaddr addr, uint64_t value, s->int_polarity = (s->int_polarity & ~mask) | data; break; case PCH_PIC_HTMSI_VEC ... PCH_PIC_HTMSI_VEC_END: - ptemp = (uint64_t *)(s->htmsi_vector + addr - PCH_PIC_HTMSI_VEC); - *ptemp = (*ptemp & ~mask) | data; + ptemp = &s->htmsi_vector[addr - PCH_PIC_HTMSI_VEC]; + stq_le_p(ptemp, (ldq_le_p(ptemp) & ~mask) | data); break; case PCH_PIC_ROUTE_ENTRY ... PCH_PIC_ROUTE_ENTRY_END: - ptemp = (uint64_t *)(s->route_entry + addr - PCH_PIC_ROUTE_ENTRY); - *ptemp = (*ptemp & ~mask) | data; + ptemp = (uint64_t *)&s->route_entry[addr - PCH_PIC_ROUTE_ENTRY]; + stq_le_p(ptemp, (ldq_le_p(ptemp) & ~mask) | data); break; default: qemu_log_mask(LOG_GUEST_ERROR, From de3c9f552d8d6af5e1a5f28eb93c836080715796 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Tue, 8 Jul 2025 15:22:37 +0800 Subject: [PATCH 0095/2396] target/loongarch: Move some function definition to kvm directory Move function definition specified with kvm to the corresponding directory. Also remove header file "cpu.h" including outside of macro QEMU_KVM_LOONGARCH_H. Signed-off-by: Bibo Mao Reviewed-by: Richard Henderson --- hw/loongarch/virt.c | 1 + target/loongarch/cpu.h | 9 --------- target/loongarch/kvm/kvm_loongarch.h | 4 ++-- 3 files changed, 3 insertions(+), 11 deletions(-) diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c index b15ada2078..31215b7785 100644 --- a/hw/loongarch/virt.c +++ b/hw/loongarch/virt.c @@ -46,6 +46,7 @@ #include "hw/block/flash.h" #include "hw/virtio/virtio-iommu.h" #include "qemu/error-report.h" +#include "kvm/kvm_loongarch.h" static void virt_get_veiointc(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) diff --git a/target/loongarch/cpu.h b/target/loongarch/cpu.h index 9538e8d61d..bbe6db33f1 100644 --- a/target/loongarch/cpu.h +++ b/target/loongarch/cpu.h @@ -496,13 +496,4 @@ static inline void set_pc(CPULoongArchState *env, uint64_t value) void loongarch_cpu_post_init(Object *obj); -#ifdef CONFIG_KVM -void kvm_loongarch_cpu_post_init(LoongArchCPU *cpu); -#else -static inline void kvm_loongarch_cpu_post_init(LoongArchCPU *cpu) -{ -} -#endif -void kvm_loongarch_init_irq_routing(void); - #endif /* LOONGARCH_CPU_H */ diff --git a/target/loongarch/kvm/kvm_loongarch.h b/target/loongarch/kvm/kvm_loongarch.h index 1051a341ec..51475675d6 100644 --- a/target/loongarch/kvm/kvm_loongarch.h +++ b/target/loongarch/kvm/kvm_loongarch.h @@ -5,11 +5,11 @@ * Copyright (c) 2023 Loongson Technology Corporation Limited */ -#include "cpu.h" - #ifndef QEMU_KVM_LOONGARCH_H #define QEMU_KVM_LOONGARCH_H +void kvm_loongarch_cpu_post_init(LoongArchCPU *cpu); +void kvm_loongarch_init_irq_routing(void); int kvm_loongarch_set_interrupt(LoongArchCPU *cpu, int irq, int level); void kvm_arch_reset_vcpu(CPUState *cs); From 982d7674ff4ed1affc4dadba2c6f6ab1b1df4e97 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Tue, 8 Jul 2025 15:35:12 +0800 Subject: [PATCH 0096/2396] target/loongarch: Define function loongarch_cpu_post_init as static Function loongarch_cpu_post_init() is implemented and used in the same file target/loongarch/cpu.c, it can be defined as static function. This patch moves implementation about function loongarch_cpu_post_init() before it is referenced. And it is only code movement, no function change. Signed-off-by: Bibo Mao Reviewed-by: Richard Henderson --- target/loongarch/cpu.c | 180 ++++++++++++++++++++--------------------- target/loongarch/cpu.h | 2 - 2 files changed, 90 insertions(+), 92 deletions(-) diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c index abad84c054..b96429ffb1 100644 --- a/target/loongarch/cpu.c +++ b/target/loongarch/cpu.c @@ -422,6 +422,96 @@ static void loongarch_la464_init_csr(Object *obj) #endif } +static bool loongarch_get_lsx(Object *obj, Error **errp) +{ + return LOONGARCH_CPU(obj)->lsx != ON_OFF_AUTO_OFF; +} + +static void loongarch_set_lsx(Object *obj, bool value, Error **errp) +{ + LoongArchCPU *cpu = LOONGARCH_CPU(obj); + uint32_t val; + + cpu->lsx = value ? ON_OFF_AUTO_ON : ON_OFF_AUTO_OFF; + if (cpu->lsx == ON_OFF_AUTO_OFF) { + cpu->lasx = ON_OFF_AUTO_OFF; + if (cpu->lasx == ON_OFF_AUTO_ON) { + error_setg(errp, "Failed to disable LSX since LASX is enabled"); + return; + } + } + + if (kvm_enabled()) { + /* kvm feature detection in function kvm_arch_init_vcpu */ + return; + } + + /* LSX feature detection in TCG mode */ + val = cpu->env.cpucfg[2]; + if (cpu->lsx == ON_OFF_AUTO_ON) { + if (FIELD_EX32(val, CPUCFG2, LSX) == 0) { + error_setg(errp, "Failed to enable LSX in TCG mode"); + return; + } + } else { + cpu->env.cpucfg[2] = FIELD_DP32(val, CPUCFG2, LASX, 0); + val = cpu->env.cpucfg[2]; + } + + cpu->env.cpucfg[2] = FIELD_DP32(val, CPUCFG2, LSX, value); +} + +static bool loongarch_get_lasx(Object *obj, Error **errp) +{ + return LOONGARCH_CPU(obj)->lasx != ON_OFF_AUTO_OFF; +} + +static void loongarch_set_lasx(Object *obj, bool value, Error **errp) +{ + LoongArchCPU *cpu = LOONGARCH_CPU(obj); + uint32_t val; + + cpu->lasx = value ? ON_OFF_AUTO_ON : ON_OFF_AUTO_OFF; + if ((cpu->lsx == ON_OFF_AUTO_OFF) && (cpu->lasx == ON_OFF_AUTO_ON)) { + error_setg(errp, "Failed to enable LASX since lSX is disabled"); + return; + } + + if (kvm_enabled()) { + /* kvm feature detection in function kvm_arch_init_vcpu */ + return; + } + + /* LASX feature detection in TCG mode */ + val = cpu->env.cpucfg[2]; + if (cpu->lasx == ON_OFF_AUTO_ON) { + if (FIELD_EX32(val, CPUCFG2, LASX) == 0) { + error_setg(errp, "Failed to enable LASX in TCG mode"); + return; + } + } + + cpu->env.cpucfg[2] = FIELD_DP32(val, CPUCFG2, LASX, value); +} + +static void loongarch_cpu_post_init(Object *obj) +{ + LoongArchCPU *cpu = LOONGARCH_CPU(obj); + + cpu->lbt = ON_OFF_AUTO_OFF; + cpu->pmu = ON_OFF_AUTO_OFF; + cpu->lsx = ON_OFF_AUTO_AUTO; + cpu->lasx = ON_OFF_AUTO_AUTO; + object_property_add_bool(obj, "lsx", loongarch_get_lsx, + loongarch_set_lsx); + object_property_add_bool(obj, "lasx", loongarch_get_lasx, + loongarch_set_lasx); + /* lbt is enabled only in kvm mode, not supported in tcg mode */ + if (kvm_enabled()) { + kvm_loongarch_cpu_post_init(cpu); + } +} + static void loongarch_la464_initfn(Object *obj) { LoongArchCPU *cpu = LOONGARCH_CPU(obj); @@ -683,96 +773,6 @@ static void loongarch_cpu_unrealizefn(DeviceState *dev) lacc->parent_unrealize(dev); } -static bool loongarch_get_lsx(Object *obj, Error **errp) -{ - return LOONGARCH_CPU(obj)->lsx != ON_OFF_AUTO_OFF; -} - -static void loongarch_set_lsx(Object *obj, bool value, Error **errp) -{ - LoongArchCPU *cpu = LOONGARCH_CPU(obj); - uint32_t val; - - cpu->lsx = value ? ON_OFF_AUTO_ON : ON_OFF_AUTO_OFF; - if (cpu->lsx == ON_OFF_AUTO_OFF) { - cpu->lasx = ON_OFF_AUTO_OFF; - if (cpu->lasx == ON_OFF_AUTO_ON) { - error_setg(errp, "Failed to disable LSX since LASX is enabled"); - return; - } - } - - if (kvm_enabled()) { - /* kvm feature detection in function kvm_arch_init_vcpu */ - return; - } - - /* LSX feature detection in TCG mode */ - val = cpu->env.cpucfg[2]; - if (cpu->lsx == ON_OFF_AUTO_ON) { - if (FIELD_EX32(val, CPUCFG2, LSX) == 0) { - error_setg(errp, "Failed to enable LSX in TCG mode"); - return; - } - } else { - cpu->env.cpucfg[2] = FIELD_DP32(val, CPUCFG2, LASX, 0); - val = cpu->env.cpucfg[2]; - } - - cpu->env.cpucfg[2] = FIELD_DP32(val, CPUCFG2, LSX, value); -} - -static bool loongarch_get_lasx(Object *obj, Error **errp) -{ - return LOONGARCH_CPU(obj)->lasx != ON_OFF_AUTO_OFF; -} - -static void loongarch_set_lasx(Object *obj, bool value, Error **errp) -{ - LoongArchCPU *cpu = LOONGARCH_CPU(obj); - uint32_t val; - - cpu->lasx = value ? ON_OFF_AUTO_ON : ON_OFF_AUTO_OFF; - if ((cpu->lsx == ON_OFF_AUTO_OFF) && (cpu->lasx == ON_OFF_AUTO_ON)) { - error_setg(errp, "Failed to enable LASX since lSX is disabled"); - return; - } - - if (kvm_enabled()) { - /* kvm feature detection in function kvm_arch_init_vcpu */ - return; - } - - /* LASX feature detection in TCG mode */ - val = cpu->env.cpucfg[2]; - if (cpu->lasx == ON_OFF_AUTO_ON) { - if (FIELD_EX32(val, CPUCFG2, LASX) == 0) { - error_setg(errp, "Failed to enable LASX in TCG mode"); - return; - } - } - - cpu->env.cpucfg[2] = FIELD_DP32(val, CPUCFG2, LASX, value); -} - -void loongarch_cpu_post_init(Object *obj) -{ - LoongArchCPU *cpu = LOONGARCH_CPU(obj); - - cpu->lbt = ON_OFF_AUTO_OFF; - cpu->pmu = ON_OFF_AUTO_OFF; - cpu->lsx = ON_OFF_AUTO_AUTO; - cpu->lasx = ON_OFF_AUTO_AUTO; - object_property_add_bool(obj, "lsx", loongarch_get_lsx, - loongarch_set_lsx); - object_property_add_bool(obj, "lasx", loongarch_get_lasx, - loongarch_set_lasx); - /* lbt is enabled only in kvm mode, not supported in tcg mode */ - if (kvm_enabled()) { - kvm_loongarch_cpu_post_init(cpu); - } -} - static void loongarch_cpu_init(Object *obj) { #ifndef CONFIG_USER_ONLY diff --git a/target/loongarch/cpu.h b/target/loongarch/cpu.h index bbe6db33f1..7731f6acdc 100644 --- a/target/loongarch/cpu.h +++ b/target/loongarch/cpu.h @@ -494,6 +494,4 @@ static inline void set_pc(CPULoongArchState *env, uint64_t value) #define CPU_RESOLVING_TYPE TYPE_LOONGARCH_CPU -void loongarch_cpu_post_init(Object *obj); - #endif /* LOONGARCH_CPU_H */ From 198c827ca5d3bee6aa0c498f25f5ea6928f57de2 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Wed, 16 Jul 2025 09:45:47 +0800 Subject: [PATCH 0097/2396] target/loongarch: Set page size in TLB entry with STLB With VTLB different TLB entry may have different page size, and page size is set in PS field of TLB entry. However with STLB, all the TLB entries have the same page size, page size comes from register CSR_STLBPS, PS field of TLB entry is not used. Here PS field of TLB entry is used with all TLB entries, even with STLB. It is convenient with TLB maintainance operation. Signed-off-by: Bibo Mao Reviewed-by: Song Gao --- target/loongarch/tcg/tlb_helper.c | 41 ++++++++----------------------- 1 file changed, 10 insertions(+), 31 deletions(-) diff --git a/target/loongarch/tcg/tlb_helper.c b/target/loongarch/tcg/tlb_helper.c index 8872593ff0..3ea0e153b1 100644 --- a/target/loongarch/tcg/tlb_helper.c +++ b/target/loongarch/tcg/tlb_helper.c @@ -110,11 +110,8 @@ static void invalidate_tlb_entry(CPULoongArchState *env, int index) if (!tlb_e) { return; } - if (index >= LOONGARCH_STLB) { - tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS); - } else { - tlb_ps = FIELD_EX64(env->CSR_STLBPS, CSR_STLBPS, PS); - } + + tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS); pagesize = MAKE_64BIT_MASK(tlb_ps, 1); mask = MAKE_64BIT_MASK(0, tlb_ps + 1); @@ -173,11 +170,8 @@ static void fill_tlb_entry(CPULoongArchState *env, int index) lo1 = env->CSR_TLBELO1; } - /* Only MTLB has the ps fields */ - if (index >= LOONGARCH_STLB) { - tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, PS, csr_ps); - } - + /* Store page size in field PS */ + tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, PS, csr_ps); tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, VPPN, csr_vppn); tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, E, 1); csr_asid = FIELD_EX64(env->CSR_ASID, CSR_ASID, ASID); @@ -283,12 +277,7 @@ void helper_tlbrd(CPULoongArchState *env) index = FIELD_EX64(env->CSR_TLBIDX, CSR_TLBIDX, INDEX); tlb = &env->tlb[index]; - - if (index >= LOONGARCH_STLB) { - tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS); - } else { - tlb_ps = FIELD_EX64(env->CSR_STLBPS, CSR_STLBPS, PS); - } + tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS); tlb_e = FIELD_EX64(tlb->tlb_misc, TLB_MISC, E); if (!tlb_e) { @@ -476,11 +465,8 @@ void helper_invtlb_page_asid(CPULoongArchState *env, target_ulong info, if (!tlb_e) { continue; } - if (i >= LOONGARCH_STLB) { - tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS); - } else { - tlb_ps = FIELD_EX64(env->CSR_STLBPS, CSR_STLBPS, PS); - } + + tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS); tlb_vppn = FIELD_EX64(tlb->tlb_misc, TLB_MISC, VPPN); vpn = (addr & TARGET_VIRT_MASK) >> (tlb_ps + 1); compare_shift = tlb_ps + 1 - R_TLB_MISC_VPPN_SHIFT; @@ -509,11 +495,8 @@ void helper_invtlb_page_asid_or_g(CPULoongArchState *env, if (!tlb_e) { continue; } - if (i >= LOONGARCH_STLB) { - tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS); - } else { - tlb_ps = FIELD_EX64(env->CSR_STLBPS, CSR_STLBPS, PS); - } + + tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS); tlb_vppn = FIELD_EX64(tlb->tlb_misc, TLB_MISC, VPPN); vpn = (addr & TARGET_VIRT_MASK) >> (tlb_ps + 1); compare_shift = tlb_ps + 1 - R_TLB_MISC_VPPN_SHIFT; @@ -673,11 +656,7 @@ static int loongarch_map_tlb_entry(CPULoongArchState *env, hwaddr *physical, uint64_t tlb_entry, tlb_ppn; uint8_t tlb_ps, n, tlb_v, tlb_d, tlb_plv, tlb_nx, tlb_nr, tlb_rplv; - if (index >= LOONGARCH_STLB) { - tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS); - } else { - tlb_ps = FIELD_EX64(env->CSR_STLBPS, CSR_STLBPS, PS); - } + tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS); n = (address >> tlb_ps) & 0x1;/* Odd or even */ tlb_entry = n ? tlb->tlb_entry1 : tlb->tlb_entry0; From a3d4bc4845911d82162f5be782f73e9742a41306 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Tue, 8 Jul 2025 16:10:20 +0800 Subject: [PATCH 0098/2396] target/loongarch: Add header file cpu-mmu.h New header file cpu-mmu.h is added and move mmu relative function declaration to this file. Signed-off-by: Bibo Mao Reviewed-by: Richard Henderson --- target/loongarch/cpu-mmu.h | 30 ++++++++++++++++++++++++++++++ target/loongarch/cpu.c | 1 + target/loongarch/cpu_helper.c | 1 + target/loongarch/internals.h | 20 -------------------- target/loongarch/tcg/csr_helper.c | 1 + target/loongarch/tcg/tlb_helper.c | 1 + 6 files changed, 34 insertions(+), 20 deletions(-) create mode 100644 target/loongarch/cpu-mmu.h diff --git a/target/loongarch/cpu-mmu.h b/target/loongarch/cpu-mmu.h new file mode 100644 index 0000000000..4c5cbd7425 --- /dev/null +++ b/target/loongarch/cpu-mmu.h @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * LoongArch CPU parameters for QEMU. + * + * Copyright (c) 2025 Loongson Technology Corporation Limited + */ + +#ifndef LOONGARCH_CPU_MMU_H +#define LOONGARCH_CPU_MMU_H + +enum { + TLBRET_MATCH = 0, + TLBRET_BADADDR = 1, + TLBRET_NOMATCH = 2, + TLBRET_INVALID = 3, + TLBRET_DIRTY = 4, + TLBRET_RI = 5, + TLBRET_XI = 6, + TLBRET_PE = 7, +}; + +bool check_ps(CPULoongArchState *ent, uint8_t ps); +int get_physical_address(CPULoongArchState *env, hwaddr *physical, + int *prot, target_ulong address, + MMUAccessType access_type, int mmu_idx, int is_debug); +void get_dir_base_width(CPULoongArchState *env, uint64_t *dir_base, + uint64_t *dir_width, target_ulong level); +hwaddr loongarch_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); + +#endif /* LOONGARCH_CPU_MMU_H */ diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c index b96429ffb1..990985708e 100644 --- a/target/loongarch/cpu.c +++ b/target/loongarch/cpu.c @@ -17,6 +17,7 @@ #include "hw/qdev-properties.h" #include "exec/translation-block.h" #include "cpu.h" +#include "cpu-mmu.h" #include "internals.h" #include "fpu/softfloat-helpers.h" #include "csr.h" diff --git a/target/loongarch/cpu_helper.c b/target/loongarch/cpu_helper.c index b5f732f15b..418122f447 100644 --- a/target/loongarch/cpu_helper.c +++ b/target/loongarch/cpu_helper.c @@ -13,6 +13,7 @@ #include "exec/target_page.h" #include "internals.h" #include "cpu-csr.h" +#include "cpu-mmu.h" #include "tcg/tcg_loongarch.h" void get_dir_base_width(CPULoongArchState *env, uint64_t *dir_base, diff --git a/target/loongarch/internals.h b/target/loongarch/internals.h index a7384b0d31..e50d109767 100644 --- a/target/loongarch/internals.h +++ b/target/loongarch/internals.h @@ -32,19 +32,6 @@ void restore_fp_status(CPULoongArchState *env); #endif #ifndef CONFIG_USER_ONLY -enum { - TLBRET_MATCH = 0, - TLBRET_BADADDR = 1, - TLBRET_NOMATCH = 2, - TLBRET_INVALID = 3, - TLBRET_DIRTY = 4, - TLBRET_RI = 5, - TLBRET_XI = 6, - TLBRET_PE = 7, -}; - -bool check_ps(CPULoongArchState *ent, uint8_t ps); - extern const VMStateDescription vmstate_loongarch_cpu; void loongarch_cpu_set_irq(void *opaque, int irq, int level); @@ -54,13 +41,6 @@ uint64_t cpu_loongarch_get_constant_timer_counter(LoongArchCPU *cpu); uint64_t cpu_loongarch_get_constant_timer_ticks(LoongArchCPU *cpu); void cpu_loongarch_store_constant_timer_config(LoongArchCPU *cpu, uint64_t value); -int get_physical_address(CPULoongArchState *env, hwaddr *physical, - int *prot, target_ulong address, - MMUAccessType access_type, int mmu_idx, int is_debug); -void get_dir_base_width(CPULoongArchState *env, uint64_t *dir_base, - uint64_t *dir_width, target_ulong level); -hwaddr loongarch_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); - #endif /* !CONFIG_USER_ONLY */ uint64_t read_fcc(CPULoongArchState *env); diff --git a/target/loongarch/tcg/csr_helper.c b/target/loongarch/tcg/csr_helper.c index 28b1bb86bd..0d99e2c92b 100644 --- a/target/loongarch/tcg/csr_helper.c +++ b/target/loongarch/tcg/csr_helper.c @@ -16,6 +16,7 @@ #include "accel/tcg/cpu-ldst.h" #include "hw/irq.h" #include "cpu-csr.h" +#include "cpu-mmu.h" target_ulong helper_csrwr_stlbps(CPULoongArchState *env, target_ulong val) { diff --git a/target/loongarch/tcg/tlb_helper.c b/target/loongarch/tcg/tlb_helper.c index 3ea0e153b1..1f49619e7f 100644 --- a/target/loongarch/tcg/tlb_helper.c +++ b/target/loongarch/tcg/tlb_helper.c @@ -10,6 +10,7 @@ #include "qemu/guest-random.h" #include "cpu.h" +#include "cpu-mmu.h" #include "internals.h" #include "exec/helper-proto.h" #include "exec/cputlb.h" From 912f75eaed5f686fe0f312e8ac797be7c1f43b0e Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Tue, 29 Jul 2025 10:44:51 +0800 Subject: [PATCH 0099/2396] target/loongarch: Add enum type TLBRet definition There is mixed usage between enum variable TLBRET_xxx and int type, here add enum type TLBRet definition and replace int type variable with enum type TLBRet in some functions. Signed-off-by: Bibo Mao Reviewed-by: Richard Henderson --- target/loongarch/cpu-mmu.h | 27 ++++++++++++++------------- target/loongarch/cpu_helper.c | 26 ++++++++++++++------------ target/loongarch/tcg/tcg_loongarch.h | 7 ++++--- target/loongarch/tcg/tlb_helper.c | 16 ++++++++-------- 4 files changed, 40 insertions(+), 36 deletions(-) diff --git a/target/loongarch/cpu-mmu.h b/target/loongarch/cpu-mmu.h index 4c5cbd7425..cbe6f37773 100644 --- a/target/loongarch/cpu-mmu.h +++ b/target/loongarch/cpu-mmu.h @@ -8,21 +8,22 @@ #ifndef LOONGARCH_CPU_MMU_H #define LOONGARCH_CPU_MMU_H -enum { - TLBRET_MATCH = 0, - TLBRET_BADADDR = 1, - TLBRET_NOMATCH = 2, - TLBRET_INVALID = 3, - TLBRET_DIRTY = 4, - TLBRET_RI = 5, - TLBRET_XI = 6, - TLBRET_PE = 7, -}; +typedef enum TLBRet { + TLBRET_MATCH, + TLBRET_BADADDR, + TLBRET_NOMATCH, + TLBRET_INVALID, + TLBRET_DIRTY, + TLBRET_RI, + TLBRET_XI, + TLBRET_PE, +} TLBRet; bool check_ps(CPULoongArchState *ent, uint8_t ps); -int get_physical_address(CPULoongArchState *env, hwaddr *physical, - int *prot, target_ulong address, - MMUAccessType access_type, int mmu_idx, int is_debug); +TLBRet get_physical_address(CPULoongArchState *env, hwaddr *physical, + int *prot, target_ulong address, + MMUAccessType access_type, int mmu_idx, + int is_debug); void get_dir_base_width(CPULoongArchState *env, uint64_t *dir_base, uint64_t *dir_width, target_ulong level); hwaddr loongarch_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); diff --git a/target/loongarch/cpu_helper.c b/target/loongarch/cpu_helper.c index 418122f447..17a0735f5c 100644 --- a/target/loongarch/cpu_helper.c +++ b/target/loongarch/cpu_helper.c @@ -44,8 +44,9 @@ void get_dir_base_width(CPULoongArchState *env, uint64_t *dir_base, } } -static int loongarch_page_table_walker(CPULoongArchState *env, hwaddr *physical, - int *prot, target_ulong address) +static TLBRet loongarch_page_table_walker(CPULoongArchState *env, + hwaddr *physical, + int *prot, target_ulong address) { CPUState *cs = env_cpu(env); target_ulong index, phys; @@ -116,15 +117,15 @@ static int loongarch_page_table_walker(CPULoongArchState *env, hwaddr *physical, /* mask other attribute bits */ *physical = base & TARGET_PAGE_MASK; - return 0; + return TLBRET_MATCH; } -static int loongarch_map_address(CPULoongArchState *env, hwaddr *physical, - int *prot, target_ulong address, - MMUAccessType access_type, int mmu_idx, - int is_debug) +static TLBRet loongarch_map_address(CPULoongArchState *env, hwaddr *physical, + int *prot, target_ulong address, + MMUAccessType access_type, int mmu_idx, + int is_debug) { - int ret; + TLBRet ret; if (tcg_enabled()) { ret = loongarch_get_addr_from_tlb(env, physical, prot, address, @@ -158,9 +159,10 @@ static hwaddr dmw_va2pa(CPULoongArchState *env, target_ulong va, } } -int get_physical_address(CPULoongArchState *env, hwaddr *physical, - int *prot, target_ulong address, - MMUAccessType access_type, int mmu_idx, int is_debug) +TLBRet get_physical_address(CPULoongArchState *env, hwaddr *physical, + int *prot, target_ulong address, + MMUAccessType access_type, int mmu_idx, + int is_debug) { int user_mode = mmu_idx == MMU_USER_IDX; int kernel_mode = mmu_idx == MMU_KERNEL_IDX; @@ -214,7 +216,7 @@ hwaddr loongarch_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) int prot; if (get_physical_address(env, &phys_addr, &prot, addr, MMU_DATA_LOAD, - cpu_mmu_index(cs, false), 1) != 0) { + cpu_mmu_index(cs, false), 1) != TLBRET_MATCH) { return -1; } return phys_addr; diff --git a/target/loongarch/tcg/tcg_loongarch.h b/target/loongarch/tcg/tcg_loongarch.h index fd4e116022..488700c3c3 100644 --- a/target/loongarch/tcg/tcg_loongarch.h +++ b/target/loongarch/tcg/tcg_loongarch.h @@ -7,6 +7,7 @@ #ifndef TARGET_LOONGARCH_TCG_LOONGARCH_H #define TARGET_LOONGARCH_TCG_LOONGARCH_H #include "cpu.h" +#include "cpu-mmu.h" void loongarch_csr_translate_init(void); @@ -14,8 +15,8 @@ bool loongarch_cpu_tlb_fill(CPUState *cs, vaddr address, int size, MMUAccessType access_type, int mmu_idx, bool probe, uintptr_t retaddr); -int loongarch_get_addr_from_tlb(CPULoongArchState *env, hwaddr *physical, - int *prot, target_ulong address, - MMUAccessType access_type, int mmu_idx); +TLBRet loongarch_get_addr_from_tlb(CPULoongArchState *env, hwaddr *physical, + int *prot, target_ulong address, + MMUAccessType access_type, int mmu_idx); #endif /* TARGET_LOONGARCH_TCG_LOONGARCH_H */ diff --git a/target/loongarch/tcg/tlb_helper.c b/target/loongarch/tcg/tlb_helper.c index 1f49619e7f..4a2a565985 100644 --- a/target/loongarch/tcg/tlb_helper.c +++ b/target/loongarch/tcg/tlb_helper.c @@ -30,7 +30,7 @@ bool check_ps(CPULoongArchState *env, uint8_t tlb_ps) } static void raise_mmu_exception(CPULoongArchState *env, target_ulong address, - MMUAccessType access_type, int tlb_error) + MMUAccessType access_type, TLBRet tlb_error) { CPUState *cs = env_cpu(env); @@ -517,7 +517,7 @@ bool loongarch_cpu_tlb_fill(CPUState *cs, vaddr address, int size, CPULoongArchState *env = cpu_env(cs); hwaddr physical; int prot; - int ret; + TLBRet ret; /* Data access */ ret = get_physical_address(env, &physical, &prot, address, @@ -648,9 +648,9 @@ void helper_ldpte(CPULoongArchState *env, target_ulong base, target_ulong odd, env->CSR_TLBREHI = FIELD_DP64(env->CSR_TLBREHI, CSR_TLBREHI, PS, ps); } -static int loongarch_map_tlb_entry(CPULoongArchState *env, hwaddr *physical, - int *prot, target_ulong address, - int access_type, int index, int mmu_idx) +static TLBRet loongarch_map_tlb_entry(CPULoongArchState *env, hwaddr *physical, + int *prot, target_ulong address, + int access_type, int index, int mmu_idx) { LoongArchTLB *tlb = &env->tlb[index]; uint64_t plv = mmu_idx; @@ -713,9 +713,9 @@ static int loongarch_map_tlb_entry(CPULoongArchState *env, hwaddr *physical, return TLBRET_MATCH; } -int loongarch_get_addr_from_tlb(CPULoongArchState *env, hwaddr *physical, - int *prot, target_ulong address, - MMUAccessType access_type, int mmu_idx) +TLBRet loongarch_get_addr_from_tlb(CPULoongArchState *env, hwaddr *physical, + int *prot, target_ulong address, + MMUAccessType access_type, int mmu_idx) { int index, match; From 82cd0be29b603ef620f45053a406bdb4a3221563 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Tue, 29 Jul 2025 11:29:32 +0800 Subject: [PATCH 0100/2396] target/loongarch: Use vaddr in get_physical_address() Replace target_ulong type with vaddr in function get_physical_address() and the same with its calling functions. Signed-off-by: Bibo Mao Reviewed-by: Richard Henderson --- target/loongarch/cpu-mmu.h | 2 +- target/loongarch/cpu_helper.c | 9 ++++----- target/loongarch/tcg/tlb_helper.c | 11 ++++++----- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/target/loongarch/cpu-mmu.h b/target/loongarch/cpu-mmu.h index cbe6f37773..dffc12820f 100644 --- a/target/loongarch/cpu-mmu.h +++ b/target/loongarch/cpu-mmu.h @@ -21,7 +21,7 @@ typedef enum TLBRet { bool check_ps(CPULoongArchState *ent, uint8_t ps); TLBRet get_physical_address(CPULoongArchState *env, hwaddr *physical, - int *prot, target_ulong address, + int *prot, vaddr address, MMUAccessType access_type, int mmu_idx, int is_debug); void get_dir_base_width(CPULoongArchState *env, uint64_t *dir_base, diff --git a/target/loongarch/cpu_helper.c b/target/loongarch/cpu_helper.c index 17a0735f5c..0c037ef163 100644 --- a/target/loongarch/cpu_helper.c +++ b/target/loongarch/cpu_helper.c @@ -46,7 +46,7 @@ void get_dir_base_width(CPULoongArchState *env, uint64_t *dir_base, static TLBRet loongarch_page_table_walker(CPULoongArchState *env, hwaddr *physical, - int *prot, target_ulong address) + int *prot, vaddr address) { CPUState *cs = env_cpu(env); target_ulong index, phys; @@ -121,7 +121,7 @@ static TLBRet loongarch_page_table_walker(CPULoongArchState *env, } static TLBRet loongarch_map_address(CPULoongArchState *env, hwaddr *physical, - int *prot, target_ulong address, + int *prot, vaddr address, MMUAccessType access_type, int mmu_idx, int is_debug) { @@ -147,8 +147,7 @@ static TLBRet loongarch_map_address(CPULoongArchState *env, hwaddr *physical, return TLBRET_NOMATCH; } -static hwaddr dmw_va2pa(CPULoongArchState *env, target_ulong va, - target_ulong dmw) +static hwaddr dmw_va2pa(CPULoongArchState *env, vaddr va, target_ulong dmw) { if (is_la64(env)) { return va & TARGET_VIRT_MASK; @@ -160,7 +159,7 @@ static hwaddr dmw_va2pa(CPULoongArchState *env, target_ulong va, } TLBRet get_physical_address(CPULoongArchState *env, hwaddr *physical, - int *prot, target_ulong address, + int *prot, vaddr address, MMUAccessType access_type, int mmu_idx, int is_debug) { diff --git a/target/loongarch/tcg/tlb_helper.c b/target/loongarch/tcg/tlb_helper.c index 4a2a565985..3d09f18020 100644 --- a/target/loongarch/tcg/tlb_helper.c +++ b/target/loongarch/tcg/tlb_helper.c @@ -29,7 +29,7 @@ bool check_ps(CPULoongArchState *env, uint8_t tlb_ps) return BIT_ULL(tlb_ps) & (env->CSR_PRCFG2); } -static void raise_mmu_exception(CPULoongArchState *env, target_ulong address, +static void raise_mmu_exception(CPULoongArchState *env, vaddr address, MMUAccessType access_type, TLBRet tlb_error) { CPUState *cs = env_cpu(env); @@ -198,7 +198,7 @@ static uint32_t get_random_tlb(uint32_t low, uint32_t high) * field in tlb entry contains bit[47:13], so need adjust. * virt_vpn = vaddr[47:13] */ -static bool loongarch_tlb_search(CPULoongArchState *env, target_ulong vaddr, +static bool loongarch_tlb_search(CPULoongArchState *env, vaddr vaddr, int *index) { LoongArchTLB *tlb; @@ -649,8 +649,9 @@ void helper_ldpte(CPULoongArchState *env, target_ulong base, target_ulong odd, } static TLBRet loongarch_map_tlb_entry(CPULoongArchState *env, hwaddr *physical, - int *prot, target_ulong address, - int access_type, int index, int mmu_idx) + int *prot, vaddr address, + int access_type, int index, + int mmu_idx) { LoongArchTLB *tlb = &env->tlb[index]; uint64_t plv = mmu_idx; @@ -714,7 +715,7 @@ static TLBRet loongarch_map_tlb_entry(CPULoongArchState *env, hwaddr *physical, } TLBRet loongarch_get_addr_from_tlb(CPULoongArchState *env, hwaddr *physical, - int *prot, target_ulong address, + int *prot, vaddr address, MMUAccessType access_type, int mmu_idx) { int index, match; From e6c855f44ad63b5e94bc3d27adca12c24ce7953d Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Tue, 29 Jul 2025 11:38:35 +0800 Subject: [PATCH 0101/2396] target/loongarch: Use MMUAccessType in loongarch_map_tlb_entry() Enum type MMUAccessType is used in function loongarch_map_tlb_entry() rather than int type, and keep consistent with its caller function. Signed-off-by: Bibo Mao Reviewed-by: Richard Henderson --- target/loongarch/tcg/tlb_helper.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/loongarch/tcg/tlb_helper.c b/target/loongarch/tcg/tlb_helper.c index 3d09f18020..915b1aadb5 100644 --- a/target/loongarch/tcg/tlb_helper.c +++ b/target/loongarch/tcg/tlb_helper.c @@ -650,7 +650,7 @@ void helper_ldpte(CPULoongArchState *env, target_ulong base, target_ulong odd, static TLBRet loongarch_map_tlb_entry(CPULoongArchState *env, hwaddr *physical, int *prot, vaddr address, - int access_type, int index, + MMUAccessType access_type, int index, int mmu_idx) { LoongArchTLB *tlb = &env->tlb[index]; From 7392cb1c7b24e34987b6f2bbff00c39fe0829cc9 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Tue, 29 Jul 2025 12:03:43 +0800 Subject: [PATCH 0102/2396] target/loongarch: Add common function loongarch_check_pte() Common function loongarch_check_pte() is to check tlb entry, return the physical address and access priviledge if found. Also it can be used with page table entry, which is used in page table walker. Signed-off-by: Bibo Mao Reviewed-by: Richard Henderson --- target/loongarch/cpu-mmu.h | 10 +++++ target/loongarch/cpu_helper.c | 61 ++++++++++++++++++++++++++++ target/loongarch/tcg/tlb_helper.c | 66 ++++++------------------------- 3 files changed, 83 insertions(+), 54 deletions(-) diff --git a/target/loongarch/cpu-mmu.h b/target/loongarch/cpu-mmu.h index dffc12820f..be3d11d3c1 100644 --- a/target/loongarch/cpu-mmu.h +++ b/target/loongarch/cpu-mmu.h @@ -19,7 +19,17 @@ typedef enum TLBRet { TLBRET_PE, } TLBRet; +typedef struct MMUContext { + vaddr addr; + uint64_t pte; + hwaddr physical; + int ps; /* page size shift */ + int prot; +} MMUContext; + bool check_ps(CPULoongArchState *ent, uint8_t ps); +TLBRet loongarch_check_pte(CPULoongArchState *env, MMUContext *context, + MMUAccessType access_type, int mmu_idx); TLBRet get_physical_address(CPULoongArchState *env, hwaddr *physical, int *prot, vaddr address, MMUAccessType access_type, int mmu_idx, diff --git a/target/loongarch/cpu_helper.c b/target/loongarch/cpu_helper.c index 0c037ef163..739cdab5aa 100644 --- a/target/loongarch/cpu_helper.c +++ b/target/loongarch/cpu_helper.c @@ -44,6 +44,67 @@ void get_dir_base_width(CPULoongArchState *env, uint64_t *dir_base, } } +TLBRet loongarch_check_pte(CPULoongArchState *env, MMUContext *context, + MMUAccessType access_type, int mmu_idx) +{ + uint64_t plv = mmu_idx; + uint64_t tlb_entry, tlb_ppn; + uint8_t tlb_ps, tlb_v, tlb_d, tlb_plv, tlb_nx, tlb_nr, tlb_rplv; + + tlb_entry = context->pte; + tlb_ps = context->ps; + tlb_v = FIELD_EX64(tlb_entry, TLBENTRY, V); + tlb_d = FIELD_EX64(tlb_entry, TLBENTRY, D); + tlb_plv = FIELD_EX64(tlb_entry, TLBENTRY, PLV); + if (is_la64(env)) { + tlb_ppn = FIELD_EX64(tlb_entry, TLBENTRY_64, PPN); + tlb_nx = FIELD_EX64(tlb_entry, TLBENTRY_64, NX); + tlb_nr = FIELD_EX64(tlb_entry, TLBENTRY_64, NR); + tlb_rplv = FIELD_EX64(tlb_entry, TLBENTRY_64, RPLV); + } else { + tlb_ppn = FIELD_EX64(tlb_entry, TLBENTRY_32, PPN); + tlb_nx = 0; + tlb_nr = 0; + tlb_rplv = 0; + } + + /* Remove sw bit between bit12 -- bit PS*/ + tlb_ppn = tlb_ppn & ~(((0x1UL << (tlb_ps - 12)) - 1)); + + /* Check access rights */ + if (!tlb_v) { + return TLBRET_INVALID; + } + + if (access_type == MMU_INST_FETCH && tlb_nx) { + return TLBRET_XI; + } + + if (access_type == MMU_DATA_LOAD && tlb_nr) { + return TLBRET_RI; + } + + if (((tlb_rplv == 0) && (plv > tlb_plv)) || + ((tlb_rplv == 1) && (plv != tlb_plv))) { + return TLBRET_PE; + } + + if ((access_type == MMU_DATA_STORE) && !tlb_d) { + return TLBRET_DIRTY; + } + + context->physical = (tlb_ppn << R_TLBENTRY_64_PPN_SHIFT) | + (context->addr & MAKE_64BIT_MASK(0, tlb_ps)); + context->prot = PAGE_READ; + if (tlb_d) { + context->prot |= PAGE_WRITE; + } + if (!tlb_nx) { + context->prot |= PAGE_EXEC; + } + return TLBRET_MATCH; +} + static TLBRet loongarch_page_table_walker(CPULoongArchState *env, hwaddr *physical, int *prot, vaddr address) diff --git a/target/loongarch/tcg/tlb_helper.c b/target/loongarch/tcg/tlb_helper.c index 915b1aadb5..10322da62e 100644 --- a/target/loongarch/tcg/tlb_helper.c +++ b/target/loongarch/tcg/tlb_helper.c @@ -654,64 +654,22 @@ static TLBRet loongarch_map_tlb_entry(CPULoongArchState *env, hwaddr *physical, int mmu_idx) { LoongArchTLB *tlb = &env->tlb[index]; - uint64_t plv = mmu_idx; - uint64_t tlb_entry, tlb_ppn; - uint8_t tlb_ps, n, tlb_v, tlb_d, tlb_plv, tlb_nx, tlb_nr, tlb_rplv; + uint8_t tlb_ps, n; + MMUContext context; + TLBRet ret; tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS); n = (address >> tlb_ps) & 0x1;/* Odd or even */ + context.pte = n ? tlb->tlb_entry1 : tlb->tlb_entry0; + context.addr = address; + context.ps = tlb_ps; + ret = loongarch_check_pte(env, &context, access_type, mmu_idx); + if (ret == TLBRET_MATCH) { + *physical = context.physical; + *prot = context.prot; + } - tlb_entry = n ? tlb->tlb_entry1 : tlb->tlb_entry0; - tlb_v = FIELD_EX64(tlb_entry, TLBENTRY, V); - tlb_d = FIELD_EX64(tlb_entry, TLBENTRY, D); - tlb_plv = FIELD_EX64(tlb_entry, TLBENTRY, PLV); - if (is_la64(env)) { - tlb_ppn = FIELD_EX64(tlb_entry, TLBENTRY_64, PPN); - tlb_nx = FIELD_EX64(tlb_entry, TLBENTRY_64, NX); - tlb_nr = FIELD_EX64(tlb_entry, TLBENTRY_64, NR); - tlb_rplv = FIELD_EX64(tlb_entry, TLBENTRY_64, RPLV); - } else { - tlb_ppn = FIELD_EX64(tlb_entry, TLBENTRY_32, PPN); - tlb_nx = 0; - tlb_nr = 0; - tlb_rplv = 0; - } - - /* Remove sw bit between bit12 -- bit PS*/ - tlb_ppn = tlb_ppn & ~(((0x1UL << (tlb_ps - 12)) - 1)); - - /* Check access rights */ - if (!tlb_v) { - return TLBRET_INVALID; - } - - if (access_type == MMU_INST_FETCH && tlb_nx) { - return TLBRET_XI; - } - - if (access_type == MMU_DATA_LOAD && tlb_nr) { - return TLBRET_RI; - } - - if (((tlb_rplv == 0) && (plv > tlb_plv)) || - ((tlb_rplv == 1) && (plv != tlb_plv))) { - return TLBRET_PE; - } - - if ((access_type == MMU_DATA_STORE) && !tlb_d) { - return TLBRET_DIRTY; - } - - *physical = (tlb_ppn << R_TLBENTRY_64_PPN_SHIFT) | - (address & MAKE_64BIT_MASK(0, tlb_ps)); - *prot = PAGE_READ; - if (tlb_d) { - *prot |= PAGE_WRITE; - } - if (!tlb_nx) { - *prot |= PAGE_EXEC; - } - return TLBRET_MATCH; + return ret; } TLBRet loongarch_get_addr_from_tlb(CPULoongArchState *env, hwaddr *physical, From 3dd4a2d0fc339c3cb321d69a0372cb9433786105 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Tue, 29 Jul 2025 16:10:02 +0800 Subject: [PATCH 0103/2396] target/loongarch: Use loongarch_check_pte in loongarch_page_table_walker Function loongarch_check_pte() can get physical address and access priviledge, it works on both TLB entry and pte entry. It can be used in function loongarch_page_table_walker() also. Signed-off-by: Bibo Mao Reviewed-by: Richard Henderson --- target/loongarch/cpu_helper.c | 42 +++++++++++++---------------------- 1 file changed, 16 insertions(+), 26 deletions(-) diff --git a/target/loongarch/cpu_helper.c b/target/loongarch/cpu_helper.c index 739cdab5aa..cd61b33ef9 100644 --- a/target/loongarch/cpu_helper.c +++ b/target/loongarch/cpu_helper.c @@ -106,15 +106,17 @@ TLBRet loongarch_check_pte(CPULoongArchState *env, MMUContext *context, } static TLBRet loongarch_page_table_walker(CPULoongArchState *env, - hwaddr *physical, - int *prot, vaddr address) + MMUContext *context, + int access_type, int mmu_idx) { CPUState *cs = env_cpu(env); target_ulong index, phys; uint64_t dir_base, dir_width; uint64_t base; int level; + vaddr address; + address = context->addr; if ((address >> 63) & 0x1) { base = env->CSR_PGDH; } else { @@ -156,29 +158,9 @@ static TLBRet loongarch_page_table_walker(CPULoongArchState *env, base = ldq_phys(cs->as, phys); } - /* TODO: check plv and other bits? */ - - /* base is pte, in normal pte format */ - if (!FIELD_EX64(base, TLBENTRY, V)) { - return TLBRET_NOMATCH; - } - - if (!FIELD_EX64(base, TLBENTRY, D)) { - *prot = PAGE_READ; - } else { - *prot = PAGE_READ | PAGE_WRITE; - } - - /* get TARGET_PAGE_SIZE aligned physical address */ - base += (address & TARGET_PHYS_MASK) & ((1 << dir_base) - 1); - /* mask RPLV, NX, NR bits */ - base = FIELD_DP64(base, TLBENTRY_64, RPLV, 0); - base = FIELD_DP64(base, TLBENTRY_64, NX, 0); - base = FIELD_DP64(base, TLBENTRY_64, NR, 0); - /* mask other attribute bits */ - *physical = base & TARGET_PAGE_MASK; - - return TLBRET_MATCH; + context->ps = dir_base; + context->pte = base; + return loongarch_check_pte(env, context, access_type, mmu_idx); } static TLBRet loongarch_map_address(CPULoongArchState *env, hwaddr *physical, @@ -187,7 +169,9 @@ static TLBRet loongarch_map_address(CPULoongArchState *env, hwaddr *physical, int is_debug) { TLBRet ret; + MMUContext context; + context.addr = address; if (tcg_enabled()) { ret = loongarch_get_addr_from_tlb(env, physical, prot, address, access_type, mmu_idx); @@ -202,7 +186,13 @@ static TLBRet loongarch_map_address(CPULoongArchState *env, hwaddr *physical, * legal mapping, even if the mapping is not yet in TLB. return 0 if * there is a valid map, else none zero. */ - return loongarch_page_table_walker(env, physical, prot, address); + ret = loongarch_page_table_walker(env, &context, access_type, mmu_idx); + if (ret == TLBRET_MATCH) { + *physical = context.physical; + *prot = context.prot; + } + + return ret; } return TLBRET_NOMATCH; From 36055cf414b591ce2467631dbd5d8c32d4350263 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Tue, 29 Jul 2025 16:40:25 +0800 Subject: [PATCH 0104/2396] target/loongarch: Use MMUConext in loongarch_map_tlb_entry() With function loongarch_map_tlb_entry(), parameter MMUConext is added and remove parameter physical, prot and address. Signed-off-by: Bibo Mao Reviewed-by: Richard Henderson --- target/loongarch/tcg/tlb_helper.c | 33 +++++++++++++++---------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/target/loongarch/tcg/tlb_helper.c b/target/loongarch/tcg/tlb_helper.c index 10322da62e..703ab9c8ca 100644 --- a/target/loongarch/tcg/tlb_helper.c +++ b/target/loongarch/tcg/tlb_helper.c @@ -648,28 +648,19 @@ void helper_ldpte(CPULoongArchState *env, target_ulong base, target_ulong odd, env->CSR_TLBREHI = FIELD_DP64(env->CSR_TLBREHI, CSR_TLBREHI, PS, ps); } -static TLBRet loongarch_map_tlb_entry(CPULoongArchState *env, hwaddr *physical, - int *prot, vaddr address, +static TLBRet loongarch_map_tlb_entry(CPULoongArchState *env, + MMUContext *context, MMUAccessType access_type, int index, int mmu_idx) { LoongArchTLB *tlb = &env->tlb[index]; uint8_t tlb_ps, n; - MMUContext context; - TLBRet ret; tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS); - n = (address >> tlb_ps) & 0x1;/* Odd or even */ - context.pte = n ? tlb->tlb_entry1 : tlb->tlb_entry0; - context.addr = address; - context.ps = tlb_ps; - ret = loongarch_check_pte(env, &context, access_type, mmu_idx); - if (ret == TLBRET_MATCH) { - *physical = context.physical; - *prot = context.prot; - } - - return ret; + n = (context->addr >> tlb_ps) & 0x1;/* Odd or even */ + context->pte = n ? tlb->tlb_entry1 : tlb->tlb_entry0; + context->ps = tlb_ps; + return loongarch_check_pte(env, context, access_type, mmu_idx); } TLBRet loongarch_get_addr_from_tlb(CPULoongArchState *env, hwaddr *physical, @@ -677,11 +668,19 @@ TLBRet loongarch_get_addr_from_tlb(CPULoongArchState *env, hwaddr *physical, MMUAccessType access_type, int mmu_idx) { int index, match; + MMUContext context; + TLBRet ret; + context.addr = address; match = loongarch_tlb_search(env, address, &index); if (match) { - return loongarch_map_tlb_entry(env, physical, prot, - address, access_type, index, mmu_idx); + ret = loongarch_map_tlb_entry(env, &context, access_type, index, + mmu_idx); + if (ret == TLBRET_MATCH) { + *physical = context.physical; + *prot = context.prot; + } + return ret; } return TLBRET_NOMATCH; From 35fc0ec73c3264b67ba2c8d5e39d5897dca2c891 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Tue, 29 Jul 2025 17:06:05 +0800 Subject: [PATCH 0105/2396] target/loongarch: Use MMUContext in loongarch_get_addr_from_tlb With function loongarch_get_addr_from_tlb(), parameter MMUContext is added and remove parameter physical, prot and address. Signed-off-by: Bibo Mao Reviewed-by: Richard Henderson --- target/loongarch/cpu_helper.c | 7 +++++-- target/loongarch/tcg/tcg_loongarch.h | 4 ++-- target/loongarch/tcg/tlb_helper.c | 18 +++++------------- 3 files changed, 12 insertions(+), 17 deletions(-) diff --git a/target/loongarch/cpu_helper.c b/target/loongarch/cpu_helper.c index cd61b33ef9..0cc01a0ca4 100644 --- a/target/loongarch/cpu_helper.c +++ b/target/loongarch/cpu_helper.c @@ -173,9 +173,12 @@ static TLBRet loongarch_map_address(CPULoongArchState *env, hwaddr *physical, context.addr = address; if (tcg_enabled()) { - ret = loongarch_get_addr_from_tlb(env, physical, prot, address, - access_type, mmu_idx); + ret = loongarch_get_addr_from_tlb(env, &context, access_type, mmu_idx); if (ret != TLBRET_NOMATCH) { + if (ret == TLBRET_MATCH) { + *physical = context.physical; + *prot = context.prot; + } return ret; } } diff --git a/target/loongarch/tcg/tcg_loongarch.h b/target/loongarch/tcg/tcg_loongarch.h index 488700c3c3..47702893e3 100644 --- a/target/loongarch/tcg/tcg_loongarch.h +++ b/target/loongarch/tcg/tcg_loongarch.h @@ -15,8 +15,8 @@ bool loongarch_cpu_tlb_fill(CPUState *cs, vaddr address, int size, MMUAccessType access_type, int mmu_idx, bool probe, uintptr_t retaddr); -TLBRet loongarch_get_addr_from_tlb(CPULoongArchState *env, hwaddr *physical, - int *prot, target_ulong address, +TLBRet loongarch_get_addr_from_tlb(CPULoongArchState *env, + MMUContext *context, MMUAccessType access_type, int mmu_idx); #endif /* TARGET_LOONGARCH_TCG_LOONGARCH_H */ diff --git a/target/loongarch/tcg/tlb_helper.c b/target/loongarch/tcg/tlb_helper.c index 703ab9c8ca..64a4e82dec 100644 --- a/target/loongarch/tcg/tlb_helper.c +++ b/target/loongarch/tcg/tlb_helper.c @@ -663,24 +663,16 @@ static TLBRet loongarch_map_tlb_entry(CPULoongArchState *env, return loongarch_check_pte(env, context, access_type, mmu_idx); } -TLBRet loongarch_get_addr_from_tlb(CPULoongArchState *env, hwaddr *physical, - int *prot, vaddr address, +TLBRet loongarch_get_addr_from_tlb(CPULoongArchState *env, + MMUContext *context, MMUAccessType access_type, int mmu_idx) { int index, match; - MMUContext context; - TLBRet ret; - context.addr = address; - match = loongarch_tlb_search(env, address, &index); + match = loongarch_tlb_search(env, context->addr, &index); if (match) { - ret = loongarch_map_tlb_entry(env, &context, access_type, index, - mmu_idx); - if (ret == TLBRET_MATCH) { - *physical = context.physical; - *prot = context.prot; - } - return ret; + return loongarch_map_tlb_entry(env, context, access_type, index, + mmu_idx); } return TLBRET_NOMATCH; From 4817a22edd49fc671f6efdc922826a4ce7f91017 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Tue, 29 Jul 2025 17:19:53 +0800 Subject: [PATCH 0106/2396] target/loongarch: Use MMUContext in loongarch_map_address() With function loongarch_map_address(), parameter MMUContext is added and remove parameter address, prot and address. Signed-off-by: Bibo Mao Reviewed-by: Richard Henderson --- target/loongarch/cpu_helper.c | 32 ++++++++++++++------------------ 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/target/loongarch/cpu_helper.c b/target/loongarch/cpu_helper.c index 0cc01a0ca4..225382f70e 100644 --- a/target/loongarch/cpu_helper.c +++ b/target/loongarch/cpu_helper.c @@ -163,22 +163,16 @@ static TLBRet loongarch_page_table_walker(CPULoongArchState *env, return loongarch_check_pte(env, context, access_type, mmu_idx); } -static TLBRet loongarch_map_address(CPULoongArchState *env, hwaddr *physical, - int *prot, vaddr address, +static TLBRet loongarch_map_address(CPULoongArchState *env, + MMUContext *context, MMUAccessType access_type, int mmu_idx, int is_debug) { TLBRet ret; - MMUContext context; - context.addr = address; if (tcg_enabled()) { - ret = loongarch_get_addr_from_tlb(env, &context, access_type, mmu_idx); + ret = loongarch_get_addr_from_tlb(env, context, access_type, mmu_idx); if (ret != TLBRET_NOMATCH) { - if (ret == TLBRET_MATCH) { - *physical = context.physical; - *prot = context.prot; - } return ret; } } @@ -189,13 +183,7 @@ static TLBRet loongarch_map_address(CPULoongArchState *env, hwaddr *physical, * legal mapping, even if the mapping is not yet in TLB. return 0 if * there is a valid map, else none zero. */ - ret = loongarch_page_table_walker(env, &context, access_type, mmu_idx); - if (ret == TLBRET_MATCH) { - *physical = context.physical; - *prot = context.prot; - } - - return ret; + return loongarch_page_table_walker(env, context, access_type, mmu_idx); } return TLBRET_NOMATCH; @@ -223,6 +211,8 @@ TLBRet get_physical_address(CPULoongArchState *env, hwaddr *physical, int64_t addr_high; uint8_t da = FIELD_EX64(env->CSR_CRMD, CSR_CRMD, DA); uint8_t pg = FIELD_EX64(env->CSR_CRMD, CSR_CRMD, PG); + MMUContext context; + TLBRet ret; /* Check PG and DA */ if (da & !pg) { @@ -258,8 +248,14 @@ TLBRet get_physical_address(CPULoongArchState *env, hwaddr *physical, } /* Mapped address */ - return loongarch_map_address(env, physical, prot, address, - access_type, mmu_idx, is_debug); + context.addr = address; + ret = loongarch_map_address(env, &context, + access_type, mmu_idx, is_debug); + if (ret == TLBRET_MATCH) { + *physical = context.physical; + *prot = context.prot; + } + return ret; } hwaddr loongarch_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) From f95b9702750507665f90e377b5c6c68274104024 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Tue, 29 Jul 2025 17:51:28 +0800 Subject: [PATCH 0107/2396] target/loongarch: Use MMUContext in get_physical_address() With function get_physical_address(), parameter MMUContext is added and remove parameter address, prot and address. Signed-off-by: Bibo Mao Reviewed-by: Richard Henderson --- target/loongarch/cpu-mmu.h | 3 +-- target/loongarch/cpu_helper.c | 32 ++++++++++++------------------- target/loongarch/tcg/tlb_helper.c | 8 +++++--- 3 files changed, 18 insertions(+), 25 deletions(-) diff --git a/target/loongarch/cpu-mmu.h b/target/loongarch/cpu-mmu.h index be3d11d3c1..0068d22efc 100644 --- a/target/loongarch/cpu-mmu.h +++ b/target/loongarch/cpu-mmu.h @@ -30,8 +30,7 @@ typedef struct MMUContext { bool check_ps(CPULoongArchState *ent, uint8_t ps); TLBRet loongarch_check_pte(CPULoongArchState *env, MMUContext *context, MMUAccessType access_type, int mmu_idx); -TLBRet get_physical_address(CPULoongArchState *env, hwaddr *physical, - int *prot, vaddr address, +TLBRet get_physical_address(CPULoongArchState *env, MMUContext *context, MMUAccessType access_type, int mmu_idx, int is_debug); void get_dir_base_width(CPULoongArchState *env, uint64_t *dir_base, diff --git a/target/loongarch/cpu_helper.c b/target/loongarch/cpu_helper.c index 225382f70e..4a9db3ea4c 100644 --- a/target/loongarch/cpu_helper.c +++ b/target/loongarch/cpu_helper.c @@ -200,8 +200,7 @@ static hwaddr dmw_va2pa(CPULoongArchState *env, vaddr va, target_ulong dmw) } } -TLBRet get_physical_address(CPULoongArchState *env, hwaddr *physical, - int *prot, vaddr address, +TLBRet get_physical_address(CPULoongArchState *env, MMUContext *context, MMUAccessType access_type, int mmu_idx, int is_debug) { @@ -211,13 +210,13 @@ TLBRet get_physical_address(CPULoongArchState *env, hwaddr *physical, int64_t addr_high; uint8_t da = FIELD_EX64(env->CSR_CRMD, CSR_CRMD, DA); uint8_t pg = FIELD_EX64(env->CSR_CRMD, CSR_CRMD, PG); - MMUContext context; - TLBRet ret; + vaddr address; /* Check PG and DA */ + address = context->addr; if (da & !pg) { - *physical = address & TARGET_PHYS_MASK; - *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; + context->physical = address & TARGET_PHYS_MASK; + context->prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; return TLBRET_MATCH; } @@ -235,8 +234,8 @@ TLBRet get_physical_address(CPULoongArchState *env, hwaddr *physical, base_c = FIELD_EX64(env->CSR_DMW[i], CSR_DMW_32, VSEG); } if ((plv & env->CSR_DMW[i]) && (base_c == base_v)) { - *physical = dmw_va2pa(env, address, env->CSR_DMW[i]); - *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; + context->physical = dmw_va2pa(env, address, env->CSR_DMW[i]); + context->prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; return TLBRET_MATCH; } } @@ -248,25 +247,18 @@ TLBRet get_physical_address(CPULoongArchState *env, hwaddr *physical, } /* Mapped address */ - context.addr = address; - ret = loongarch_map_address(env, &context, - access_type, mmu_idx, is_debug); - if (ret == TLBRET_MATCH) { - *physical = context.physical; - *prot = context.prot; - } - return ret; + return loongarch_map_address(env, context, access_type, mmu_idx, is_debug); } hwaddr loongarch_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) { CPULoongArchState *env = cpu_env(cs); - hwaddr phys_addr; - int prot; + MMUContext context; - if (get_physical_address(env, &phys_addr, &prot, addr, MMU_DATA_LOAD, + context.addr = addr; + if (get_physical_address(env, &context, MMU_DATA_LOAD, cpu_mmu_index(cs, false), 1) != TLBRET_MATCH) { return -1; } - return phys_addr; + return context.physical; } diff --git a/target/loongarch/tcg/tlb_helper.c b/target/loongarch/tcg/tlb_helper.c index 64a4e82dec..7d3f98633d 100644 --- a/target/loongarch/tcg/tlb_helper.c +++ b/target/loongarch/tcg/tlb_helper.c @@ -517,13 +517,15 @@ bool loongarch_cpu_tlb_fill(CPUState *cs, vaddr address, int size, CPULoongArchState *env = cpu_env(cs); hwaddr physical; int prot; + MMUContext context; TLBRet ret; /* Data access */ - ret = get_physical_address(env, &physical, &prot, address, - access_type, mmu_idx, 0); - + context.addr = address; + ret = get_physical_address(env, &context, access_type, mmu_idx, 0); if (ret == TLBRET_MATCH) { + physical = context.physical; + prot = context.prot; tlb_set_page(cs, address & TARGET_PAGE_MASK, physical & TARGET_PAGE_MASK, prot, mmu_idx, TARGET_PAGE_SIZE); From cc78259deb21940521a227619eb00a4b8e3e36c2 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Wed, 30 Jul 2025 09:47:55 +0800 Subject: [PATCH 0108/2396] target/loongarch: Use correct address when flush tlb With tlb_flush_range_by_mmuidx(), the virtual address is 64 bit. However on LoongArch TLB emulation system, virtual address is 48 bit. It is necessary to signed-extend 48 bit address to 64 bit when flush tlb, also fix address calculation issue with odd page. Signed-off-by: Bibo Mao Reviewed-by: Richard Henderson --- target/loongarch/tcg/tlb_helper.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/target/loongarch/tcg/tlb_helper.c b/target/loongarch/tcg/tlb_helper.c index 7d3f98633d..9365860c8c 100644 --- a/target/loongarch/tcg/tlb_helper.c +++ b/target/loongarch/tcg/tlb_helper.c @@ -115,16 +115,16 @@ static void invalidate_tlb_entry(CPULoongArchState *env, int index) tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS); pagesize = MAKE_64BIT_MASK(tlb_ps, 1); mask = MAKE_64BIT_MASK(0, tlb_ps + 1); + addr = (tlb_vppn << R_TLB_MISC_VPPN_SHIFT) & ~mask; + addr = sextract64(addr, 0, TARGET_VIRT_ADDR_SPACE_BITS); if (tlb_v0) { - addr = (tlb_vppn << R_TLB_MISC_VPPN_SHIFT) & ~mask; /* even */ tlb_flush_range_by_mmuidx(env_cpu(env), addr, pagesize, mmu_idx, TARGET_LONG_BITS); } if (tlb_v1) { - addr = (tlb_vppn << R_TLB_MISC_VPPN_SHIFT) & pagesize; /* odd */ - tlb_flush_range_by_mmuidx(env_cpu(env), addr, pagesize, + tlb_flush_range_by_mmuidx(env_cpu(env), addr + pagesize, pagesize, mmu_idx, TARGET_LONG_BITS); } } From e1e2909f8e74051a34a044940f90d4650b6e784a Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Thu, 28 Aug 2025 12:09:44 +0100 Subject: [PATCH 0109/2396] hw/i386/pc_piix.c: restrict isapc machine to 32-bit CPUs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The isapc machine represents a legacy ISA PC with a 486 CPU. Whilst it is possible to specify any CPU via -cpu on the command line, it makes no sense to allow modern 64-bit CPUs to be used. Restrict the isapc machine to the available 32-bit CPUs, taking care to handle the case where if a user inadvertently uses either -cpu max or -cpu host then the "best" 32-bit CPU is used (in this case the pentium3). Signed-off-by: Mark Cave-Ayland Reviewed-by: Philippe Mathieu-Daudé Link: https://lore.kernel.org/r/20250828111057.468712-2-mark.caveayland@nutanix.com Signed-off-by: Paolo Bonzini --- hw/i386/pc_piix.c | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index d165ac72ed..8f5fb3cf90 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -436,6 +436,31 @@ static void pc_set_south_bridge(Object *obj, int value, Error **errp) #ifdef CONFIG_ISAPC static void pc_init_isa(MachineState *machine) { + /* + * There is a small chance that someone unintentionally passes "-cpu max" + * for the isapc machine, which will provide a much more modern 32-bit + * CPU than would be expected for an ISA-era PC. If the "max" cpu type has + * been specified, choose the "best" 32-bit cpu possible which we consider + * be the pentium3 (deliberately choosing an Intel CPU given that the + * default 486 CPU for the isapc machine is also an Intel CPU). + */ + if (!strcmp(machine->cpu_type, X86_CPU_TYPE_NAME("max"))) { + machine->cpu_type = X86_CPU_TYPE_NAME("pentium3"); + warn_report("-cpu max is invalid for isapc machine, using pentium3"); + } + + /* + * Similarly if someone unintentionally passes "-cpu host" for the isapc + * machine then display a warning and also switch to the "best" 32-bit + * cpu possible which we consider to be the pentium3. This is because any + * host CPU will already be modern than this, but it also ensures any + * newer CPU flags/features are filtered out for older guests. + */ + if (!strcmp(machine->cpu_type, X86_CPU_TYPE_NAME("host"))) { + machine->cpu_type = X86_CPU_TYPE_NAME("pentium3"); + warn_report("-cpu host is invalid for isapc machine, using pentium3"); + } + pc_init1(machine, NULL); } #endif @@ -815,7 +840,20 @@ DEFINE_I440FX_MACHINE(2, 6); #ifdef CONFIG_ISAPC static void isapc_machine_options(MachineClass *m) { + static const char * const valid_cpu_types[] = { + X86_CPU_TYPE_NAME("486"), + X86_CPU_TYPE_NAME("athlon"), + X86_CPU_TYPE_NAME("kvm32"), + X86_CPU_TYPE_NAME("pentium"), + X86_CPU_TYPE_NAME("pentium2"), + X86_CPU_TYPE_NAME("pentium3"), + X86_CPU_TYPE_NAME("qemu32"), + X86_CPU_TYPE_NAME("max"), + X86_CPU_TYPE_NAME("host"), + NULL + }; PCMachineClass *pcmc = PC_MACHINE_CLASS(m); + m->desc = "ISA-only PC"; m->max_cpus = 1; m->option_rom_has_mr = true; @@ -828,6 +866,7 @@ static void isapc_machine_options(MachineClass *m) pcmc->has_reserved_memory = false; m->default_nic = "ne2k_isa"; m->default_cpu_type = X86_CPU_TYPE_NAME("486"); + m->valid_cpu_types = valid_cpu_types; m->no_floppy = !module_object_class_by_name(TYPE_ISA_FDC); m->no_parallel = !module_object_class_by_name(TYPE_ISA_PARALLEL); } From 483a232e0431f19a4d6596be59c1d51370407249 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Thu, 28 Aug 2025 12:09:45 +0100 Subject: [PATCH 0110/2396] hw/i386/pc_piix.c: restrict isapc machine to 3.5G memory Since the isapc machine is now limited to using 32-bit CPUs, add a hard restriction so that the machine cannot be started with more than 3.5G memory. This matches the default value for max_ram_below_4g if not specified and provides consistent behaviour betweem TCG and KVM accelerators. Signed-off-by: Mark Cave-Ayland Link: https://lore.kernel.org/r/20250828111057.468712-3-mark.caveayland@nutanix.com Signed-off-by: Paolo Bonzini --- hw/i386/pc_piix.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 8f5fb3cf90..9a3b5d88f0 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -461,6 +461,12 @@ static void pc_init_isa(MachineState *machine) warn_report("-cpu host is invalid for isapc machine, using pentium3"); } + if (machine->ram_size > 3.5 * GiB) { + error_report("Too much memory for this machine: %" PRId64 " MiB, " + "maximum 3584 MiB", machine->ram_size / MiB); + exit(1); + } + pc_init1(machine, NULL); } #endif From b55eab382cfbeb11f0afe116a06243d3fe5e43d9 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Thu, 28 Aug 2025 12:09:46 +0100 Subject: [PATCH 0111/2396] hw/i386/pc_piix.c: remove include for loader.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This header is not required since the loader functionality is handled separately by pc_memory_init() in pc.c. Signed-off-by: Mark Cave-Ayland Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Xiaoyao Li Link: https://lore.kernel.org/r/20250828111057.468712-4-mark.caveayland@nutanix.com Signed-off-by: Paolo Bonzini --- hw/i386/pc_piix.c | 1 - 1 file changed, 1 deletion(-) diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 9a3b5d88f0..351986232d 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -28,7 +28,6 @@ #include "qemu/units.h" #include "hw/char/parallel-isa.h" #include "hw/dma/i8257.h" -#include "hw/loader.h" #include "hw/i386/x86.h" #include "hw/i386/pc.h" #include "hw/i386/apic.h" From 79233a7e600ff26c623aecee81aa9a04cbbc7668 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Thu, 28 Aug 2025 12:09:47 +0100 Subject: [PATCH 0112/2396] hw/i386/pc_piix.c: inline pc_xen_hvm_init_pci() into pc_xen_hvm_init() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This helps to simplify the initialisation of the Xen hvm machine. Signed-off-by: Philippe Mathieu-Daudé Signed-off-by: Mark Cave-Ayland Reviewed-by: Xiaoyao Li Link: https://lore.kernel.org/r/20250828111057.468712-5-mark.caveayland@nutanix.com Signed-off-by: Paolo Bonzini --- hw/i386/pc_piix.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 351986232d..8e302dc013 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -471,14 +471,6 @@ static void pc_init_isa(MachineState *machine) #endif #ifdef CONFIG_XEN -static void pc_xen_hvm_init_pci(MachineState *machine) -{ - const char *pci_type = xen_igd_gfx_pt_enabled() ? - TYPE_IGD_PASSTHROUGH_I440FX_PCI_DEVICE : TYPE_I440FX_PCI_DEVICE; - - pc_init1(machine, pci_type); -} - static void pc_xen_hvm_init(MachineState *machine) { PCMachineState *pcms = PC_MACHINE(machine); @@ -488,7 +480,10 @@ static void pc_xen_hvm_init(MachineState *machine) exit(1); } - pc_xen_hvm_init_pci(machine); + pc_init1(machine, xen_igd_gfx_pt_enabled() + ? TYPE_IGD_PASSTHROUGH_I440FX_PCI_DEVICE + : TYPE_I440FX_PCI_DEVICE); + xen_igd_reserve_slot(pcms->pcibus); pci_create_simple(pcms->pcibus, -1, "xen-platform"); } From 469be2f11f7279fe9174199183cf51ba1f557e2d Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Thu, 28 Aug 2025 12:09:48 +0100 Subject: [PATCH 0113/2396] hw/i386/pc_piix.c: duplicate pc_init1() into pc_isa_init() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is to prepare for splitting the isapc machine into its own separate file. Signed-off-by: Mark Cave-Ayland Reviewed-by: Philippe Mathieu-Daudé Link: https://lore.kernel.org/r/20250828111057.468712-6-mark.caveayland@nutanix.com Signed-off-by: Paolo Bonzini --- hw/i386/pc_piix.c | 275 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 274 insertions(+), 1 deletion(-) diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 8e302dc013..60bf18c680 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -435,6 +435,23 @@ static void pc_set_south_bridge(Object *obj, int value, Error **errp) #ifdef CONFIG_ISAPC static void pc_init_isa(MachineState *machine) { + const char *pci_type = NULL; + PCMachineState *pcms = PC_MACHINE(machine); + PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms); + X86MachineState *x86ms = X86_MACHINE(machine); + MemoryRegion *system_memory = get_system_memory(); + MemoryRegion *system_io = get_system_io(); + Object *phb = NULL; + ISABus *isa_bus; + Object *piix4_pm = NULL; + qemu_irq smi_irq; + GSIState *gsi_state; + MemoryRegion *ram_memory; + MemoryRegion *pci_memory = NULL; + MemoryRegion *rom_memory = system_memory; + ram_addr_t lowmem; + uint64_t hole64_size = 0; + /* * There is a small chance that someone unintentionally passes "-cpu max" * for the isapc machine, which will provide a much more modern 32-bit @@ -466,7 +483,263 @@ static void pc_init_isa(MachineState *machine) exit(1); } - pc_init1(machine, NULL); + /* + * Calculate ram split, for memory below and above 4G. It's a bit + * complicated for backward compatibility reasons ... + * + * - Traditional split is 3.5G (lowmem = 0xe0000000). This is the + * default value for max_ram_below_4g now. + * + * - Then, to gigabyte align the memory, we move the split to 3G + * (lowmem = 0xc0000000). But only in case we have to split in + * the first place, i.e. ram_size is larger than (traditional) + * lowmem. And for new machine types (gigabyte_align = true) + * only, for live migration compatibility reasons. + * + * - Next the max-ram-below-4g option was added, which allowed to + * reduce lowmem to a smaller value, to allow a larger PCI I/O + * window below 4G. qemu doesn't enforce gigabyte alignment here, + * but prints a warning. + * + * - Finally max-ram-below-4g got updated to also allow raising lowmem, + * so legacy non-PAE guests can get as much memory as possible in + * the 32bit address space below 4G. + * + * - Note that Xen has its own ram setup code in xen_ram_init(), + * called via xen_hvm_init_pc(). + * + * Examples: + * qemu -M pc-1.7 -m 4G (old default) -> 3584M low, 512M high + * qemu -M pc -m 4G (new default) -> 3072M low, 1024M high + * qemu -M pc,max-ram-below-4g=2G -m 4G -> 2048M low, 2048M high + * qemu -M pc,max-ram-below-4g=4G -m 3968M -> 3968M low (=4G-128M) + */ + if (xen_enabled()) { + xen_hvm_init_pc(pcms, &ram_memory); + } else { + ram_memory = machine->ram; + if (!pcms->max_ram_below_4g) { + pcms->max_ram_below_4g = 0xe0000000; /* default: 3.5G */ + } + lowmem = pcms->max_ram_below_4g; + if (machine->ram_size >= pcms->max_ram_below_4g) { + if (pcmc->gigabyte_align) { + if (lowmem > 0xc0000000) { + lowmem = 0xc0000000; + } + if (lowmem & (1 * GiB - 1)) { + warn_report("Large machine and max_ram_below_4g " + "(%" PRIu64 ") not a multiple of 1G; " + "possible bad performance.", + pcms->max_ram_below_4g); + } + } + } + + if (machine->ram_size >= lowmem) { + x86ms->above_4g_mem_size = machine->ram_size - lowmem; + x86ms->below_4g_mem_size = lowmem; + } else { + x86ms->above_4g_mem_size = 0; + x86ms->below_4g_mem_size = machine->ram_size; + } + } + + pc_machine_init_sgx_epc(pcms); + x86_cpus_init(x86ms, pcmc->default_cpu_version); + + if (kvm_enabled()) { + kvmclock_create(pcmc->kvmclock_create_always); + } + + if (pcmc->pci_enabled) { + pci_memory = g_new(MemoryRegion, 1); + memory_region_init(pci_memory, NULL, "pci", UINT64_MAX); + rom_memory = pci_memory; + + phb = OBJECT(qdev_new(TYPE_I440FX_PCI_HOST_BRIDGE)); + object_property_add_child(OBJECT(machine), "i440fx", phb); + object_property_set_link(phb, PCI_HOST_PROP_RAM_MEM, + OBJECT(ram_memory), &error_fatal); + object_property_set_link(phb, PCI_HOST_PROP_PCI_MEM, + OBJECT(pci_memory), &error_fatal); + object_property_set_link(phb, PCI_HOST_PROP_SYSTEM_MEM, + OBJECT(system_memory), &error_fatal); + object_property_set_link(phb, PCI_HOST_PROP_IO_MEM, + OBJECT(system_io), &error_fatal); + object_property_set_uint(phb, PCI_HOST_BELOW_4G_MEM_SIZE, + x86ms->below_4g_mem_size, &error_fatal); + object_property_set_uint(phb, PCI_HOST_ABOVE_4G_MEM_SIZE, + x86ms->above_4g_mem_size, &error_fatal); + object_property_set_str(phb, I440FX_HOST_PROP_PCI_TYPE, pci_type, + &error_fatal); + sysbus_realize_and_unref(SYS_BUS_DEVICE(phb), &error_fatal); + + pcms->pcibus = PCI_BUS(qdev_get_child_bus(DEVICE(phb), "pci.0")); + pci_bus_map_irqs(pcms->pcibus, + xen_enabled() ? xen_pci_slot_get_pirq + : pc_pci_slot_get_pirq); + + hole64_size = object_property_get_uint(phb, + PCI_HOST_PROP_PCI_HOLE64_SIZE, + &error_abort); + } + + /* allocate ram and load rom/bios */ + if (!xen_enabled()) { + pc_memory_init(pcms, system_memory, rom_memory, hole64_size); + } else { + assert(machine->ram_size == x86ms->below_4g_mem_size + + x86ms->above_4g_mem_size); + + pc_system_flash_cleanup_unused(pcms); + if (machine->kernel_filename != NULL) { + /* For xen HVM direct kernel boot, load linux here */ + xen_load_linux(pcms); + } + } + + gsi_state = pc_gsi_create(&x86ms->gsi, pcmc->pci_enabled); + + if (pcmc->pci_enabled) { + PCIDevice *pci_dev; + DeviceState *dev; + size_t i; + + pci_dev = pci_new_multifunction(-1, pcms->south_bridge); + object_property_set_bool(OBJECT(pci_dev), "has-usb", + machine_usb(machine), &error_abort); + object_property_set_bool(OBJECT(pci_dev), "has-acpi", + x86_machine_is_acpi_enabled(x86ms), + &error_abort); + object_property_set_bool(OBJECT(pci_dev), "has-pic", false, + &error_abort); + object_property_set_bool(OBJECT(pci_dev), "has-pit", false, + &error_abort); + qdev_prop_set_uint32(DEVICE(pci_dev), "smb_io_base", 0xb100); + object_property_set_bool(OBJECT(pci_dev), "smm-enabled", + x86_machine_is_smm_enabled(x86ms), + &error_abort); + dev = DEVICE(pci_dev); + for (i = 0; i < ISA_NUM_IRQS; i++) { + qdev_connect_gpio_out_named(dev, "isa-irqs", i, x86ms->gsi[i]); + } + pci_realize_and_unref(pci_dev, pcms->pcibus, &error_fatal); + + if (xen_enabled()) { + pci_device_set_intx_routing_notifier( + pci_dev, piix_intx_routing_notifier_xen); + + /* + * Xen supports additional interrupt routes from the PCI devices to + * the IOAPIC: the four pins of each PCI device on the bus are also + * connected to the IOAPIC directly. + * These additional routes can be discovered through ACPI. + */ + pci_bus_irqs(pcms->pcibus, xen_intx_set_irq, pci_dev, + XEN_IOAPIC_NUM_PIRQS); + } + + isa_bus = ISA_BUS(qdev_get_child_bus(DEVICE(pci_dev), "isa.0")); + x86ms->rtc = ISA_DEVICE(object_resolve_path_component(OBJECT(pci_dev), + "rtc")); + piix4_pm = object_resolve_path_component(OBJECT(pci_dev), "pm"); + dev = DEVICE(object_resolve_path_component(OBJECT(pci_dev), "ide")); + pci_ide_create_devs(PCI_DEVICE(dev)); + pcms->idebus[0] = qdev_get_child_bus(dev, "ide.0"); + pcms->idebus[1] = qdev_get_child_bus(dev, "ide.1"); + } else { + uint32_t irq; + + isa_bus = isa_bus_new(NULL, system_memory, system_io, + &error_abort); + isa_bus_register_input_irqs(isa_bus, x86ms->gsi); + + x86ms->rtc = isa_new(TYPE_MC146818_RTC); + qdev_prop_set_int32(DEVICE(x86ms->rtc), "base_year", 2000); + isa_realize_and_unref(x86ms->rtc, isa_bus, &error_fatal); + irq = object_property_get_uint(OBJECT(x86ms->rtc), "irq", + &error_fatal); + isa_connect_gpio_out(ISA_DEVICE(x86ms->rtc), 0, irq); + + i8257_dma_init(OBJECT(machine), isa_bus, 0); + pcms->hpet_enabled = false; + } + + if (x86ms->pic == ON_OFF_AUTO_ON || x86ms->pic == ON_OFF_AUTO_AUTO) { + pc_i8259_create(isa_bus, gsi_state->i8259_irq); + } + + if (phb) { + ioapic_init_gsi(gsi_state, phb); + } + + if (tcg_enabled()) { + x86_register_ferr_irq(x86ms->gsi[13]); + } + + pc_vga_init(isa_bus, pcmc->pci_enabled ? pcms->pcibus : NULL); + + /* init basic PC hardware */ + pc_basic_device_init(pcms, isa_bus, x86ms->gsi, x86ms->rtc, + !MACHINE_CLASS(pcmc)->no_floppy, 0x4); + + pc_nic_init(pcmc, isa_bus, pcms->pcibus); + +#ifdef CONFIG_IDE_ISA + if (!pcmc->pci_enabled) { + DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS]; + int i; + + ide_drive_get(hd, ARRAY_SIZE(hd)); + for (i = 0; i < MAX_IDE_BUS; i++) { + ISADevice *dev; + char busname[] = "ide.0"; + dev = isa_ide_init(isa_bus, ide_iobase[i], ide_iobase2[i], + ide_irq[i], + hd[MAX_IDE_DEVS * i], hd[MAX_IDE_DEVS * i + 1]); + /* + * The ide bus name is ide.0 for the first bus and ide.1 for the + * second one. + */ + busname[4] = '0' + i; + pcms->idebus[i] = qdev_get_child_bus(DEVICE(dev), busname); + } + } +#endif + + if (piix4_pm) { + smi_irq = qemu_allocate_irq(pc_acpi_smi_interrupt, first_cpu, 0); + + qdev_connect_gpio_out_named(DEVICE(piix4_pm), "smi-irq", 0, smi_irq); + pcms->smbus = I2C_BUS(qdev_get_child_bus(DEVICE(piix4_pm), "i2c")); + /* TODO: Populate SPD eeprom data. */ + smbus_eeprom_init(pcms->smbus, 8, NULL, 0); + + object_property_add_link(OBJECT(machine), PC_MACHINE_ACPI_DEVICE_PROP, + TYPE_HOTPLUG_HANDLER, + (Object **)&x86ms->acpi_dev, + object_property_allow_set_link, + OBJ_PROP_LINK_STRONG); + object_property_set_link(OBJECT(machine), PC_MACHINE_ACPI_DEVICE_PROP, + piix4_pm, &error_abort); + } + + if (machine->nvdimms_state->is_enabled) { + nvdimm_init_acpi_state(machine->nvdimms_state, system_io, + x86_nvdimm_acpi_dsmio, + x86ms->fw_cfg, OBJECT(pcms)); + } + +#if defined(CONFIG_IGVM) + /* Apply guest state from IGVM if supplied */ + if (x86ms->igvm) { + if (IGVM_CFG_GET_CLASS(x86ms->igvm) + ->process(x86ms->igvm, machine->cgs, false, &error_fatal) < 0) { + g_assert_not_reached(); + } + } +#endif } #endif From ba5500e0385fad2dd1d4878872695023e5e32e92 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Thu, 28 Aug 2025 12:09:49 +0100 Subject: [PATCH 0114/2396] hw/i386/pc_piix.c: remove pcmc->pci_enabled dependent initialisation from pc_init_isa() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PCI code will never be used for an isapc machine. Signed-off-by: Mark Cave-Ayland Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Xiaoyao Li Link: https://lore.kernel.org/r/20250828111057.468712-7-mark.caveayland@nutanix.com Signed-off-by: Paolo Bonzini --- hw/i386/pc_piix.c | 120 ++++++---------------------------------------- 1 file changed, 15 insertions(+), 105 deletions(-) diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 60bf18c680..f1b4468d0a 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -435,19 +435,17 @@ static void pc_set_south_bridge(Object *obj, int value, Error **errp) #ifdef CONFIG_ISAPC static void pc_init_isa(MachineState *machine) { - const char *pci_type = NULL; PCMachineState *pcms = PC_MACHINE(machine); PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms); X86MachineState *x86ms = X86_MACHINE(machine); MemoryRegion *system_memory = get_system_memory(); MemoryRegion *system_io = get_system_io(); - Object *phb = NULL; ISABus *isa_bus; Object *piix4_pm = NULL; qemu_irq smi_irq; + uint32_t irq; GSIState *gsi_state; MemoryRegion *ram_memory; - MemoryRegion *pci_memory = NULL; MemoryRegion *rom_memory = system_memory; ram_addr_t lowmem; uint64_t hole64_size = 0; @@ -552,39 +550,6 @@ static void pc_init_isa(MachineState *machine) kvmclock_create(pcmc->kvmclock_create_always); } - if (pcmc->pci_enabled) { - pci_memory = g_new(MemoryRegion, 1); - memory_region_init(pci_memory, NULL, "pci", UINT64_MAX); - rom_memory = pci_memory; - - phb = OBJECT(qdev_new(TYPE_I440FX_PCI_HOST_BRIDGE)); - object_property_add_child(OBJECT(machine), "i440fx", phb); - object_property_set_link(phb, PCI_HOST_PROP_RAM_MEM, - OBJECT(ram_memory), &error_fatal); - object_property_set_link(phb, PCI_HOST_PROP_PCI_MEM, - OBJECT(pci_memory), &error_fatal); - object_property_set_link(phb, PCI_HOST_PROP_SYSTEM_MEM, - OBJECT(system_memory), &error_fatal); - object_property_set_link(phb, PCI_HOST_PROP_IO_MEM, - OBJECT(system_io), &error_fatal); - object_property_set_uint(phb, PCI_HOST_BELOW_4G_MEM_SIZE, - x86ms->below_4g_mem_size, &error_fatal); - object_property_set_uint(phb, PCI_HOST_ABOVE_4G_MEM_SIZE, - x86ms->above_4g_mem_size, &error_fatal); - object_property_set_str(phb, I440FX_HOST_PROP_PCI_TYPE, pci_type, - &error_fatal); - sysbus_realize_and_unref(SYS_BUS_DEVICE(phb), &error_fatal); - - pcms->pcibus = PCI_BUS(qdev_get_child_bus(DEVICE(phb), "pci.0")); - pci_bus_map_irqs(pcms->pcibus, - xen_enabled() ? xen_pci_slot_get_pirq - : pc_pci_slot_get_pirq); - - hole64_size = object_property_get_uint(phb, - PCI_HOST_PROP_PCI_HOLE64_SIZE, - &error_abort); - } - /* allocate ram and load rom/bios */ if (!xen_enabled()) { pc_memory_init(pcms, system_memory, rom_memory, hole64_size); @@ -599,92 +564,37 @@ static void pc_init_isa(MachineState *machine) } } - gsi_state = pc_gsi_create(&x86ms->gsi, pcmc->pci_enabled); + gsi_state = pc_gsi_create(&x86ms->gsi, false); - if (pcmc->pci_enabled) { - PCIDevice *pci_dev; - DeviceState *dev; - size_t i; + isa_bus = isa_bus_new(NULL, system_memory, system_io, + &error_abort); + isa_bus_register_input_irqs(isa_bus, x86ms->gsi); - pci_dev = pci_new_multifunction(-1, pcms->south_bridge); - object_property_set_bool(OBJECT(pci_dev), "has-usb", - machine_usb(machine), &error_abort); - object_property_set_bool(OBJECT(pci_dev), "has-acpi", - x86_machine_is_acpi_enabled(x86ms), - &error_abort); - object_property_set_bool(OBJECT(pci_dev), "has-pic", false, - &error_abort); - object_property_set_bool(OBJECT(pci_dev), "has-pit", false, - &error_abort); - qdev_prop_set_uint32(DEVICE(pci_dev), "smb_io_base", 0xb100); - object_property_set_bool(OBJECT(pci_dev), "smm-enabled", - x86_machine_is_smm_enabled(x86ms), - &error_abort); - dev = DEVICE(pci_dev); - for (i = 0; i < ISA_NUM_IRQS; i++) { - qdev_connect_gpio_out_named(dev, "isa-irqs", i, x86ms->gsi[i]); - } - pci_realize_and_unref(pci_dev, pcms->pcibus, &error_fatal); + x86ms->rtc = isa_new(TYPE_MC146818_RTC); + qdev_prop_set_int32(DEVICE(x86ms->rtc), "base_year", 2000); + isa_realize_and_unref(x86ms->rtc, isa_bus, &error_fatal); + irq = object_property_get_uint(OBJECT(x86ms->rtc), "irq", + &error_fatal); + isa_connect_gpio_out(ISA_DEVICE(x86ms->rtc), 0, irq); - if (xen_enabled()) { - pci_device_set_intx_routing_notifier( - pci_dev, piix_intx_routing_notifier_xen); - - /* - * Xen supports additional interrupt routes from the PCI devices to - * the IOAPIC: the four pins of each PCI device on the bus are also - * connected to the IOAPIC directly. - * These additional routes can be discovered through ACPI. - */ - pci_bus_irqs(pcms->pcibus, xen_intx_set_irq, pci_dev, - XEN_IOAPIC_NUM_PIRQS); - } - - isa_bus = ISA_BUS(qdev_get_child_bus(DEVICE(pci_dev), "isa.0")); - x86ms->rtc = ISA_DEVICE(object_resolve_path_component(OBJECT(pci_dev), - "rtc")); - piix4_pm = object_resolve_path_component(OBJECT(pci_dev), "pm"); - dev = DEVICE(object_resolve_path_component(OBJECT(pci_dev), "ide")); - pci_ide_create_devs(PCI_DEVICE(dev)); - pcms->idebus[0] = qdev_get_child_bus(dev, "ide.0"); - pcms->idebus[1] = qdev_get_child_bus(dev, "ide.1"); - } else { - uint32_t irq; - - isa_bus = isa_bus_new(NULL, system_memory, system_io, - &error_abort); - isa_bus_register_input_irqs(isa_bus, x86ms->gsi); - - x86ms->rtc = isa_new(TYPE_MC146818_RTC); - qdev_prop_set_int32(DEVICE(x86ms->rtc), "base_year", 2000); - isa_realize_and_unref(x86ms->rtc, isa_bus, &error_fatal); - irq = object_property_get_uint(OBJECT(x86ms->rtc), "irq", - &error_fatal); - isa_connect_gpio_out(ISA_DEVICE(x86ms->rtc), 0, irq); - - i8257_dma_init(OBJECT(machine), isa_bus, 0); - pcms->hpet_enabled = false; - } + i8257_dma_init(OBJECT(machine), isa_bus, 0); + pcms->hpet_enabled = false; if (x86ms->pic == ON_OFF_AUTO_ON || x86ms->pic == ON_OFF_AUTO_AUTO) { pc_i8259_create(isa_bus, gsi_state->i8259_irq); } - if (phb) { - ioapic_init_gsi(gsi_state, phb); - } - if (tcg_enabled()) { x86_register_ferr_irq(x86ms->gsi[13]); } - pc_vga_init(isa_bus, pcmc->pci_enabled ? pcms->pcibus : NULL); + pc_vga_init(isa_bus, NULL); /* init basic PC hardware */ pc_basic_device_init(pcms, isa_bus, x86ms->gsi, x86ms->rtc, !MACHINE_CLASS(pcmc)->no_floppy, 0x4); - pc_nic_init(pcmc, isa_bus, pcms->pcibus); + pc_nic_init(pcmc, isa_bus, NULL); #ifdef CONFIG_IDE_ISA if (!pcmc->pci_enabled) { From dc58530f0a58fe09862026ab6c26c68c00f4d535 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Thu, 28 Aug 2025 12:09:50 +0100 Subject: [PATCH 0115/2396] hw/i386/pc_piix.c: remove igvm initialisation from pc_init_isa() According to the QEMU documentation igvm is only supported for the pc and q35 machines so remove igvm support from the isapc machine. Signed-off-by: Mark Cave-Ayland Link: https://lore.kernel.org/r/20250828111057.468712-8-mark.caveayland@nutanix.com Signed-off-by: Paolo Bonzini --- hw/i386/pc_piix.c | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index f1b4468d0a..5ae265bd53 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -640,16 +640,6 @@ static void pc_init_isa(MachineState *machine) x86_nvdimm_acpi_dsmio, x86ms->fw_cfg, OBJECT(pcms)); } - -#if defined(CONFIG_IGVM) - /* Apply guest state from IGVM if supplied */ - if (x86ms->igvm) { - if (IGVM_CFG_GET_CLASS(x86ms->igvm) - ->process(x86ms->igvm, machine->cgs, false, &error_fatal) < 0) { - g_assert_not_reached(); - } - } -#endif } #endif From ae4199af92d38fffd0d1fb9f02eaeb0632eff6df Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Thu, 28 Aug 2025 12:09:51 +0100 Subject: [PATCH 0116/2396] hw/i386/pc_piix.c: remove SMI and piix4_pm initialisation from pc_init_isa() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These are based upon the PIIX4 PCI chipset and so can never be used on an isapc machine. Signed-off-by: Mark Cave-Ayland Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Xiaoyao Li Link: https://lore.kernel.org/r/20250828111057.468712-9-mark.caveayland@nutanix.com Signed-off-by: Paolo Bonzini --- hw/i386/pc_piix.c | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 5ae265bd53..57b02da5a8 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -441,8 +441,6 @@ static void pc_init_isa(MachineState *machine) MemoryRegion *system_memory = get_system_memory(); MemoryRegion *system_io = get_system_io(); ISABus *isa_bus; - Object *piix4_pm = NULL; - qemu_irq smi_irq; uint32_t irq; GSIState *gsi_state; MemoryRegion *ram_memory; @@ -618,23 +616,6 @@ static void pc_init_isa(MachineState *machine) } #endif - if (piix4_pm) { - smi_irq = qemu_allocate_irq(pc_acpi_smi_interrupt, first_cpu, 0); - - qdev_connect_gpio_out_named(DEVICE(piix4_pm), "smi-irq", 0, smi_irq); - pcms->smbus = I2C_BUS(qdev_get_child_bus(DEVICE(piix4_pm), "i2c")); - /* TODO: Populate SPD eeprom data. */ - smbus_eeprom_init(pcms->smbus, 8, NULL, 0); - - object_property_add_link(OBJECT(machine), PC_MACHINE_ACPI_DEVICE_PROP, - TYPE_HOTPLUG_HANDLER, - (Object **)&x86ms->acpi_dev, - object_property_allow_set_link, - OBJ_PROP_LINK_STRONG); - object_property_set_link(OBJECT(machine), PC_MACHINE_ACPI_DEVICE_PROP, - piix4_pm, &error_abort); - } - if (machine->nvdimms_state->is_enabled) { nvdimm_init_acpi_state(machine->nvdimms_state, system_io, x86_nvdimm_acpi_dsmio, From d7916f6d5ec346d05ef63f5419d97a4d9f7d0a75 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Thu, 28 Aug 2025 12:09:52 +0100 Subject: [PATCH 0117/2396] hw/i386/pc_piix.c: remove SGX initialisation from pc_init_isa() The Intel SGX instructions only exist on recent CPUs and so would never be available on a CPU from the pre-PCI era. Signed-off-by: Mark Cave-Ayland Reviewed-by: Xiaoyao Li Link: https://lore.kernel.org/r/20250828111057.468712-10-mark.caveayland@nutanix.com Signed-off-by: Paolo Bonzini --- hw/i386/pc_piix.c | 1 - 1 file changed, 1 deletion(-) diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 57b02da5a8..9a2eee8ab0 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -541,7 +541,6 @@ static void pc_init_isa(MachineState *machine) } } - pc_machine_init_sgx_epc(pcms); x86_cpus_init(x86ms, pcmc->default_cpu_version); if (kvm_enabled()) { From 62f8d562bb86cd7e1a0ce06f191c402a1eeba309 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Thu, 28 Aug 2025 12:09:53 +0100 Subject: [PATCH 0118/2396] hw/i386/pc_piix.c: remove nvdimm initialisation from pc_init_isa() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit NVDIMMs cannot be used by PCs from a pre-PCI era. Signed-off-by: Mark Cave-Ayland Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Xiaoyao Li Link: https://lore.kernel.org/r/20250828111057.468712-11-mark.caveayland@nutanix.com Signed-off-by: Paolo Bonzini --- hw/i386/pc_piix.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 9a2eee8ab0..daf63a326b 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -614,12 +614,6 @@ static void pc_init_isa(MachineState *machine) } } #endif - - if (machine->nvdimms_state->is_enabled) { - nvdimm_init_acpi_state(machine->nvdimms_state, system_io, - x86_nvdimm_acpi_dsmio, - x86ms->fw_cfg, OBJECT(pcms)); - } } #endif From f2096fa151cbdf6cd169a6f0be9c5ccb5cd10466 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Thu, 28 Aug 2025 12:09:54 +0100 Subject: [PATCH 0119/2396] hw/i386/pc_piix.c: simplify RAM size logic in pc_init_isa() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All isapc machines must have 32-bit CPUs and so the RAM split logic can be hardcoded accordingly. Signed-off-by: Mark Cave-Ayland Reviewed-by: Philippe Mathieu-Daudé Link: https://lore.kernel.org/r/20250828111057.468712-12-mark.caveayland@nutanix.com Signed-off-by: Paolo Bonzini --- hw/i386/pc_piix.c | 58 ++++------------------------------------------- 1 file changed, 4 insertions(+), 54 deletions(-) diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index daf63a326b..0bc033943c 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -445,7 +445,6 @@ static void pc_init_isa(MachineState *machine) GSIState *gsi_state; MemoryRegion *ram_memory; MemoryRegion *rom_memory = system_memory; - ram_addr_t lowmem; uint64_t hole64_size = 0; /* @@ -480,65 +479,16 @@ static void pc_init_isa(MachineState *machine) } /* - * Calculate ram split, for memory below and above 4G. It's a bit - * complicated for backward compatibility reasons ... - * - * - Traditional split is 3.5G (lowmem = 0xe0000000). This is the - * default value for max_ram_below_4g now. - * - * - Then, to gigabyte align the memory, we move the split to 3G - * (lowmem = 0xc0000000). But only in case we have to split in - * the first place, i.e. ram_size is larger than (traditional) - * lowmem. And for new machine types (gigabyte_align = true) - * only, for live migration compatibility reasons. - * - * - Next the max-ram-below-4g option was added, which allowed to - * reduce lowmem to a smaller value, to allow a larger PCI I/O - * window below 4G. qemu doesn't enforce gigabyte alignment here, - * but prints a warning. - * - * - Finally max-ram-below-4g got updated to also allow raising lowmem, - * so legacy non-PAE guests can get as much memory as possible in - * the 32bit address space below 4G. - * - * - Note that Xen has its own ram setup code in xen_ram_init(), - * called via xen_hvm_init_pc(). - * - * Examples: - * qemu -M pc-1.7 -m 4G (old default) -> 3584M low, 512M high - * qemu -M pc -m 4G (new default) -> 3072M low, 1024M high - * qemu -M pc,max-ram-below-4g=2G -m 4G -> 2048M low, 2048M high - * qemu -M pc,max-ram-below-4g=4G -m 3968M -> 3968M low (=4G-128M) + * There is no RAM split for the isapc machine */ if (xen_enabled()) { xen_hvm_init_pc(pcms, &ram_memory); } else { ram_memory = machine->ram; - if (!pcms->max_ram_below_4g) { - pcms->max_ram_below_4g = 0xe0000000; /* default: 3.5G */ - } - lowmem = pcms->max_ram_below_4g; - if (machine->ram_size >= pcms->max_ram_below_4g) { - if (pcmc->gigabyte_align) { - if (lowmem > 0xc0000000) { - lowmem = 0xc0000000; - } - if (lowmem & (1 * GiB - 1)) { - warn_report("Large machine and max_ram_below_4g " - "(%" PRIu64 ") not a multiple of 1G; " - "possible bad performance.", - pcms->max_ram_below_4g); - } - } - } - if (machine->ram_size >= lowmem) { - x86ms->above_4g_mem_size = machine->ram_size - lowmem; - x86ms->below_4g_mem_size = lowmem; - } else { - x86ms->above_4g_mem_size = 0; - x86ms->below_4g_mem_size = machine->ram_size; - } + pcms->max_ram_below_4g = 3.5 * GiB; + x86ms->above_4g_mem_size = 0; + x86ms->below_4g_mem_size = machine->ram_size; } x86_cpus_init(x86ms, pcmc->default_cpu_version); From 20fd284ec3c6ec39fb8e062d52f1ee514e89c554 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Thu, 28 Aug 2025 12:09:55 +0100 Subject: [PATCH 0120/2396] hw/i386/pc_piix.c: hardcode hole64_size to 0 in pc_init_isa() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All isapc machines must have 32-bit CPUs and have no PCI 64-bit hole so it can be hardcoded to 0. Signed-off-by: Mark Cave-Ayland Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Xiaoyao Li Link: https://lore.kernel.org/r/20250828111057.468712-13-mark.caveayland@nutanix.com Signed-off-by: Paolo Bonzini --- hw/i386/pc_piix.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 0bc033943c..66dc4a5186 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -445,7 +445,6 @@ static void pc_init_isa(MachineState *machine) GSIState *gsi_state; MemoryRegion *ram_memory; MemoryRegion *rom_memory = system_memory; - uint64_t hole64_size = 0; /* * There is a small chance that someone unintentionally passes "-cpu max" @@ -499,7 +498,7 @@ static void pc_init_isa(MachineState *machine) /* allocate ram and load rom/bios */ if (!xen_enabled()) { - pc_memory_init(pcms, system_memory, rom_memory, hole64_size); + pc_memory_init(pcms, system_memory, rom_memory, 0); } else { assert(machine->ram_size == x86ms->below_4g_mem_size + x86ms->above_4g_mem_size); From b11ad71e32441baff354cdab1993847b61923570 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Thu, 28 Aug 2025 12:09:56 +0100 Subject: [PATCH 0121/2396] hw/i386/pc_piix.c: remove pc_system_flash_cleanup_unused() from pc_init_isa() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This function contains 'assert(PC_MACHINE_GET_CLASS(pcms)->pci_enabled)' and so we can safely assume that it should never be used for the isapc machine. Signed-off-by: Mark Cave-Ayland Reviewed-by: Philippe Mathieu-Daudé Link: https://lore.kernel.org/r/20250828111057.468712-14-mark.caveayland@nutanix.com Signed-off-by: Paolo Bonzini --- hw/i386/pc_piix.c | 1 - 1 file changed, 1 deletion(-) diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 66dc4a5186..fb936748bd 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -503,7 +503,6 @@ static void pc_init_isa(MachineState *machine) assert(machine->ram_size == x86ms->below_4g_mem_size + x86ms->above_4g_mem_size); - pc_system_flash_cleanup_unused(pcms); if (machine->kernel_filename != NULL) { /* For xen HVM direct kernel boot, load linux here */ xen_load_linux(pcms); From 32c73eb73c7c09290a642c0c3ea541ce00350994 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Thu, 28 Aug 2025 12:09:57 +0100 Subject: [PATCH 0122/2396] hw/i386/pc_piix.c: always initialise ISA IDE drives in pc_init_isa() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit By definition an isapc machine must always use ISA IDE drives so ensure that they are always enabled. At the same time also remove the surrounding CONFIG_IDE_ISA define since it will be enabled via the ISAPC Kconfig. Signed-off-by: Mark Cave-Ayland Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Xiaoyao Li Link: https://lore.kernel.org/r/20250828111057.468712-15-mark.caveayland@nutanix.com Signed-off-by: Paolo Bonzini --- hw/i386/pc_piix.c | 35 +++++++++++++++-------------------- 1 file changed, 15 insertions(+), 20 deletions(-) diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index fb936748bd..72ddd9b149 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -445,6 +445,8 @@ static void pc_init_isa(MachineState *machine) GSIState *gsi_state; MemoryRegion *ram_memory; MemoryRegion *rom_memory = system_memory; + DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS]; + int i; /* * There is a small chance that someone unintentionally passes "-cpu max" @@ -541,27 +543,20 @@ static void pc_init_isa(MachineState *machine) pc_nic_init(pcmc, isa_bus, NULL); -#ifdef CONFIG_IDE_ISA - if (!pcmc->pci_enabled) { - DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS]; - int i; - - ide_drive_get(hd, ARRAY_SIZE(hd)); - for (i = 0; i < MAX_IDE_BUS; i++) { - ISADevice *dev; - char busname[] = "ide.0"; - dev = isa_ide_init(isa_bus, ide_iobase[i], ide_iobase2[i], - ide_irq[i], - hd[MAX_IDE_DEVS * i], hd[MAX_IDE_DEVS * i + 1]); - /* - * The ide bus name is ide.0 for the first bus and ide.1 for the - * second one. - */ - busname[4] = '0' + i; - pcms->idebus[i] = qdev_get_child_bus(DEVICE(dev), busname); - } + ide_drive_get(hd, ARRAY_SIZE(hd)); + for (i = 0; i < MAX_IDE_BUS; i++) { + ISADevice *dev; + char busname[] = "ide.0"; + dev = isa_ide_init(isa_bus, ide_iobase[i], ide_iobase2[i], + ide_irq[i], + hd[MAX_IDE_DEVS * i], hd[MAX_IDE_DEVS * i + 1]); + /* + * The ide bus name is ide.0 for the first bus and ide.1 for the + * second one. + */ + busname[4] = '0' + i; + pcms->idebus[i] = qdev_get_child_bus(DEVICE(dev), busname); } -#endif } #endif From 99d0630a454581eeb2bfabdc0bc15cc07d145876 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Thu, 28 Aug 2025 12:09:58 +0100 Subject: [PATCH 0123/2396] hw/i386/pc_piix.c: assume pcmc->pci_enabled is always true in pc_init1() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PCI is always enabled on the pc-i440fx machine so hardcode the relevant logic in pc_init1(). Add an assert() to ensure that this is always the case at runtime as already done in pc_q35_init(). Signed-off-by: Mark Cave-Ayland Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Xiaoyao Li Link: https://lore.kernel.org/r/20250828111057.468712-16-mark.caveayland@nutanix.com Signed-off-by: Paolo Bonzini --- hw/i386/pc_piix.c | 192 ++++++++++++++++++---------------------------- 1 file changed, 76 insertions(+), 116 deletions(-) diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 72ddd9b149..3ea77b2c44 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -125,6 +125,11 @@ static void pc_init1(MachineState *machine, const char *pci_type) MemoryRegion *rom_memory = system_memory; ram_addr_t lowmem; uint64_t hole64_size = 0; + PCIDevice *pci_dev; + DeviceState *dev; + size_t i; + + assert(pcmc->pci_enabled); /* * Calculate ram split, for memory below and above 4G. It's a bit @@ -195,38 +200,36 @@ static void pc_init1(MachineState *machine, const char *pci_type) kvmclock_create(pcmc->kvmclock_create_always); } - if (pcmc->pci_enabled) { - pci_memory = g_new(MemoryRegion, 1); - memory_region_init(pci_memory, NULL, "pci", UINT64_MAX); - rom_memory = pci_memory; + pci_memory = g_new(MemoryRegion, 1); + memory_region_init(pci_memory, NULL, "pci", UINT64_MAX); + rom_memory = pci_memory; - phb = OBJECT(qdev_new(TYPE_I440FX_PCI_HOST_BRIDGE)); - object_property_add_child(OBJECT(machine), "i440fx", phb); - object_property_set_link(phb, PCI_HOST_PROP_RAM_MEM, - OBJECT(ram_memory), &error_fatal); - object_property_set_link(phb, PCI_HOST_PROP_PCI_MEM, - OBJECT(pci_memory), &error_fatal); - object_property_set_link(phb, PCI_HOST_PROP_SYSTEM_MEM, - OBJECT(system_memory), &error_fatal); - object_property_set_link(phb, PCI_HOST_PROP_IO_MEM, - OBJECT(system_io), &error_fatal); - object_property_set_uint(phb, PCI_HOST_BELOW_4G_MEM_SIZE, - x86ms->below_4g_mem_size, &error_fatal); - object_property_set_uint(phb, PCI_HOST_ABOVE_4G_MEM_SIZE, - x86ms->above_4g_mem_size, &error_fatal); - object_property_set_str(phb, I440FX_HOST_PROP_PCI_TYPE, pci_type, - &error_fatal); - sysbus_realize_and_unref(SYS_BUS_DEVICE(phb), &error_fatal); + phb = OBJECT(qdev_new(TYPE_I440FX_PCI_HOST_BRIDGE)); + object_property_add_child(OBJECT(machine), "i440fx", phb); + object_property_set_link(phb, PCI_HOST_PROP_RAM_MEM, + OBJECT(ram_memory), &error_fatal); + object_property_set_link(phb, PCI_HOST_PROP_PCI_MEM, + OBJECT(pci_memory), &error_fatal); + object_property_set_link(phb, PCI_HOST_PROP_SYSTEM_MEM, + OBJECT(system_memory), &error_fatal); + object_property_set_link(phb, PCI_HOST_PROP_IO_MEM, + OBJECT(system_io), &error_fatal); + object_property_set_uint(phb, PCI_HOST_BELOW_4G_MEM_SIZE, + x86ms->below_4g_mem_size, &error_fatal); + object_property_set_uint(phb, PCI_HOST_ABOVE_4G_MEM_SIZE, + x86ms->above_4g_mem_size, &error_fatal); + object_property_set_str(phb, I440FX_HOST_PROP_PCI_TYPE, pci_type, + &error_fatal); + sysbus_realize_and_unref(SYS_BUS_DEVICE(phb), &error_fatal); - pcms->pcibus = PCI_BUS(qdev_get_child_bus(DEVICE(phb), "pci.0")); - pci_bus_map_irqs(pcms->pcibus, - xen_enabled() ? xen_pci_slot_get_pirq - : pc_pci_slot_get_pirq); + pcms->pcibus = PCI_BUS(qdev_get_child_bus(DEVICE(phb), "pci.0")); + pci_bus_map_irqs(pcms->pcibus, + xen_enabled() ? xen_pci_slot_get_pirq + : pc_pci_slot_get_pirq); - hole64_size = object_property_get_uint(phb, - PCI_HOST_PROP_PCI_HOLE64_SIZE, - &error_abort); - } + hole64_size = object_property_get_uint(phb, + PCI_HOST_PROP_PCI_HOLE64_SIZE, + &error_abort); /* allocate ram and load rom/bios */ if (!xen_enabled()) { @@ -242,72 +245,51 @@ static void pc_init1(MachineState *machine, const char *pci_type) } } - gsi_state = pc_gsi_create(&x86ms->gsi, pcmc->pci_enabled); + gsi_state = pc_gsi_create(&x86ms->gsi, true); - if (pcmc->pci_enabled) { - PCIDevice *pci_dev; - DeviceState *dev; - size_t i; - - pci_dev = pci_new_multifunction(-1, pcms->south_bridge); - object_property_set_bool(OBJECT(pci_dev), "has-usb", - machine_usb(machine), &error_abort); - object_property_set_bool(OBJECT(pci_dev), "has-acpi", - x86_machine_is_acpi_enabled(x86ms), - &error_abort); - object_property_set_bool(OBJECT(pci_dev), "has-pic", false, - &error_abort); - object_property_set_bool(OBJECT(pci_dev), "has-pit", false, - &error_abort); - qdev_prop_set_uint32(DEVICE(pci_dev), "smb_io_base", 0xb100); - object_property_set_bool(OBJECT(pci_dev), "smm-enabled", - x86_machine_is_smm_enabled(x86ms), - &error_abort); - dev = DEVICE(pci_dev); - for (i = 0; i < ISA_NUM_IRQS; i++) { - qdev_connect_gpio_out_named(dev, "isa-irqs", i, x86ms->gsi[i]); - } - pci_realize_and_unref(pci_dev, pcms->pcibus, &error_fatal); - - if (xen_enabled()) { - pci_device_set_intx_routing_notifier( - pci_dev, piix_intx_routing_notifier_xen); - - /* - * Xen supports additional interrupt routes from the PCI devices to - * the IOAPIC: the four pins of each PCI device on the bus are also - * connected to the IOAPIC directly. - * These additional routes can be discovered through ACPI. - */ - pci_bus_irqs(pcms->pcibus, xen_intx_set_irq, pci_dev, - XEN_IOAPIC_NUM_PIRQS); - } - - isa_bus = ISA_BUS(qdev_get_child_bus(DEVICE(pci_dev), "isa.0")); - x86ms->rtc = ISA_DEVICE(object_resolve_path_component(OBJECT(pci_dev), - "rtc")); - piix4_pm = object_resolve_path_component(OBJECT(pci_dev), "pm"); - dev = DEVICE(object_resolve_path_component(OBJECT(pci_dev), "ide")); - pci_ide_create_devs(PCI_DEVICE(dev)); - pcms->idebus[0] = qdev_get_child_bus(dev, "ide.0"); - pcms->idebus[1] = qdev_get_child_bus(dev, "ide.1"); - } else { - uint32_t irq; - - isa_bus = isa_bus_new(NULL, system_memory, system_io, - &error_abort); - isa_bus_register_input_irqs(isa_bus, x86ms->gsi); - - x86ms->rtc = isa_new(TYPE_MC146818_RTC); - qdev_prop_set_int32(DEVICE(x86ms->rtc), "base_year", 2000); - isa_realize_and_unref(x86ms->rtc, isa_bus, &error_fatal); - irq = object_property_get_uint(OBJECT(x86ms->rtc), "irq", - &error_fatal); - isa_connect_gpio_out(ISA_DEVICE(x86ms->rtc), 0, irq); - - i8257_dma_init(OBJECT(machine), isa_bus, 0); - pcms->hpet_enabled = false; + pci_dev = pci_new_multifunction(-1, pcms->south_bridge); + object_property_set_bool(OBJECT(pci_dev), "has-usb", + machine_usb(machine), &error_abort); + object_property_set_bool(OBJECT(pci_dev), "has-acpi", + x86_machine_is_acpi_enabled(x86ms), + &error_abort); + object_property_set_bool(OBJECT(pci_dev), "has-pic", false, + &error_abort); + object_property_set_bool(OBJECT(pci_dev), "has-pit", false, + &error_abort); + qdev_prop_set_uint32(DEVICE(pci_dev), "smb_io_base", 0xb100); + object_property_set_bool(OBJECT(pci_dev), "smm-enabled", + x86_machine_is_smm_enabled(x86ms), + &error_abort); + dev = DEVICE(pci_dev); + for (i = 0; i < ISA_NUM_IRQS; i++) { + qdev_connect_gpio_out_named(dev, "isa-irqs", i, x86ms->gsi[i]); } + pci_realize_and_unref(pci_dev, pcms->pcibus, &error_fatal); + + if (xen_enabled()) { + pci_device_set_intx_routing_notifier( + pci_dev, piix_intx_routing_notifier_xen); + + /* + * Xen supports additional interrupt routes from the PCI devices to + * the IOAPIC: the four pins of each PCI device on the bus are also + * connected to the IOAPIC directly. + * These additional routes can be discovered through ACPI. + */ + pci_bus_irqs(pcms->pcibus, xen_intx_set_irq, pci_dev, + XEN_IOAPIC_NUM_PIRQS); + } + + isa_bus = ISA_BUS(qdev_get_child_bus(DEVICE(pci_dev), "isa.0")); + x86ms->rtc = ISA_DEVICE(object_resolve_path_component(OBJECT(pci_dev), + "rtc")); + piix4_pm = object_resolve_path_component(OBJECT(pci_dev), "pm"); + dev = DEVICE(object_resolve_path_component(OBJECT(pci_dev), "ide")); + pci_ide_create_devs(PCI_DEVICE(dev)); + pcms->idebus[0] = qdev_get_child_bus(dev, "ide.0"); + pcms->idebus[1] = qdev_get_child_bus(dev, "ide.1"); + if (x86ms->pic == ON_OFF_AUTO_ON || x86ms->pic == ON_OFF_AUTO_AUTO) { pc_i8259_create(isa_bus, gsi_state->i8259_irq); @@ -321,7 +303,7 @@ static void pc_init1(MachineState *machine, const char *pci_type) x86_register_ferr_irq(x86ms->gsi[13]); } - pc_vga_init(isa_bus, pcmc->pci_enabled ? pcms->pcibus : NULL); + pc_vga_init(isa_bus, pcms->pcibus); /* init basic PC hardware */ pc_basic_device_init(pcms, isa_bus, x86ms->gsi, x86ms->rtc, @@ -329,28 +311,6 @@ static void pc_init1(MachineState *machine, const char *pci_type) pc_nic_init(pcmc, isa_bus, pcms->pcibus); -#ifdef CONFIG_IDE_ISA - if (!pcmc->pci_enabled) { - DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS]; - int i; - - ide_drive_get(hd, ARRAY_SIZE(hd)); - for (i = 0; i < MAX_IDE_BUS; i++) { - ISADevice *dev; - char busname[] = "ide.0"; - dev = isa_ide_init(isa_bus, ide_iobase[i], ide_iobase2[i], - ide_irq[i], - hd[MAX_IDE_DEVS * i], hd[MAX_IDE_DEVS * i + 1]); - /* - * The ide bus name is ide.0 for the first bus and ide.1 for the - * second one. - */ - busname[4] = '0' + i; - pcms->idebus[i] = qdev_get_child_bus(DEVICE(dev), busname); - } - } -#endif - if (piix4_pm) { smi_irq = qemu_allocate_irq(pc_acpi_smi_interrupt, first_cpu, 0); From 3113e7db1d5d683e91cdbb4796e1b154cdda73bf Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Thu, 28 Aug 2025 12:09:59 +0100 Subject: [PATCH 0124/2396] hw/i386: move isapc machine to separate isapc.c file Now that pc_init_isa() is independent of any PCI initialisation, move it into a separate isapc.c file including the ISA IDE variables which are now no longer needed for the pc-i440fx machine. This enables us to finally fix the dependency of ISAPC on I440FX in hw/i386/Kconfig. Note that as part of the move to a separate file we can see that the licence text is a verbatim copy of the MIT licence. The text originates from commit 1df912cf9e ("VL license of the day is MIT/BSD") so we can be sure that this was the original intent. As a consequence we can update the file header to use a SPDX tag as per the current project contribution guidelines. Signed-off-by: Mark Cave-Ayland Reviewed-by: Bernhard Beschow Reviewed-by: Xiaoyao Li Link: https://lore.kernel.org/r/20250828111057.468712-17-mark.caveayland@nutanix.com Signed-off-by: Paolo Bonzini --- hw/i386/Kconfig | 3 - hw/i386/isapc.c | 190 ++++++++++++++++++++++++++++++++++++++++++++ hw/i386/meson.build | 1 + hw/i386/pc_piix.c | 172 --------------------------------------- 4 files changed, 191 insertions(+), 175 deletions(-) create mode 100644 hw/i386/isapc.c diff --git a/hw/i386/Kconfig b/hw/i386/Kconfig index 3a0e2b8ebb..6a0ab54bea 100644 --- a/hw/i386/Kconfig +++ b/hw/i386/Kconfig @@ -96,9 +96,6 @@ config ISAPC select ISA_BUS select PC select IDE_ISA - # FIXME: it is in the same file as i440fx, and does not compile - # if separated - depends on I440FX config Q35 bool diff --git a/hw/i386/isapc.c b/hw/i386/isapc.c new file mode 100644 index 0000000000..300d64b7ad --- /dev/null +++ b/hw/i386/isapc.c @@ -0,0 +1,190 @@ +/* + * QEMU PC System Emulator + * + * Copyright (c) 2003-2004 Fabrice Bellard + * + * SPDX-License-Identifier: MIT + */ + +#include "qemu/osdep.h" + +#include "qemu/units.h" +#include "qemu/error-report.h" +#include "hw/char/parallel-isa.h" +#include "hw/dma/i8257.h" +#include "hw/i386/pc.h" +#include "hw/ide/isa.h" +#include "hw/ide/ide-bus.h" +#include "system/kvm.h" +#include "hw/i386/kvm/clock.h" +#include "hw/xen/xen-x86.h" +#include "system/xen.h" +#include "hw/rtc/mc146818rtc.h" +#include "target/i386/cpu.h" + +static const int ide_iobase[MAX_IDE_BUS] = { 0x1f0, 0x170 }; +static const int ide_iobase2[MAX_IDE_BUS] = { 0x3f6, 0x376 }; +static const int ide_irq[MAX_IDE_BUS] = { 14, 15 }; + + +static void pc_init_isa(MachineState *machine) +{ + PCMachineState *pcms = PC_MACHINE(machine); + PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms); + X86MachineState *x86ms = X86_MACHINE(machine); + MemoryRegion *system_memory = get_system_memory(); + MemoryRegion *system_io = get_system_io(); + ISABus *isa_bus; + uint32_t irq; + GSIState *gsi_state; + MemoryRegion *ram_memory; + MemoryRegion *rom_memory = system_memory; + DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS]; + int i; + + /* + * There is a small chance that someone unintentionally passes "-cpu max" + * for the isapc machine, which will provide a much more modern 32-bit + * CPU than would be expected for an ISA-era PC. If the "max" cpu type has + * been specified, choose the "best" 32-bit cpu possible which we consider + * be the pentium3 (deliberately choosing an Intel CPU given that the + * default 486 CPU for the isapc machine is also an Intel CPU). + */ + if (!strcmp(machine->cpu_type, X86_CPU_TYPE_NAME("max"))) { + machine->cpu_type = X86_CPU_TYPE_NAME("pentium3"); + warn_report("-cpu max is invalid for isapc machine, using pentium3"); + } + + /* + * Similarly if someone unintentionally passes "-cpu host" for the isapc + * machine then display a warning and also switch to the "best" 32-bit + * cpu possible which we consider to be the pentium3. This is because any + * host CPU will already be modern than this, but it also ensures any + * newer CPU flags/features are filtered out for older guests. + */ + if (!strcmp(machine->cpu_type, X86_CPU_TYPE_NAME("host"))) { + machine->cpu_type = X86_CPU_TYPE_NAME("pentium3"); + warn_report("-cpu host is invalid for isapc machine, using pentium3"); + } + + if (machine->ram_size > 3.5 * GiB) { + error_report("Too much memory for this machine: %" PRId64 " MiB, " + "maximum 3584 MiB", machine->ram_size / MiB); + exit(1); + } + + /* + * There is no RAM split for the isapc machine + */ + if (xen_enabled()) { + xen_hvm_init_pc(pcms, &ram_memory); + } else { + ram_memory = machine->ram; + + pcms->max_ram_below_4g = 3.5 * GiB; + x86ms->above_4g_mem_size = 0; + x86ms->below_4g_mem_size = machine->ram_size; + } + + x86_cpus_init(x86ms, pcmc->default_cpu_version); + + if (kvm_enabled()) { + kvmclock_create(pcmc->kvmclock_create_always); + } + + /* allocate ram and load rom/bios */ + if (!xen_enabled()) { + pc_memory_init(pcms, system_memory, rom_memory, 0); + } else { + assert(machine->ram_size == x86ms->below_4g_mem_size + + x86ms->above_4g_mem_size); + + if (machine->kernel_filename != NULL) { + /* For xen HVM direct kernel boot, load linux here */ + xen_load_linux(pcms); + } + } + + gsi_state = pc_gsi_create(&x86ms->gsi, false); + + isa_bus = isa_bus_new(NULL, system_memory, system_io, + &error_abort); + isa_bus_register_input_irqs(isa_bus, x86ms->gsi); + + x86ms->rtc = isa_new(TYPE_MC146818_RTC); + qdev_prop_set_int32(DEVICE(x86ms->rtc), "base_year", 2000); + isa_realize_and_unref(x86ms->rtc, isa_bus, &error_fatal); + irq = object_property_get_uint(OBJECT(x86ms->rtc), "irq", + &error_fatal); + isa_connect_gpio_out(ISA_DEVICE(x86ms->rtc), 0, irq); + + i8257_dma_init(OBJECT(machine), isa_bus, 0); + pcms->hpet_enabled = false; + + if (x86ms->pic == ON_OFF_AUTO_ON || x86ms->pic == ON_OFF_AUTO_AUTO) { + pc_i8259_create(isa_bus, gsi_state->i8259_irq); + } + + if (tcg_enabled()) { + x86_register_ferr_irq(x86ms->gsi[13]); + } + + pc_vga_init(isa_bus, NULL); + + /* init basic PC hardware */ + pc_basic_device_init(pcms, isa_bus, x86ms->gsi, x86ms->rtc, + !MACHINE_CLASS(pcmc)->no_floppy, 0x4); + + pc_nic_init(pcmc, isa_bus, NULL); + + ide_drive_get(hd, ARRAY_SIZE(hd)); + for (i = 0; i < MAX_IDE_BUS; i++) { + ISADevice *dev; + char busname[] = "ide.0"; + dev = isa_ide_init(isa_bus, ide_iobase[i], ide_iobase2[i], + ide_irq[i], + hd[MAX_IDE_DEVS * i], hd[MAX_IDE_DEVS * i + 1]); + /* + * The ide bus name is ide.0 for the first bus and ide.1 for the + * second one. + */ + busname[4] = '0' + i; + pcms->idebus[i] = qdev_get_child_bus(DEVICE(dev), busname); + } +} + +static void isapc_machine_options(MachineClass *m) +{ + static const char * const valid_cpu_types[] = { + X86_CPU_TYPE_NAME("486"), + X86_CPU_TYPE_NAME("athlon"), + X86_CPU_TYPE_NAME("kvm32"), + X86_CPU_TYPE_NAME("pentium"), + X86_CPU_TYPE_NAME("pentium2"), + X86_CPU_TYPE_NAME("pentium3"), + X86_CPU_TYPE_NAME("qemu32"), + X86_CPU_TYPE_NAME("max"), + X86_CPU_TYPE_NAME("host"), + NULL + }; + PCMachineClass *pcmc = PC_MACHINE_CLASS(m); + + m->desc = "ISA-only PC"; + m->max_cpus = 1; + m->option_rom_has_mr = true; + m->rom_file_has_mr = false; + pcmc->pci_enabled = false; + pcmc->has_acpi_build = false; + pcmc->smbios_defaults = false; + pcmc->gigabyte_align = false; + pcmc->smbios_legacy_mode = true; + pcmc->has_reserved_memory = false; + m->default_nic = "ne2k_isa"; + m->default_cpu_type = X86_CPU_TYPE_NAME("486"); + m->valid_cpu_types = valid_cpu_types; + m->no_floppy = !module_object_class_by_name(TYPE_ISA_FDC); + m->no_parallel = !module_object_class_by_name(TYPE_ISA_PARALLEL); +} + +DEFINE_PC_MACHINE(isapc, "isapc", pc_init_isa, + isapc_machine_options); diff --git a/hw/i386/meson.build b/hw/i386/meson.build index 7896f348cf..436b3ce52d 100644 --- a/hw/i386/meson.build +++ b/hw/i386/meson.build @@ -14,6 +14,7 @@ i386_ss.add(when: 'CONFIG_X86_IOMMU', if_true: files('x86-iommu.c'), i386_ss.add(when: 'CONFIG_AMD_IOMMU', if_true: files('amd_iommu.c'), if_false: files('amd_iommu-stub.c')) i386_ss.add(when: 'CONFIG_I440FX', if_true: files('pc_piix.c')) +i386_ss.add(when: 'CONFIG_ISAPC', if_true: files('isapc.c')) i386_ss.add(when: 'CONFIG_MICROVM', if_true: files('x86-common.c', 'microvm.c', 'acpi-microvm.c', 'microvm-dt.c')) i386_ss.add(when: 'CONFIG_NITRO_ENCLAVE', if_true: files('nitro_enclave.c')) i386_ss.add(when: 'CONFIG_Q35', if_true: files('pc_q35.c')) diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 3ea77b2c44..988c9edc32 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -71,12 +71,6 @@ #define XEN_IOAPIC_NUM_PIRQS 128ULL -#ifdef CONFIG_IDE_ISA -static const int ide_iobase[MAX_IDE_BUS] = { 0x1f0, 0x170 }; -static const int ide_iobase2[MAX_IDE_BUS] = { 0x3f6, 0x376 }; -static const int ide_irq[MAX_IDE_BUS] = { 14, 15 }; -#endif - static GlobalProperty pc_piix_compat_defaults[] = { { TYPE_RAMFB_DEVICE, "use-legacy-x86-rom", "true" }, { TYPE_VFIO_PCI_NOHOTPLUG, "use-legacy-x86-rom", "true" }, @@ -392,134 +386,6 @@ static void pc_set_south_bridge(Object *obj, int value, Error **errp) pcms->south_bridge = PCSouthBridgeOption_lookup.array[value]; } -#ifdef CONFIG_ISAPC -static void pc_init_isa(MachineState *machine) -{ - PCMachineState *pcms = PC_MACHINE(machine); - PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms); - X86MachineState *x86ms = X86_MACHINE(machine); - MemoryRegion *system_memory = get_system_memory(); - MemoryRegion *system_io = get_system_io(); - ISABus *isa_bus; - uint32_t irq; - GSIState *gsi_state; - MemoryRegion *ram_memory; - MemoryRegion *rom_memory = system_memory; - DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS]; - int i; - - /* - * There is a small chance that someone unintentionally passes "-cpu max" - * for the isapc machine, which will provide a much more modern 32-bit - * CPU than would be expected for an ISA-era PC. If the "max" cpu type has - * been specified, choose the "best" 32-bit cpu possible which we consider - * be the pentium3 (deliberately choosing an Intel CPU given that the - * default 486 CPU for the isapc machine is also an Intel CPU). - */ - if (!strcmp(machine->cpu_type, X86_CPU_TYPE_NAME("max"))) { - machine->cpu_type = X86_CPU_TYPE_NAME("pentium3"); - warn_report("-cpu max is invalid for isapc machine, using pentium3"); - } - - /* - * Similarly if someone unintentionally passes "-cpu host" for the isapc - * machine then display a warning and also switch to the "best" 32-bit - * cpu possible which we consider to be the pentium3. This is because any - * host CPU will already be modern than this, but it also ensures any - * newer CPU flags/features are filtered out for older guests. - */ - if (!strcmp(machine->cpu_type, X86_CPU_TYPE_NAME("host"))) { - machine->cpu_type = X86_CPU_TYPE_NAME("pentium3"); - warn_report("-cpu host is invalid for isapc machine, using pentium3"); - } - - if (machine->ram_size > 3.5 * GiB) { - error_report("Too much memory for this machine: %" PRId64 " MiB, " - "maximum 3584 MiB", machine->ram_size / MiB); - exit(1); - } - - /* - * There is no RAM split for the isapc machine - */ - if (xen_enabled()) { - xen_hvm_init_pc(pcms, &ram_memory); - } else { - ram_memory = machine->ram; - - pcms->max_ram_below_4g = 3.5 * GiB; - x86ms->above_4g_mem_size = 0; - x86ms->below_4g_mem_size = machine->ram_size; - } - - x86_cpus_init(x86ms, pcmc->default_cpu_version); - - if (kvm_enabled()) { - kvmclock_create(pcmc->kvmclock_create_always); - } - - /* allocate ram and load rom/bios */ - if (!xen_enabled()) { - pc_memory_init(pcms, system_memory, rom_memory, 0); - } else { - assert(machine->ram_size == x86ms->below_4g_mem_size + - x86ms->above_4g_mem_size); - - if (machine->kernel_filename != NULL) { - /* For xen HVM direct kernel boot, load linux here */ - xen_load_linux(pcms); - } - } - - gsi_state = pc_gsi_create(&x86ms->gsi, false); - - isa_bus = isa_bus_new(NULL, system_memory, system_io, - &error_abort); - isa_bus_register_input_irqs(isa_bus, x86ms->gsi); - - x86ms->rtc = isa_new(TYPE_MC146818_RTC); - qdev_prop_set_int32(DEVICE(x86ms->rtc), "base_year", 2000); - isa_realize_and_unref(x86ms->rtc, isa_bus, &error_fatal); - irq = object_property_get_uint(OBJECT(x86ms->rtc), "irq", - &error_fatal); - isa_connect_gpio_out(ISA_DEVICE(x86ms->rtc), 0, irq); - - i8257_dma_init(OBJECT(machine), isa_bus, 0); - pcms->hpet_enabled = false; - - if (x86ms->pic == ON_OFF_AUTO_ON || x86ms->pic == ON_OFF_AUTO_AUTO) { - pc_i8259_create(isa_bus, gsi_state->i8259_irq); - } - - if (tcg_enabled()) { - x86_register_ferr_irq(x86ms->gsi[13]); - } - - pc_vga_init(isa_bus, NULL); - - /* init basic PC hardware */ - pc_basic_device_init(pcms, isa_bus, x86ms->gsi, x86ms->rtc, - !MACHINE_CLASS(pcmc)->no_floppy, 0x4); - - pc_nic_init(pcmc, isa_bus, NULL); - - ide_drive_get(hd, ARRAY_SIZE(hd)); - for (i = 0; i < MAX_IDE_BUS; i++) { - ISADevice *dev; - char busname[] = "ide.0"; - dev = isa_ide_init(isa_bus, ide_iobase[i], ide_iobase2[i], - ide_irq[i], - hd[MAX_IDE_DEVS * i], hd[MAX_IDE_DEVS * i + 1]); - /* - * The ide bus name is ide.0 for the first bus and ide.1 for the - * second one. - */ - busname[4] = '0' + i; - pcms->idebus[i] = qdev_get_child_bus(DEVICE(dev), busname); - } -} -#endif - #ifdef CONFIG_XEN static void pc_xen_hvm_init(MachineState *machine) { @@ -887,44 +753,6 @@ static void pc_i440fx_machine_2_6_options(MachineClass *m) DEFINE_I440FX_MACHINE(2, 6); -#ifdef CONFIG_ISAPC -static void isapc_machine_options(MachineClass *m) -{ - static const char * const valid_cpu_types[] = { - X86_CPU_TYPE_NAME("486"), - X86_CPU_TYPE_NAME("athlon"), - X86_CPU_TYPE_NAME("kvm32"), - X86_CPU_TYPE_NAME("pentium"), - X86_CPU_TYPE_NAME("pentium2"), - X86_CPU_TYPE_NAME("pentium3"), - X86_CPU_TYPE_NAME("qemu32"), - X86_CPU_TYPE_NAME("max"), - X86_CPU_TYPE_NAME("host"), - NULL - }; - PCMachineClass *pcmc = PC_MACHINE_CLASS(m); - - m->desc = "ISA-only PC"; - m->max_cpus = 1; - m->option_rom_has_mr = true; - m->rom_file_has_mr = false; - pcmc->pci_enabled = false; - pcmc->has_acpi_build = false; - pcmc->smbios_defaults = false; - pcmc->gigabyte_align = false; - pcmc->smbios_legacy_mode = true; - pcmc->has_reserved_memory = false; - m->default_nic = "ne2k_isa"; - m->default_cpu_type = X86_CPU_TYPE_NAME("486"); - m->valid_cpu_types = valid_cpu_types; - m->no_floppy = !module_object_class_by_name(TYPE_ISA_FDC); - m->no_parallel = !module_object_class_by_name(TYPE_ISA_PARALLEL); -} - -DEFINE_PC_MACHINE(isapc, "isapc", pc_init_isa, - isapc_machine_options); -#endif - #ifdef CONFIG_XEN static void xenfv_machine_4_2_options(MachineClass *m) { From d773f9689109eb00ba6627c2264af949c66897fb Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Thu, 28 Aug 2025 12:10:00 +0100 Subject: [PATCH 0125/2396] hw/i386/pc_piix.c: remove unused headers after isapc machine split MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The headers for isapc-only devices can be removed from pc_piix.c since they are no longer used by the i440fx-pc machine. Signed-off-by: Mark Cave-Ayland Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Xiaoyao Li Link: https://lore.kernel.org/r/20250828111057.468712-18-mark.caveayland@nutanix.com Signed-off-by: Paolo Bonzini --- hw/i386/pc_piix.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 988c9edc32..627de09c70 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -27,19 +27,16 @@ #include "qemu/units.h" #include "hw/char/parallel-isa.h" -#include "hw/dma/i8257.h" #include "hw/i386/x86.h" #include "hw/i386/pc.h" #include "hw/i386/apic.h" #include "hw/pci-host/i440fx.h" -#include "hw/rtc/mc146818rtc.h" #include "hw/southbridge/piix.h" #include "hw/display/ramfb.h" #include "hw/pci/pci.h" #include "hw/pci/pci_ids.h" #include "hw/usb.h" #include "net/net.h" -#include "hw/ide/isa.h" #include "hw/ide/pci.h" #include "hw/irq.h" #include "system/kvm.h" From 523a64f388a689a1e9bc593ca8768fe1449613db Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Thu, 28 Aug 2025 12:10:01 +0100 Subject: [PATCH 0126/2396] hw/i386/pc_piix.c: replace rom_memory with pci_memory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that we can guarantee the i440fx-pc machine will always have a PCI bus, any instances of rom_memory can be replaced by pci_memory and rom_memory removed completely. Signed-off-by: Mark Cave-Ayland Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Xiaoyao Li Link: https://lore.kernel.org/r/20250828111057.468712-19-mark.caveayland@nutanix.com Signed-off-by: Paolo Bonzini --- hw/i386/pc_piix.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 627de09c70..7e78b6daa6 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -113,7 +113,6 @@ static void pc_init1(MachineState *machine, const char *pci_type) GSIState *gsi_state; MemoryRegion *ram_memory; MemoryRegion *pci_memory = NULL; - MemoryRegion *rom_memory = system_memory; ram_addr_t lowmem; uint64_t hole64_size = 0; PCIDevice *pci_dev; @@ -193,7 +192,6 @@ static void pc_init1(MachineState *machine, const char *pci_type) pci_memory = g_new(MemoryRegion, 1); memory_region_init(pci_memory, NULL, "pci", UINT64_MAX); - rom_memory = pci_memory; phb = OBJECT(qdev_new(TYPE_I440FX_PCI_HOST_BRIDGE)); object_property_add_child(OBJECT(machine), "i440fx", phb); @@ -224,7 +222,7 @@ static void pc_init1(MachineState *machine, const char *pci_type) /* allocate ram and load rom/bios */ if (!xen_enabled()) { - pc_memory_init(pcms, system_memory, rom_memory, hole64_size); + pc_memory_init(pcms, system_memory, pci_memory, hole64_size); } else { assert(machine->ram_size == x86ms->below_4g_mem_size + x86ms->above_4g_mem_size); From d8701867d12241f53f3b17973e7fd533c764c76a Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Thu, 28 Aug 2025 12:10:02 +0100 Subject: [PATCH 0127/2396] hw/i386/isapc.c: replace rom_memory with system_memory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that we can guarantee the isapc machine will never have a PCI bus, any instances of rom_memory can be replaced by system_memory and rom_memory removed completely. Signed-off-by: Mark Cave-Ayland Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Xiaoyao Li Link: https://lore.kernel.org/r/20250828111057.468712-20-mark.caveayland@nutanix.com Signed-off-by: Paolo Bonzini --- hw/i386/isapc.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/hw/i386/isapc.c b/hw/i386/isapc.c index 300d64b7ad..44f4a44672 100644 --- a/hw/i386/isapc.c +++ b/hw/i386/isapc.c @@ -38,7 +38,6 @@ static void pc_init_isa(MachineState *machine) uint32_t irq; GSIState *gsi_state; MemoryRegion *ram_memory; - MemoryRegion *rom_memory = system_memory; DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS]; int i; @@ -94,7 +93,7 @@ static void pc_init_isa(MachineState *machine) /* allocate ram and load rom/bios */ if (!xen_enabled()) { - pc_memory_init(pcms, system_memory, rom_memory, 0); + pc_memory_init(pcms, system_memory, system_memory, 0); } else { assert(machine->ram_size == x86ms->below_4g_mem_size + x86ms->above_4g_mem_size); From b8217bbaf2bafef1a4f54082a3548613eeef8f2b Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 29 Aug 2025 10:46:48 +0200 Subject: [PATCH 0128/2396] user-exec: ensure interrupt_request is not used cpu_interrupt() is not called anymore except by ARM but even there it is dead code; disentangling the various cpregs accessors from user-mode emulation is a work in progress. Signed-off-by: Paolo Bonzini --- accel/tcg/cpu-exec.c | 6 ++++-- accel/tcg/user-exec.c | 4 +--- include/hw/core/cpu.h | 1 + 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index 713bdb2056..b44dd1e820 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -778,6 +778,9 @@ static inline bool cpu_handle_interrupt(CPUState *cpu, */ qatomic_set_mb(&cpu->neg.icount_decr.u16.high, 0); +#ifdef CONFIG_USER_ONLY + g_assert(!qatomic_read(&cpu->interrupt_request)); +#else if (unlikely(qatomic_read(&cpu->interrupt_request))) { int interrupt_request; bql_lock(); @@ -792,7 +795,6 @@ static inline bool cpu_handle_interrupt(CPUState *cpu, bql_unlock(); return true; } -#if !defined(CONFIG_USER_ONLY) if (replay_mode == REPLAY_MODE_PLAY && !replay_has_interrupt()) { /* Do nothing */ } else if (interrupt_request & CPU_INTERRUPT_HALT) { @@ -840,7 +842,6 @@ static inline bool cpu_handle_interrupt(CPUState *cpu, * reload the 'interrupt_request' value */ interrupt_request = cpu->interrupt_request; } -#endif /* !CONFIG_USER_ONLY */ if (interrupt_request & CPU_INTERRUPT_EXITTB) { cpu->interrupt_request &= ~CPU_INTERRUPT_EXITTB; /* ensure that no TB jump will be modified as @@ -851,6 +852,7 @@ static inline bool cpu_handle_interrupt(CPUState *cpu, /* If we exit via cpu_loop_exit/longjmp it is reset in cpu_exec */ bql_unlock(); } +#endif /* !CONFIG_USER_ONLY */ /* Finally, check if we need to exit to the main loop. */ if (unlikely(qatomic_read(&cpu->exit_request)) || icount_exit_request(cpu)) { diff --git a/accel/tcg/user-exec.c b/accel/tcg/user-exec.c index f25d80e2dc..748bfab04a 100644 --- a/accel/tcg/user-exec.c +++ b/accel/tcg/user-exec.c @@ -48,9 +48,7 @@ __thread uintptr_t helper_retaddr; void cpu_interrupt(CPUState *cpu, int mask) { - g_assert(bql_locked()); - cpu->interrupt_request |= mask; - qatomic_set(&cpu->neg.icount_decr.u16.high, -1); + g_assert_not_reached(); } /* diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h index 5eaf41a566..f73b4357c7 100644 --- a/include/hw/core/cpu.h +++ b/include/hw/core/cpu.h @@ -423,6 +423,7 @@ struct qemu_work_item; * @created: Indicates whether the CPU thread has been successfully created. * @halt_cond: condition variable sleeping threads can wait on. * @interrupt_request: Indicates a pending interrupt request. + * Only used by system emulation. * @halted: Nonzero if the CPU is in suspended state. * @stop: Indicates a pending stop request. * @stopped: Indicates the CPU has been artificially stopped. From 87511341c30d8c9c77178db16491a0ccacc5d64b Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Thu, 21 Aug 2025 17:56:03 +0200 Subject: [PATCH 0129/2396] add cpu_test_interrupt()/cpu_set_interrupt() helpers and use them tree wide The helpers form load-acquire/store-release pair and ensure that appropriate barriers are in place in case checks happen outside of BQL. Use them to replace open-coded checkers/setters across the code, to make sure that barriers are not missed. Helpers also make code a bit more readable. Signed-off-by: Igor Mammedov Reviewed-by: Peter Xu Reviewed-by: Jason J. Herne Link: https://lore.kernel.org/r/20250821155603.2422553-1-imammedo@redhat.com Signed-off-by: Paolo Bonzini --- accel/tcg/cpu-exec.c | 12 +++++----- accel/tcg/tcg-accel-ops.c | 2 +- hw/intc/s390_flic.c | 2 +- hw/openrisc/cputimer.c | 2 +- include/hw/core/cpu.h | 22 +++++++++++++++++++ system/cpus.c | 9 +++++++- target/alpha/cpu.c | 8 +++---- target/arm/cpu.c | 20 ++++++++--------- target/arm/helper.c | 18 +++++++-------- target/arm/hvf/hvf.c | 6 ++--- target/avr/cpu.c | 2 +- target/hppa/cpu.c | 2 +- target/i386/hvf/hvf.c | 4 ++-- target/i386/hvf/x86hvf.c | 21 +++++++++--------- target/i386/kvm/kvm.c | 34 ++++++++++++++--------------- target/i386/nvmm/nvmm-all.c | 24 ++++++++++---------- target/i386/tcg/system/seg_helper.c | 2 +- target/i386/tcg/system/svm_helper.c | 2 +- target/i386/whpx/whpx-all.c | 34 ++++++++++++++--------------- target/loongarch/cpu.c | 2 +- target/m68k/cpu.c | 2 +- target/microblaze/cpu.c | 2 +- target/mips/cpu.c | 6 ++--- target/mips/kvm.c | 2 +- target/openrisc/cpu.c | 3 +-- target/ppc/cpu_init.c | 2 +- target/ppc/kvm.c | 2 +- target/rx/cpu.c | 3 +-- target/rx/helper.c | 2 +- target/s390x/cpu-system.c | 2 +- target/sh4/cpu.c | 2 +- target/sh4/helper.c | 2 +- target/sparc/cpu.c | 2 +- target/sparc/int64_helper.c | 4 ++-- 34 files changed, 145 insertions(+), 119 deletions(-) diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index b44dd1e820..96c124aa72 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -779,9 +779,9 @@ static inline bool cpu_handle_interrupt(CPUState *cpu, qatomic_set_mb(&cpu->neg.icount_decr.u16.high, 0); #ifdef CONFIG_USER_ONLY - g_assert(!qatomic_read(&cpu->interrupt_request)); + assert(!cpu_test_interrupt(cpu, ~0)); #else - if (unlikely(qatomic_read(&cpu->interrupt_request))) { + if (unlikely(cpu_test_interrupt(cpu, ~0))) { int interrupt_request; bql_lock(); interrupt_request = cpu->interrupt_request; @@ -789,7 +789,7 @@ static inline bool cpu_handle_interrupt(CPUState *cpu, /* Mask out external interrupts for this step. */ interrupt_request &= ~CPU_INTERRUPT_SSTEP_MASK; } - if (interrupt_request & CPU_INTERRUPT_DEBUG) { + if (cpu_test_interrupt(cpu, CPU_INTERRUPT_DEBUG)) { cpu->interrupt_request &= ~CPU_INTERRUPT_DEBUG; cpu->exception_index = EXCP_DEBUG; bql_unlock(); @@ -797,7 +797,7 @@ static inline bool cpu_handle_interrupt(CPUState *cpu, } if (replay_mode == REPLAY_MODE_PLAY && !replay_has_interrupt()) { /* Do nothing */ - } else if (interrupt_request & CPU_INTERRUPT_HALT) { + } else if (cpu_test_interrupt(cpu, CPU_INTERRUPT_HALT)) { replay_interrupt(); cpu->interrupt_request &= ~CPU_INTERRUPT_HALT; cpu->halted = 1; @@ -807,7 +807,7 @@ static inline bool cpu_handle_interrupt(CPUState *cpu, } else { const TCGCPUOps *tcg_ops = cpu->cc->tcg_ops; - if (interrupt_request & CPU_INTERRUPT_RESET) { + if (cpu_test_interrupt(cpu, CPU_INTERRUPT_RESET)) { replay_interrupt(); tcg_ops->cpu_exec_reset(cpu); bql_unlock(); @@ -842,7 +842,7 @@ static inline bool cpu_handle_interrupt(CPUState *cpu, * reload the 'interrupt_request' value */ interrupt_request = cpu->interrupt_request; } - if (interrupt_request & CPU_INTERRUPT_EXITTB) { + if (cpu_test_interrupt(cpu, CPU_INTERRUPT_EXITTB)) { cpu->interrupt_request &= ~CPU_INTERRUPT_EXITTB; /* ensure that no TB jump will be modified as the program flow was changed */ diff --git a/accel/tcg/tcg-accel-ops.c b/accel/tcg/tcg-accel-ops.c index 3b0d7d298e..9c37266c1e 100644 --- a/accel/tcg/tcg-accel-ops.c +++ b/accel/tcg/tcg-accel-ops.c @@ -97,7 +97,7 @@ static void tcg_cpu_reset_hold(CPUState *cpu) /* mask must never be zero, except for A20 change call */ void tcg_handle_interrupt(CPUState *cpu, int mask) { - cpu->interrupt_request |= mask; + cpu_set_interrupt(cpu, mask); /* * If called from iothread context, wake the target cpu in diff --git a/hw/intc/s390_flic.c b/hw/intc/s390_flic.c index 8f4c9fd52e..1eed5125d1 100644 --- a/hw/intc/s390_flic.c +++ b/hw/intc/s390_flic.c @@ -190,7 +190,7 @@ static void qemu_s390_flic_notify(uint32_t type) CPU_FOREACH(cs) { S390CPU *cpu = S390_CPU(cs); - cs->interrupt_request |= CPU_INTERRUPT_HARD; + cpu_set_interrupt(cs, CPU_INTERRUPT_HARD); /* ignore CPUs that are not sleeping */ if (s390_cpu_get_state(cpu) != S390_CPU_STATE_OPERATING && diff --git a/hw/openrisc/cputimer.c b/hw/openrisc/cputimer.c index 6331997d56..51da226fcd 100644 --- a/hw/openrisc/cputimer.c +++ b/hw/openrisc/cputimer.c @@ -105,7 +105,7 @@ static void openrisc_timer_cb(void *opaque) CPUState *cs = CPU(cpu); cpu->env.ttmr |= TTMR_IP; - cs->interrupt_request |= CPU_INTERRUPT_TIMER; + cpu_set_interrupt(cs, CPU_INTERRUPT_TIMER); } switch (cpu->env.ttmr & TTMR_M) { diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h index f73b4357c7..b01a0cffd6 100644 --- a/include/hw/core/cpu.h +++ b/include/hw/core/cpu.h @@ -943,6 +943,28 @@ CPUState *cpu_by_arch_id(int64_t id); void cpu_interrupt(CPUState *cpu, int mask); +/** + * cpu_test_interrupt: + * @cpu: The CPU to check interrupt(s) on. + * @mask: The interrupts to check. + * + * Checks if any of interrupts in @mask are pending on @cpu. + */ +static inline bool cpu_test_interrupt(CPUState *cpu, int mask) +{ + return qatomic_load_acquire(&cpu->interrupt_request) & mask; +} + +/** + * cpu_set_interrupt: + * @cpu: The CPU to set pending interrupt(s) on. + * @mask: The interrupts to set. + * + * Sets interrupts in @mask as pending on @cpu. Unlike @cpu_interrupt, + * this does not kick the vCPU. + */ +void cpu_set_interrupt(CPUState *cpu, int mask); + /** * cpu_set_pc: * @cpu: The CPU to set the program counter for. diff --git a/system/cpus.c b/system/cpus.c index 256723558d..437848b5eb 100644 --- a/system/cpus.c +++ b/system/cpus.c @@ -254,9 +254,16 @@ int64_t cpus_get_elapsed_ticks(void) return cpu_get_ticks(); } +void cpu_set_interrupt(CPUState *cpu, int mask) +{ + /* Pairs with cpu_test_interrupt(). */ + qatomic_store_release(&cpu->interrupt_request, + cpu->interrupt_request | mask); +} + void generic_handle_interrupt(CPUState *cpu, int mask) { - cpu->interrupt_request |= mask; + cpu_set_interrupt(cpu, mask); if (!qemu_cpu_is_self(cpu)) { qemu_cpu_kick(cpu); diff --git a/target/alpha/cpu.c b/target/alpha/cpu.c index bf1787a69d..932cddac05 100644 --- a/target/alpha/cpu.c +++ b/target/alpha/cpu.c @@ -86,10 +86,10 @@ static bool alpha_cpu_has_work(CPUState *cs) assume that if a CPU really wants to stay asleep, it will mask interrupts at the chipset level, which will prevent these bits from being set in the first place. */ - return cs->interrupt_request & (CPU_INTERRUPT_HARD - | CPU_INTERRUPT_TIMER - | CPU_INTERRUPT_SMP - | CPU_INTERRUPT_MCHK); + return cpu_test_interrupt(cs, CPU_INTERRUPT_HARD + | CPU_INTERRUPT_TIMER + | CPU_INTERRUPT_SMP + | CPU_INTERRUPT_MCHK); } #endif /* !CONFIG_USER_ONLY */ diff --git a/target/arm/cpu.c b/target/arm/cpu.c index e2b2337399..a29c3facbf 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -142,11 +142,11 @@ static bool arm_cpu_has_work(CPUState *cs) ARMCPU *cpu = ARM_CPU(cs); return (cpu->power_state != PSCI_OFF) - && cs->interrupt_request & - (CPU_INTERRUPT_FIQ | CPU_INTERRUPT_HARD - | CPU_INTERRUPT_NMI | CPU_INTERRUPT_VINMI | CPU_INTERRUPT_VFNMI - | CPU_INTERRUPT_VFIQ | CPU_INTERRUPT_VIRQ | CPU_INTERRUPT_VSERR - | CPU_INTERRUPT_EXITTB); + && cpu_test_interrupt(cs, + CPU_INTERRUPT_FIQ | CPU_INTERRUPT_HARD + | CPU_INTERRUPT_NMI | CPU_INTERRUPT_VINMI | CPU_INTERRUPT_VFNMI + | CPU_INTERRUPT_VFIQ | CPU_INTERRUPT_VIRQ | CPU_INTERRUPT_VSERR + | CPU_INTERRUPT_EXITTB); } #endif /* !CONFIG_USER_ONLY */ @@ -958,7 +958,7 @@ void arm_cpu_update_virq(ARMCPU *cpu) !(arm_hcrx_el2_eff(env) & HCRX_VINMI)) || (env->irq_line_state & CPU_INTERRUPT_VIRQ); - if (new_state != ((cs->interrupt_request & CPU_INTERRUPT_VIRQ) != 0)) { + if (new_state != cpu_test_interrupt(cs, CPU_INTERRUPT_VIRQ)) { if (new_state) { cpu_interrupt(cs, CPU_INTERRUPT_VIRQ); } else { @@ -980,7 +980,7 @@ void arm_cpu_update_vfiq(ARMCPU *cpu) !(arm_hcrx_el2_eff(env) & HCRX_VFNMI)) || (env->irq_line_state & CPU_INTERRUPT_VFIQ); - if (new_state != ((cs->interrupt_request & CPU_INTERRUPT_VFIQ) != 0)) { + if (new_state != cpu_test_interrupt(cs, CPU_INTERRUPT_VFIQ)) { if (new_state) { cpu_interrupt(cs, CPU_INTERRUPT_VFIQ); } else { @@ -1002,7 +1002,7 @@ void arm_cpu_update_vinmi(ARMCPU *cpu) (arm_hcrx_el2_eff(env) & HCRX_VINMI)) || (env->irq_line_state & CPU_INTERRUPT_VINMI); - if (new_state != ((cs->interrupt_request & CPU_INTERRUPT_VINMI) != 0)) { + if (new_state != cpu_test_interrupt(cs, CPU_INTERRUPT_VINMI)) { if (new_state) { cpu_interrupt(cs, CPU_INTERRUPT_VINMI); } else { @@ -1022,7 +1022,7 @@ void arm_cpu_update_vfnmi(ARMCPU *cpu) bool new_state = (arm_hcr_el2_eff(env) & HCR_VF) && (arm_hcrx_el2_eff(env) & HCRX_VFNMI); - if (new_state != ((cs->interrupt_request & CPU_INTERRUPT_VFNMI) != 0)) { + if (new_state != cpu_test_interrupt(cs, CPU_INTERRUPT_VFNMI)) { if (new_state) { cpu_interrupt(cs, CPU_INTERRUPT_VFNMI); } else { @@ -1041,7 +1041,7 @@ void arm_cpu_update_vserr(ARMCPU *cpu) bool new_state = env->cp15.hcr_el2 & HCR_VSE; - if (new_state != ((cs->interrupt_request & CPU_INTERRUPT_VSERR) != 0)) { + if (new_state != cpu_test_interrupt(cs, CPU_INTERRUPT_VSERR)) { if (new_state) { cpu_interrupt(cs, CPU_INTERRUPT_VSERR); } else { diff --git a/target/arm/helper.c b/target/arm/helper.c index 0c1299ff84..4cd36e950a 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -833,40 +833,40 @@ static uint64_t isr_read(CPUARMState *env, const ARMCPRegInfo *ri) uint64_t ret = 0; if (hcr_el2 & HCR_IMO) { - if (cs->interrupt_request & CPU_INTERRUPT_VIRQ) { + if (cpu_test_interrupt(cs, CPU_INTERRUPT_VIRQ)) { ret |= CPSR_I; } - if (cs->interrupt_request & CPU_INTERRUPT_VINMI) { + if (cpu_test_interrupt(cs, CPU_INTERRUPT_VINMI)) { ret |= ISR_IS; ret |= CPSR_I; } } else { - if (cs->interrupt_request & CPU_INTERRUPT_HARD) { + if (cpu_test_interrupt(cs, CPU_INTERRUPT_HARD)) { ret |= CPSR_I; } - if (cs->interrupt_request & CPU_INTERRUPT_NMI) { + if (cpu_test_interrupt(cs, CPU_INTERRUPT_NMI)) { ret |= ISR_IS; ret |= CPSR_I; } } if (hcr_el2 & HCR_FMO) { - if (cs->interrupt_request & CPU_INTERRUPT_VFIQ) { + if (cpu_test_interrupt(cs, CPU_INTERRUPT_VFIQ)) { ret |= CPSR_F; } - if (cs->interrupt_request & CPU_INTERRUPT_VFNMI) { + if (cpu_test_interrupt(cs, CPU_INTERRUPT_VFNMI)) { ret |= ISR_FS; ret |= CPSR_F; } } else { - if (cs->interrupt_request & CPU_INTERRUPT_FIQ) { + if (cpu_test_interrupt(cs, CPU_INTERRUPT_FIQ)) { ret |= CPSR_F; } } if (hcr_el2 & HCR_AMO) { - if (cs->interrupt_request & CPU_INTERRUPT_VSERR) { + if (cpu_test_interrupt(cs, CPU_INTERRUPT_VSERR)) { ret |= CPSR_A; } } @@ -9147,7 +9147,7 @@ void arm_cpu_do_interrupt(CPUState *cs) arm_call_el_change_hook(cpu); if (!kvm_enabled()) { - cs->interrupt_request |= CPU_INTERRUPT_EXITTB; + cpu_set_interrupt(cs, CPU_INTERRUPT_EXITTB); } } #endif /* !CONFIG_USER_ONLY */ diff --git a/target/arm/hvf/hvf.c b/target/arm/hvf/hvf.c index 47b0cd3a35..b77db99079 100644 --- a/target/arm/hvf/hvf.c +++ b/target/arm/hvf/hvf.c @@ -1782,13 +1782,13 @@ static int hvf_sysreg_write(CPUState *cpu, uint32_t reg, uint64_t val) static int hvf_inject_interrupts(CPUState *cpu) { - if (cpu->interrupt_request & CPU_INTERRUPT_FIQ) { + if (cpu_test_interrupt(cpu, CPU_INTERRUPT_FIQ)) { trace_hvf_inject_fiq(); hv_vcpu_set_pending_interrupt(cpu->accel->fd, HV_INTERRUPT_TYPE_FIQ, true); } - if (cpu->interrupt_request & CPU_INTERRUPT_HARD) { + if (cpu_test_interrupt(cpu, CPU_INTERRUPT_HARD)) { trace_hvf_inject_irq(); hv_vcpu_set_pending_interrupt(cpu->accel->fd, HV_INTERRUPT_TYPE_IRQ, true); @@ -1840,7 +1840,7 @@ static void hvf_wfi(CPUState *cpu) uint64_t nanos; uint32_t cntfrq; - if (cpu->interrupt_request & (CPU_INTERRUPT_HARD | CPU_INTERRUPT_FIQ)) { + if (cpu_test_interrupt(cpu, CPU_INTERRUPT_HARD | CPU_INTERRUPT_FIQ)) { /* Interrupt pending, no need to wait */ return; } diff --git a/target/avr/cpu.c b/target/avr/cpu.c index 6995de6a12..a6df71d020 100644 --- a/target/avr/cpu.c +++ b/target/avr/cpu.c @@ -45,7 +45,7 @@ static vaddr avr_cpu_get_pc(CPUState *cs) static bool avr_cpu_has_work(CPUState *cs) { - return (cs->interrupt_request & (CPU_INTERRUPT_HARD | CPU_INTERRUPT_RESET)) + return cpu_test_interrupt(cs, CPU_INTERRUPT_HARD | CPU_INTERRUPT_RESET) && cpu_interrupts_enabled(cpu_env(cs)); } diff --git a/target/hppa/cpu.c b/target/hppa/cpu.c index 24777727e6..0ca79ee5e2 100644 --- a/target/hppa/cpu.c +++ b/target/hppa/cpu.c @@ -135,7 +135,7 @@ static void hppa_restore_state_to_opc(CPUState *cs, #ifndef CONFIG_USER_ONLY static bool hppa_cpu_has_work(CPUState *cs) { - return cs->interrupt_request & (CPU_INTERRUPT_HARD | CPU_INTERRUPT_NMI); + return cpu_test_interrupt(cs, CPU_INTERRUPT_HARD | CPU_INTERRUPT_NMI); } #endif /* !CONFIG_USER_ONLY */ diff --git a/target/i386/hvf/hvf.c b/target/i386/hvf/hvf.c index 818b50419f..8445cadece 100644 --- a/target/i386/hvf/hvf.c +++ b/target/i386/hvf/hvf.c @@ -773,9 +773,9 @@ int hvf_vcpu_exec(CPUState *cpu) switch (exit_reason) { case EXIT_REASON_HLT: { macvm_set_rip(cpu, rip + ins_len); - if (!((cpu->interrupt_request & CPU_INTERRUPT_HARD) && + if (!(cpu_test_interrupt(cpu, CPU_INTERRUPT_HARD) && (env->eflags & IF_MASK)) - && !(cpu->interrupt_request & CPU_INTERRUPT_NMI) && + && !cpu_test_interrupt(cpu, CPU_INTERRUPT_NMI) && !(idtvec_info & VMCS_IDT_VEC_VALID)) { cpu->halted = 1; ret = EXCP_HLT; diff --git a/target/i386/hvf/x86hvf.c b/target/i386/hvf/x86hvf.c index 17fce1d3cd..9e05e0e576 100644 --- a/target/i386/hvf/x86hvf.c +++ b/target/i386/hvf/x86hvf.c @@ -395,7 +395,7 @@ bool hvf_inject_interrupts(CPUState *cs) }; } - if (cs->interrupt_request & CPU_INTERRUPT_NMI) { + if (cpu_test_interrupt(cs, CPU_INTERRUPT_NMI)) { if (!(env->hflags2 & HF2_NMI_MASK) && !(info & VMCS_INTR_VALID)) { cs->interrupt_request &= ~CPU_INTERRUPT_NMI; info = VMCS_INTR_VALID | VMCS_INTR_T_NMI | EXCP02_NMI; @@ -406,7 +406,7 @@ bool hvf_inject_interrupts(CPUState *cs) } if (!(env->hflags & HF_INHIBIT_IRQ_MASK) && - (cs->interrupt_request & CPU_INTERRUPT_HARD) && + cpu_test_interrupt(cs, CPU_INTERRUPT_HARD) && (env->eflags & IF_MASK) && !(info & VMCS_INTR_VALID)) { int line = cpu_get_pic_interrupt(env); cs->interrupt_request &= ~CPU_INTERRUPT_HARD; @@ -415,11 +415,10 @@ bool hvf_inject_interrupts(CPUState *cs) VMCS_INTR_VALID | VMCS_INTR_T_HWINTR); } } - if (cs->interrupt_request & CPU_INTERRUPT_HARD) { + if (cpu_test_interrupt(cs, CPU_INTERRUPT_HARD)) { vmx_set_int_window_exiting(cs); } - return (cs->interrupt_request - & (CPU_INTERRUPT_INIT | CPU_INTERRUPT_TPR)); + return cpu_test_interrupt(cs, CPU_INTERRUPT_INIT | CPU_INTERRUPT_TPR); } int hvf_process_events(CPUState *cs) @@ -432,25 +431,25 @@ int hvf_process_events(CPUState *cs) env->eflags = rreg(cs->accel->fd, HV_X86_RFLAGS); } - if (cs->interrupt_request & CPU_INTERRUPT_INIT) { + if (cpu_test_interrupt(cs, CPU_INTERRUPT_INIT)) { cpu_synchronize_state(cs); do_cpu_init(cpu); } - if (cs->interrupt_request & CPU_INTERRUPT_POLL) { + if (cpu_test_interrupt(cs, CPU_INTERRUPT_POLL)) { cs->interrupt_request &= ~CPU_INTERRUPT_POLL; apic_poll_irq(cpu->apic_state); } - if (((cs->interrupt_request & CPU_INTERRUPT_HARD) && + if ((cpu_test_interrupt(cs, CPU_INTERRUPT_HARD) && (env->eflags & IF_MASK)) || - (cs->interrupt_request & CPU_INTERRUPT_NMI)) { + cpu_test_interrupt(cs, CPU_INTERRUPT_NMI)) { cs->halted = 0; } - if (cs->interrupt_request & CPU_INTERRUPT_SIPI) { + if (cpu_test_interrupt(cs, CPU_INTERRUPT_SIPI)) { cpu_synchronize_state(cs); do_cpu_sipi(cpu); } - if (cs->interrupt_request & CPU_INTERRUPT_TPR) { + if (cpu_test_interrupt(cs, CPU_INTERRUPT_TPR)) { cs->interrupt_request &= ~CPU_INTERRUPT_TPR; cpu_synchronize_state(cs); apic_handle_tpr_access_report(cpu->apic_state, env->eip, diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index 369626f8c8..a7b5c8f81b 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -5453,8 +5453,8 @@ void kvm_arch_pre_run(CPUState *cpu, struct kvm_run *run) int ret; /* Inject NMI */ - if (cpu->interrupt_request & (CPU_INTERRUPT_NMI | CPU_INTERRUPT_SMI)) { - if (cpu->interrupt_request & CPU_INTERRUPT_NMI) { + if (cpu_test_interrupt(cpu, CPU_INTERRUPT_NMI | CPU_INTERRUPT_SMI)) { + if (cpu_test_interrupt(cpu, CPU_INTERRUPT_NMI)) { bql_lock(); cpu->interrupt_request &= ~CPU_INTERRUPT_NMI; bql_unlock(); @@ -5465,7 +5465,7 @@ void kvm_arch_pre_run(CPUState *cpu, struct kvm_run *run) strerror(-ret)); } } - if (cpu->interrupt_request & CPU_INTERRUPT_SMI) { + if (cpu_test_interrupt(cpu, CPU_INTERRUPT_SMI)) { bql_lock(); cpu->interrupt_request &= ~CPU_INTERRUPT_SMI; bql_unlock(); @@ -5486,12 +5486,12 @@ void kvm_arch_pre_run(CPUState *cpu, struct kvm_run *run) * or (for userspace APIC, but it is cheap to combine the checks here) * pending TPR access reports. */ - if (cpu->interrupt_request & (CPU_INTERRUPT_INIT | CPU_INTERRUPT_TPR)) { - if ((cpu->interrupt_request & CPU_INTERRUPT_INIT) && + if (cpu_test_interrupt(cpu, CPU_INTERRUPT_INIT | CPU_INTERRUPT_TPR)) { + if (cpu_test_interrupt(cpu, CPU_INTERRUPT_INIT) && !(env->hflags & HF_SMM_MASK)) { cpu->exit_request = 1; } - if (cpu->interrupt_request & CPU_INTERRUPT_TPR) { + if (cpu_test_interrupt(cpu, CPU_INTERRUPT_TPR)) { cpu->exit_request = 1; } } @@ -5499,7 +5499,7 @@ void kvm_arch_pre_run(CPUState *cpu, struct kvm_run *run) if (!kvm_pic_in_kernel()) { /* Try to inject an interrupt if the guest can accept it */ if (run->ready_for_interrupt_injection && - (cpu->interrupt_request & CPU_INTERRUPT_HARD) && + cpu_test_interrupt(cpu, CPU_INTERRUPT_HARD) && (env->eflags & IF_MASK)) { int irq; @@ -5523,7 +5523,7 @@ void kvm_arch_pre_run(CPUState *cpu, struct kvm_run *run) * interrupt, request an interrupt window exit. This will * cause a return to userspace as soon as the guest is ready to * receive interrupts. */ - if ((cpu->interrupt_request & CPU_INTERRUPT_HARD)) { + if (cpu_test_interrupt(cpu, CPU_INTERRUPT_HARD)) { run->request_interrupt_window = 1; } else { run->request_interrupt_window = 0; @@ -5595,7 +5595,7 @@ int kvm_arch_process_async_events(CPUState *cs) X86CPU *cpu = X86_CPU(cs); CPUX86State *env = &cpu->env; - if (cs->interrupt_request & CPU_INTERRUPT_MCE) { + if (cpu_test_interrupt(cs, CPU_INTERRUPT_MCE)) { /* We must not raise CPU_INTERRUPT_MCE if it's not supported. */ assert(env->mcg_cap); @@ -5618,7 +5618,7 @@ int kvm_arch_process_async_events(CPUState *cs) } } - if ((cs->interrupt_request & CPU_INTERRUPT_INIT) && + if (cpu_test_interrupt(cs, CPU_INTERRUPT_INIT) && !(env->hflags & HF_SMM_MASK)) { kvm_cpu_synchronize_state(cs); do_cpu_init(cpu); @@ -5628,20 +5628,20 @@ int kvm_arch_process_async_events(CPUState *cs) return 0; } - if (cs->interrupt_request & CPU_INTERRUPT_POLL) { + if (cpu_test_interrupt(cs, CPU_INTERRUPT_POLL)) { cs->interrupt_request &= ~CPU_INTERRUPT_POLL; apic_poll_irq(cpu->apic_state); } - if (((cs->interrupt_request & CPU_INTERRUPT_HARD) && + if ((cpu_test_interrupt(cs, CPU_INTERRUPT_HARD) && (env->eflags & IF_MASK)) || - (cs->interrupt_request & CPU_INTERRUPT_NMI)) { + cpu_test_interrupt(cs, CPU_INTERRUPT_NMI)) { cs->halted = 0; } - if (cs->interrupt_request & CPU_INTERRUPT_SIPI) { + if (cpu_test_interrupt(cs, CPU_INTERRUPT_SIPI)) { kvm_cpu_synchronize_state(cs); do_cpu_sipi(cpu); } - if (cs->interrupt_request & CPU_INTERRUPT_TPR) { + if (cpu_test_interrupt(cs, CPU_INTERRUPT_TPR)) { cs->interrupt_request &= ~CPU_INTERRUPT_TPR; kvm_cpu_synchronize_state(cs); apic_handle_tpr_access_report(cpu->apic_state, env->eip, @@ -5656,9 +5656,9 @@ static int kvm_handle_halt(X86CPU *cpu) CPUState *cs = CPU(cpu); CPUX86State *env = &cpu->env; - if (!((cs->interrupt_request & CPU_INTERRUPT_HARD) && + if (!(cpu_test_interrupt(cs, CPU_INTERRUPT_HARD) && (env->eflags & IF_MASK)) && - !(cs->interrupt_request & CPU_INTERRUPT_NMI)) { + !cpu_test_interrupt(cs, CPU_INTERRUPT_NMI)) { cs->halted = 1; return EXCP_HLT; } diff --git a/target/i386/nvmm/nvmm-all.c b/target/i386/nvmm/nvmm-all.c index 92e3b8b2f4..c1ac74c4f0 100644 --- a/target/i386/nvmm/nvmm-all.c +++ b/target/i386/nvmm/nvmm-all.c @@ -413,11 +413,11 @@ nvmm_vcpu_pre_run(CPUState *cpu) * Force the VCPU out of its inner loop to process any INIT requests * or commit pending TPR access. */ - if (cpu->interrupt_request & (CPU_INTERRUPT_INIT | CPU_INTERRUPT_TPR)) { + if (cpu_test_interrupt(cpu, CPU_INTERRUPT_INIT | CPU_INTERRUPT_TPR)) { cpu->exit_request = 1; } - if (!has_event && (cpu->interrupt_request & CPU_INTERRUPT_NMI)) { + if (!has_event && cpu_test_interrupt(cpu, CPU_INTERRUPT_NMI)) { if (nvmm_can_take_nmi(cpu)) { cpu->interrupt_request &= ~CPU_INTERRUPT_NMI; event->type = NVMM_VCPU_EVENT_INTR; @@ -426,7 +426,7 @@ nvmm_vcpu_pre_run(CPUState *cpu) } } - if (!has_event && (cpu->interrupt_request & CPU_INTERRUPT_HARD)) { + if (!has_event && cpu_test_interrupt(cpu, CPU_INTERRUPT_HARD)) { if (nvmm_can_take_int(cpu)) { cpu->interrupt_request &= ~CPU_INTERRUPT_HARD; event->type = NVMM_VCPU_EVENT_INTR; @@ -436,7 +436,7 @@ nvmm_vcpu_pre_run(CPUState *cpu) } /* Don't want SMIs. */ - if (cpu->interrupt_request & CPU_INTERRUPT_SMI) { + if (cpu_test_interrupt(cpu, CPU_INTERRUPT_SMI)) { cpu->interrupt_request &= ~CPU_INTERRUPT_SMI; } @@ -651,9 +651,9 @@ nvmm_handle_halted(struct nvmm_machine *mach, CPUState *cpu, bql_lock(); - if (!((cpu->interrupt_request & CPU_INTERRUPT_HARD) && + if (!(cpu_test_interrupt(cpu, CPU_INTERRUPT_HARD) && (cpu_env(cpu)->eflags & IF_MASK)) && - !(cpu->interrupt_request & CPU_INTERRUPT_NMI)) { + !cpu_test_interrupt(cpu, CPU_INTERRUPT_NMI)) { cpu->exception_index = EXCP_HLT; cpu->halted = true; ret = 1; @@ -691,25 +691,25 @@ nvmm_vcpu_loop(CPUState *cpu) * Some asynchronous events must be handled outside of the inner * VCPU loop. They are handled here. */ - if (cpu->interrupt_request & CPU_INTERRUPT_INIT) { + if (cpu_test_interrupt(cpu, CPU_INTERRUPT_INIT)) { nvmm_cpu_synchronize_state(cpu); do_cpu_init(x86_cpu); /* set int/nmi windows back to the reset state */ } - if (cpu->interrupt_request & CPU_INTERRUPT_POLL) { + if (cpu_test_interrupt(cpu, CPU_INTERRUPT_POLL)) { cpu->interrupt_request &= ~CPU_INTERRUPT_POLL; apic_poll_irq(x86_cpu->apic_state); } - if (((cpu->interrupt_request & CPU_INTERRUPT_HARD) && + if ((cpu_test_interrupt(cpu, CPU_INTERRUPT_HARD) && (env->eflags & IF_MASK)) || - (cpu->interrupt_request & CPU_INTERRUPT_NMI)) { + cpu_test_interrupt(cpu, CPU_INTERRUPT_NMI)) { cpu->halted = false; } - if (cpu->interrupt_request & CPU_INTERRUPT_SIPI) { + if (cpu_test_interrupt(cpu, CPU_INTERRUPT_SIPI)) { nvmm_cpu_synchronize_state(cpu); do_cpu_sipi(x86_cpu); } - if (cpu->interrupt_request & CPU_INTERRUPT_TPR) { + if (cpu_test_interrupt(cpu, CPU_INTERRUPT_TPR)) { cpu->interrupt_request &= ~CPU_INTERRUPT_TPR; nvmm_cpu_synchronize_state(cpu); apic_handle_tpr_access_report(x86_cpu->apic_state, env->eip, diff --git a/target/i386/tcg/system/seg_helper.c b/target/i386/tcg/system/seg_helper.c index d4ea890c12..794a23ddfc 100644 --- a/target/i386/tcg/system/seg_helper.c +++ b/target/i386/tcg/system/seg_helper.c @@ -133,7 +133,7 @@ bool x86_cpu_exec_halt(CPUState *cpu) X86CPU *x86_cpu = X86_CPU(cpu); CPUX86State *env = &x86_cpu->env; - if (cpu->interrupt_request & CPU_INTERRUPT_POLL) { + if (cpu_test_interrupt(cpu, CPU_INTERRUPT_POLL)) { bql_lock(); apic_poll_irq(x86_cpu->apic_state); cpu_reset_interrupt(cpu, CPU_INTERRUPT_POLL); diff --git a/target/i386/tcg/system/svm_helper.c b/target/i386/tcg/system/svm_helper.c index dea039b87a..3569196bdd 100644 --- a/target/i386/tcg/system/svm_helper.c +++ b/target/i386/tcg/system/svm_helper.c @@ -403,7 +403,7 @@ void helper_vmrun(CPUX86State *env, int aflag, int next_eip_addend) env->hflags2 |= HF2_GIF_MASK; if (ctl_has_irq(env)) { - cs->interrupt_request |= CPU_INTERRUPT_VIRQ; + cpu_set_interrupt(cs, CPU_INTERRUPT_VIRQ); } if (virtual_gif_set(env)) { diff --git a/target/i386/whpx/whpx-all.c b/target/i386/whpx/whpx-all.c index b72dcff3c8..878cdd1668 100644 --- a/target/i386/whpx/whpx-all.c +++ b/target/i386/whpx/whpx-all.c @@ -1436,9 +1436,9 @@ static int whpx_handle_halt(CPUState *cpu) int ret = 0; bql_lock(); - if (!((cpu->interrupt_request & CPU_INTERRUPT_HARD) && + if (!(cpu_test_interrupt(cpu, CPU_INTERRUPT_HARD) && (cpu_env(cpu)->eflags & IF_MASK)) && - !(cpu->interrupt_request & CPU_INTERRUPT_NMI)) { + !cpu_test_interrupt(cpu, CPU_INTERRUPT_NMI)) { cpu->exception_index = EXCP_HLT; cpu->halted = true; ret = 1; @@ -1469,15 +1469,15 @@ static void whpx_vcpu_pre_run(CPUState *cpu) /* Inject NMI */ if (!vcpu->interruption_pending && - cpu->interrupt_request & (CPU_INTERRUPT_NMI | CPU_INTERRUPT_SMI)) { - if (cpu->interrupt_request & CPU_INTERRUPT_NMI) { + cpu_test_interrupt(cpu, CPU_INTERRUPT_NMI | CPU_INTERRUPT_SMI)) { + if (cpu_test_interrupt(cpu, CPU_INTERRUPT_NMI)) { cpu->interrupt_request &= ~CPU_INTERRUPT_NMI; vcpu->interruptable = false; new_int.InterruptionType = WHvX64PendingNmi; new_int.InterruptionPending = 1; new_int.InterruptionVector = 2; } - if (cpu->interrupt_request & CPU_INTERRUPT_SMI) { + if (cpu_test_interrupt(cpu, CPU_INTERRUPT_SMI)) { cpu->interrupt_request &= ~CPU_INTERRUPT_SMI; } } @@ -1486,12 +1486,12 @@ static void whpx_vcpu_pre_run(CPUState *cpu) * Force the VCPU out of its inner loop to process any INIT requests or * commit pending TPR access. */ - if (cpu->interrupt_request & (CPU_INTERRUPT_INIT | CPU_INTERRUPT_TPR)) { - if ((cpu->interrupt_request & CPU_INTERRUPT_INIT) && + if (cpu_test_interrupt(cpu, CPU_INTERRUPT_INIT | CPU_INTERRUPT_TPR)) { + if (cpu_test_interrupt(cpu, CPU_INTERRUPT_INIT) && !(env->hflags & HF_SMM_MASK)) { cpu->exit_request = 1; } - if (cpu->interrupt_request & CPU_INTERRUPT_TPR) { + if (cpu_test_interrupt(cpu, CPU_INTERRUPT_TPR)) { cpu->exit_request = 1; } } @@ -1501,7 +1501,7 @@ static void whpx_vcpu_pre_run(CPUState *cpu) if (!vcpu->interruption_pending && vcpu->interruptable && (env->eflags & IF_MASK)) { assert(!new_int.InterruptionPending); - if (cpu->interrupt_request & CPU_INTERRUPT_HARD) { + if (cpu_test_interrupt(cpu, CPU_INTERRUPT_HARD)) { cpu->interrupt_request &= ~CPU_INTERRUPT_HARD; irq = cpu_get_pic_interrupt(env); if (irq >= 0) { @@ -1519,7 +1519,7 @@ static void whpx_vcpu_pre_run(CPUState *cpu) reg_count += 1; } } else if (vcpu->ready_for_pic_interrupt && - (cpu->interrupt_request & CPU_INTERRUPT_HARD)) { + cpu_test_interrupt(cpu, CPU_INTERRUPT_HARD)) { cpu->interrupt_request &= ~CPU_INTERRUPT_HARD; irq = cpu_get_pic_interrupt(env); if (irq >= 0) { @@ -1546,7 +1546,7 @@ static void whpx_vcpu_pre_run(CPUState *cpu) /* Update the state of the interrupt delivery notification */ if (!vcpu->window_registered && - cpu->interrupt_request & CPU_INTERRUPT_HARD) { + cpu_test_interrupt(cpu, CPU_INTERRUPT_HARD)) { reg_values[reg_count].DeliverabilityNotifications = (WHV_X64_DELIVERABILITY_NOTIFICATIONS_REGISTER) { .InterruptNotification = 1 @@ -1599,30 +1599,30 @@ static void whpx_vcpu_process_async_events(CPUState *cpu) CPUX86State *env = &x86_cpu->env; AccelCPUState *vcpu = cpu->accel; - if ((cpu->interrupt_request & CPU_INTERRUPT_INIT) && + if (cpu_test_interrupt(cpu, CPU_INTERRUPT_INIT) && !(env->hflags & HF_SMM_MASK)) { whpx_cpu_synchronize_state(cpu); do_cpu_init(x86_cpu); vcpu->interruptable = true; } - if (cpu->interrupt_request & CPU_INTERRUPT_POLL) { + if (cpu_test_interrupt(cpu, CPU_INTERRUPT_POLL)) { cpu->interrupt_request &= ~CPU_INTERRUPT_POLL; apic_poll_irq(x86_cpu->apic_state); } - if (((cpu->interrupt_request & CPU_INTERRUPT_HARD) && + if ((cpu_test_interrupt(cpu, CPU_INTERRUPT_HARD) && (env->eflags & IF_MASK)) || - (cpu->interrupt_request & CPU_INTERRUPT_NMI)) { + cpu_test_interrupt(cpu, CPU_INTERRUPT_NMI)) { cpu->halted = false; } - if (cpu->interrupt_request & CPU_INTERRUPT_SIPI) { + if (cpu_test_interrupt(cpu, CPU_INTERRUPT_SIPI)) { whpx_cpu_synchronize_state(cpu); do_cpu_sipi(x86_cpu); } - if (cpu->interrupt_request & CPU_INTERRUPT_TPR) { + if (cpu_test_interrupt(cpu, CPU_INTERRUPT_TPR)) { cpu->interrupt_request &= ~CPU_INTERRUPT_TPR; whpx_cpu_synchronize_state(cpu); apic_handle_tpr_access_report(x86_cpu->apic_state, env->eip, diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c index abad84c054..3a7621c0ea 100644 --- a/target/loongarch/cpu.c +++ b/target/loongarch/cpu.c @@ -376,7 +376,7 @@ static bool loongarch_cpu_has_work(CPUState *cs) { bool has_work = false; - if ((cs->interrupt_request & CPU_INTERRUPT_HARD) && + if (cpu_test_interrupt(cs, CPU_INTERRUPT_HARD) && cpu_loongarch_hw_interrupts_pending(cpu_env(cs))) { has_work = true; } diff --git a/target/m68k/cpu.c b/target/m68k/cpu.c index 6a09db3a6f..f1b673119d 100644 --- a/target/m68k/cpu.c +++ b/target/m68k/cpu.c @@ -74,7 +74,7 @@ static void m68k_restore_state_to_opc(CPUState *cs, #ifndef CONFIG_USER_ONLY static bool m68k_cpu_has_work(CPUState *cs) { - return cs->interrupt_request & CPU_INTERRUPT_HARD; + return cpu_test_interrupt(cs, CPU_INTERRUPT_HARD); } #endif /* !CONFIG_USER_ONLY */ diff --git a/target/microblaze/cpu.c b/target/microblaze/cpu.c index ee0a869a94..22231f09e6 100644 --- a/target/microblaze/cpu.c +++ b/target/microblaze/cpu.c @@ -129,7 +129,7 @@ static void mb_restore_state_to_opc(CPUState *cs, #ifndef CONFIG_USER_ONLY static bool mb_cpu_has_work(CPUState *cs) { - return cs->interrupt_request & (CPU_INTERRUPT_HARD | CPU_INTERRUPT_NMI); + return cpu_test_interrupt(cs, CPU_INTERRUPT_HARD | CPU_INTERRUPT_NMI); } #endif /* !CONFIG_USER_ONLY */ diff --git a/target/mips/cpu.c b/target/mips/cpu.c index 1f6c41fd34..5989c3ba17 100644 --- a/target/mips/cpu.c +++ b/target/mips/cpu.c @@ -145,7 +145,7 @@ static bool mips_cpu_has_work(CPUState *cs) * check for interrupts that can be taken. For pre-release 6 CPUs, * check for CP0 Config7 'Wait IE ignore' bit. */ - if ((cs->interrupt_request & CPU_INTERRUPT_HARD) && + if (cpu_test_interrupt(cs, CPU_INTERRUPT_HARD) && cpu_mips_hw_interrupts_pending(env)) { if (cpu_mips_hw_interrupts_enabled(env) || (env->CP0_Config7 & (1 << CP0C7_WII)) || @@ -160,7 +160,7 @@ static bool mips_cpu_has_work(CPUState *cs) * The QEMU model will issue an _WAKE request whenever the CPUs * should be woken up. */ - if (cs->interrupt_request & CPU_INTERRUPT_WAKE) { + if (cpu_test_interrupt(cs, CPU_INTERRUPT_WAKE)) { has_work = true; } @@ -170,7 +170,7 @@ static bool mips_cpu_has_work(CPUState *cs) } /* MIPS Release 6 has the ability to halt the CPU. */ if (env->CP0_Config5 & (1 << CP0C5_VP)) { - if (cs->interrupt_request & CPU_INTERRUPT_WAKE) { + if (cpu_test_interrupt(cs, CPU_INTERRUPT_WAKE)) { has_work = true; } if (!mips_vp_active(env)) { diff --git a/target/mips/kvm.c b/target/mips/kvm.c index ec53acb51a..450947c3fa 100644 --- a/target/mips/kvm.c +++ b/target/mips/kvm.c @@ -144,7 +144,7 @@ void kvm_arch_pre_run(CPUState *cs, struct kvm_run *run) bql_lock(); - if ((cs->interrupt_request & CPU_INTERRUPT_HARD) && + if (cpu_test_interrupt(cs, CPU_INTERRUPT_HARD) && cpu_mips_io_interrupts_pending(cpu)) { intr.cpu = -1; intr.irq = 2; diff --git a/target/openrisc/cpu.c b/target/openrisc/cpu.c index dfbb2df643..9bbfe22ed3 100644 --- a/target/openrisc/cpu.c +++ b/target/openrisc/cpu.c @@ -78,8 +78,7 @@ static void openrisc_restore_state_to_opc(CPUState *cs, #ifndef CONFIG_USER_ONLY static bool openrisc_cpu_has_work(CPUState *cs) { - return cs->interrupt_request & (CPU_INTERRUPT_HARD | - CPU_INTERRUPT_TIMER); + return cpu_test_interrupt(cs, CPU_INTERRUPT_HARD | CPU_INTERRUPT_TIMER); } #endif /* !CONFIG_USER_ONLY */ diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c index a0e77f2673..db841f1260 100644 --- a/target/ppc/cpu_init.c +++ b/target/ppc/cpu_init.c @@ -7225,7 +7225,7 @@ static int ppc_cpu_mmu_index(CPUState *cs, bool ifetch) #ifndef CONFIG_USER_ONLY static bool ppc_cpu_has_work(CPUState *cs) { - return cs->interrupt_request & CPU_INTERRUPT_HARD; + return cpu_test_interrupt(cs, CPU_INTERRUPT_HARD); } #endif /* !CONFIG_USER_ONLY */ diff --git a/target/ppc/kvm.c b/target/ppc/kvm.c index 015658049e..d145774b09 100644 --- a/target/ppc/kvm.c +++ b/target/ppc/kvm.c @@ -1354,7 +1354,7 @@ static int kvmppc_handle_halt(PowerPCCPU *cpu) CPUState *cs = CPU(cpu); CPUPPCState *env = &cpu->env; - if (!(cs->interrupt_request & CPU_INTERRUPT_HARD) && + if (!cpu_test_interrupt(cs, CPU_INTERRUPT_HARD) && FIELD_EX64(env->msr, MSR, EE)) { cs->halted = 1; cs->exception_index = EXCP_HLT; diff --git a/target/rx/cpu.c b/target/rx/cpu.c index c6dd5d6f83..da02ae7bf8 100644 --- a/target/rx/cpu.c +++ b/target/rx/cpu.c @@ -75,8 +75,7 @@ static void rx_restore_state_to_opc(CPUState *cs, static bool rx_cpu_has_work(CPUState *cs) { - return cs->interrupt_request & - (CPU_INTERRUPT_HARD | CPU_INTERRUPT_FIR); + return cpu_test_interrupt(cs, CPU_INTERRUPT_HARD | CPU_INTERRUPT_FIR); } static int rx_cpu_mmu_index(CPUState *cs, bool ifunc) diff --git a/target/rx/helper.c b/target/rx/helper.c index 0640ab322b..ce003af421 100644 --- a/target/rx/helper.c +++ b/target/rx/helper.c @@ -44,7 +44,7 @@ void rx_cpu_unpack_psw(CPURXState *env, uint32_t psw, int rte) void rx_cpu_do_interrupt(CPUState *cs) { CPURXState *env = cpu_env(cs); - int do_irq = cs->interrupt_request & INT_FLAGS; + int do_irq = cpu_test_interrupt(cs, INT_FLAGS); uint32_t save_psw; env->in_sleep = 0; diff --git a/target/s390x/cpu-system.c b/target/s390x/cpu-system.c index 709ccd5299..f3a9ffb2a2 100644 --- a/target/s390x/cpu-system.c +++ b/target/s390x/cpu-system.c @@ -49,7 +49,7 @@ bool s390_cpu_has_work(CPUState *cs) return false; } - if (!(cs->interrupt_request & CPU_INTERRUPT_HARD)) { + if (!cpu_test_interrupt(cs, CPU_INTERRUPT_HARD)) { return false; } diff --git a/target/sh4/cpu.c b/target/sh4/cpu.c index 4f561e8c91..21ccb86df4 100644 --- a/target/sh4/cpu.c +++ b/target/sh4/cpu.c @@ -108,7 +108,7 @@ static bool superh_io_recompile_replay_branch(CPUState *cs, static bool superh_cpu_has_work(CPUState *cs) { - return cs->interrupt_request & CPU_INTERRUPT_HARD; + return cpu_test_interrupt(cs, CPU_INTERRUPT_HARD); } #endif /* !CONFIG_USER_ONLY */ diff --git a/target/sh4/helper.c b/target/sh4/helper.c index fb7642bda1..1744ef0e6d 100644 --- a/target/sh4/helper.c +++ b/target/sh4/helper.c @@ -58,7 +58,7 @@ int cpu_sh4_is_cached(CPUSH4State *env, target_ulong addr) void superh_cpu_do_interrupt(CPUState *cs) { CPUSH4State *env = cpu_env(cs); - int do_irq = cs->interrupt_request & CPU_INTERRUPT_HARD; + int do_irq = cpu_test_interrupt(cs, CPU_INTERRUPT_HARD); int do_exp, irq_vector = cs->exception_index; /* prioritize exceptions over interrupts */ diff --git a/target/sparc/cpu.c b/target/sparc/cpu.c index 245caf2de0..c9773f1540 100644 --- a/target/sparc/cpu.c +++ b/target/sparc/cpu.c @@ -783,7 +783,7 @@ static void sparc_restore_state_to_opc(CPUState *cs, #ifndef CONFIG_USER_ONLY static bool sparc_cpu_has_work(CPUState *cs) { - return (cs->interrupt_request & CPU_INTERRUPT_HARD) && + return cpu_test_interrupt(cs, CPU_INTERRUPT_HARD) && cpu_interrupts_enabled(cpu_env(cs)); } #endif /* !CONFIG_USER_ONLY */ diff --git a/target/sparc/int64_helper.c b/target/sparc/int64_helper.c index bd14c7a0db..49e4e51c6d 100644 --- a/target/sparc/int64_helper.c +++ b/target/sparc/int64_helper.c @@ -89,7 +89,7 @@ void cpu_check_irqs(CPUSPARCState *env) * the next bit is (2 << psrpil). */ if (pil < (2 << env->psrpil)) { - if (cs->interrupt_request & CPU_INTERRUPT_HARD) { + if (cpu_test_interrupt(cs, CPU_INTERRUPT_HARD)) { trace_sparc64_cpu_check_irqs_reset_irq(env->interrupt_index); env->interrupt_index = 0; cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); @@ -120,7 +120,7 @@ void cpu_check_irqs(CPUSPARCState *env) break; } } - } else if (cs->interrupt_request & CPU_INTERRUPT_HARD) { + } else if (cpu_test_interrupt(cs, CPU_INTERRUPT_HARD)) { trace_sparc64_cpu_check_irqs_disabled(pil, env->pil_in, env->softint, env->interrupt_index); env->interrupt_index = 0; From 73c520b088878682e2d3b7fa19a6366ec8d39829 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Thu, 14 Aug 2025 18:05:53 +0200 Subject: [PATCH 0130/2396] memory: reintroduce BQL-free fine-grained PIO/MMIO This patch brings back Jan's idea [1] of BQL-free IO access This will let us make access to ACPI PM/HPET timers cheaper, and prevent BQL contention in case of workload that heavily uses the timers with a lot of vCPUs. 1) 196ea13104f (memory: Add global-locking property to memory regions) ... de7ea885c539 (kvm: Switch to unlocked MMIO) Signed-off-by: Igor Mammedov Reviewed-by: Peter Xu Link: https://lore.kernel.org/r/20250814160600.2327672-2-imammedo@redhat.com Signed-off-by: Paolo Bonzini --- include/system/memory.h | 12 ++++++++++++ system/memory.c | 15 +++++++++++++++ system/physmem.c | 2 +- 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/include/system/memory.h b/include/system/memory.h index e2cd6ed126..aa85fc27a1 100644 --- a/include/system/memory.h +++ b/include/system/memory.h @@ -833,6 +833,7 @@ struct MemoryRegion { bool nonvolatile; bool rom_device; bool flush_coalesced_mmio; + bool lockless_io; bool unmergeable; uint8_t dirty_log_mask; bool is_iommu; @@ -2341,6 +2342,17 @@ void memory_region_set_flush_coalesced(MemoryRegion *mr); */ void memory_region_clear_flush_coalesced(MemoryRegion *mr); +/** + * memory_region_enable_lockless_io: Enable lockless (BQL free) acceess. + * + * Enable BQL-free access for devices that are well prepared to handle + * locking during I/O themselves: either by doing fine grained locking or + * by providing lock-free I/O schemes. + * + * @mr: the memory region to be updated. + */ +void memory_region_enable_lockless_io(MemoryRegion *mr); + /** * memory_region_add_eventfd: Request an eventfd to be triggered when a word * is written to a location. diff --git a/system/memory.c b/system/memory.c index 5646547940..44701c465c 100644 --- a/system/memory.c +++ b/system/memory.c @@ -2546,6 +2546,21 @@ void memory_region_clear_flush_coalesced(MemoryRegion *mr) } } +void memory_region_enable_lockless_io(MemoryRegion *mr) +{ + mr->lockless_io = true; + /* + * reentrancy_guard has per device scope, that when enabled + * will effectively prevent concurrent access to device's IO + * MemoryRegion(s) by not calling accessor callback. + * + * Turn it off for lock-less IO enabled devices, to allow + * concurrent IO. + * TODO: remove this when reentrancy_guard becomes per transaction. + */ + mr->disable_reentrancy_guard = true; +} + void memory_region_add_eventfd(MemoryRegion *mr, hwaddr addr, unsigned size, diff --git a/system/physmem.c b/system/physmem.c index e5dd760e0b..f498572fc8 100644 --- a/system/physmem.c +++ b/system/physmem.c @@ -2900,7 +2900,7 @@ bool prepare_mmio_access(MemoryRegion *mr) { bool release_lock = false; - if (!bql_locked()) { + if (!bql_locked() && !mr->lockless_io) { bql_lock(); release_lock = true; } From 4ae5e2b2bf65452538511a2895d7a4e2115058a5 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Thu, 14 Aug 2025 18:05:54 +0200 Subject: [PATCH 0131/2396] acpi: mark PMTIMER as unlocked Reading QEMU_CLOCK_VIRTUAL is thread-safe, write access is NOP. This makes possible to boot Windows with large vCPUs count when hv-time is not used. Reproducer: -M q35,hpet=off -cpu host -enable-kvm -smp 240,sockets=4 -m 8G WS2025.img fails to boot within 30min. With this fix it boots within 2-1min. Signed-off-by: Igor Mammedov Reviewed-by: Peter Xu Acked-by: Michael S. Tsirkin Link: https://lore.kernel.org/r/20250814160600.2327672-3-imammedo@redhat.com Signed-off-by: Paolo Bonzini --- hw/acpi/core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/acpi/core.c b/hw/acpi/core.c index 58f8964e13..ff16582803 100644 --- a/hw/acpi/core.c +++ b/hw/acpi/core.c @@ -547,6 +547,7 @@ void acpi_pm_tmr_init(ACPIREGS *ar, acpi_update_sci_fn update_sci, ar->tmr.timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, acpi_pm_tmr_timer, ar); memory_region_init_io(&ar->tmr.io, memory_region_owner(parent), &acpi_pm_tmr_ops, ar, "acpi-tmr", 4); + memory_region_enable_lockless_io(&ar->tmr.io); memory_region_add_subregion(parent, 8, &ar->tmr.io); } From 7defb58bafac8dcb23c06be5e4f2d1a33d8392fd Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Thu, 14 Aug 2025 18:05:55 +0200 Subject: [PATCH 0132/2396] hpet: switch to fine-grained device locking as a step towards lock-less HPET counter read, use per device locking instead of BQL. Signed-off-by: Igor Mammedov Reviewed-by: Peter Xu Link: https://lore.kernel.org/r/20250814160600.2327672-4-imammedo@redhat.com Signed-off-by: Paolo Bonzini --- hw/timer/hpet.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/hw/timer/hpet.c b/hw/timer/hpet.c index cb48cc151f..ab5aa59ae4 100644 --- a/hw/timer/hpet.c +++ b/hw/timer/hpet.c @@ -38,6 +38,7 @@ #include "hw/timer/i8254.h" #include "system/address-spaces.h" #include "qom/object.h" +#include "qemu/lockable.h" #include "trace.h" struct hpet_fw_config hpet_fw_cfg = {.count = UINT8_MAX}; @@ -69,6 +70,7 @@ struct HPETState { SysBusDevice parent_obj; /*< public >*/ + QemuMutex lock; MemoryRegion iomem; uint64_t hpet_offset; bool hpet_offset_saved; @@ -428,6 +430,7 @@ static uint64_t hpet_ram_read(void *opaque, hwaddr addr, trace_hpet_ram_read(addr); addr &= ~4; + QEMU_LOCK_GUARD(&s->lock); /*address range of all global regs*/ if (addr <= 0xff) { switch (addr) { @@ -482,6 +485,7 @@ static void hpet_ram_write(void *opaque, hwaddr addr, int len = MIN(size * 8, 64 - shift); uint64_t old_val, new_val, cleared; + QEMU_LOCK_GUARD(&s->lock); trace_hpet_ram_write(addr, value); addr &= ~4; @@ -679,8 +683,10 @@ static void hpet_init(Object *obj) SysBusDevice *sbd = SYS_BUS_DEVICE(obj); HPETState *s = HPET(obj); + qemu_mutex_init(&s->lock); /* HPET Area */ memory_region_init_io(&s->iomem, obj, &hpet_ram_ops, s, "hpet", HPET_LEN); + memory_region_enable_lockless_io(&s->iomem); sysbus_init_mmio(sbd, &s->iomem); } From a453bf0354412592362139bdf4df0d4900ec0686 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Thu, 14 Aug 2025 18:05:56 +0200 Subject: [PATCH 0133/2396] hpet: move out main counter read into a separate block Follow up patche will switch main counter read to lock-less mode. As preparation for that move relevant branch into a separate top level block to make followup patch cleaner/simplier by reducing contextual noise when lock-less read is introduced. no functional changes. Signed-off-by: Igor Mammedov Reviewed-by: Peter Xu Link: https://lore.kernel.org/r/20250814160600.2327672-5-imammedo@redhat.com Signed-off-by: Paolo Bonzini --- hw/timer/hpet.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/hw/timer/hpet.c b/hw/timer/hpet.c index ab5aa59ae4..c776afc0f2 100644 --- a/hw/timer/hpet.c +++ b/hw/timer/hpet.c @@ -431,6 +431,16 @@ static uint64_t hpet_ram_read(void *opaque, hwaddr addr, addr &= ~4; QEMU_LOCK_GUARD(&s->lock); + if (addr == HPET_COUNTER) { + if (hpet_enabled(s)) { + cur_tick = hpet_get_ticks(s); + } else { + cur_tick = s->hpet_counter; + } + trace_hpet_ram_read_reading_counter(addr & 4, cur_tick); + return cur_tick >> shift; + } + /*address range of all global regs*/ if (addr <= 0xff) { switch (addr) { @@ -438,14 +448,6 @@ static uint64_t hpet_ram_read(void *opaque, hwaddr addr, return s->capability >> shift; case HPET_CFG: return s->config >> shift; - case HPET_COUNTER: - if (hpet_enabled(s)) { - cur_tick = hpet_get_ticks(s); - } else { - cur_tick = s->hpet_counter; - } - trace_hpet_ram_read_reading_counter(addr & 4, cur_tick); - return cur_tick >> shift; case HPET_STATUS: return s->isr >> shift; default: From 20c2345290f34aac434284cf9a242c7904d39a27 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Thu, 14 Aug 2025 18:05:57 +0200 Subject: [PATCH 0134/2396] hpet: make main counter read lock-less Make access to main HPET counter lock-less. In unlikely event of an update in progress, readers will busy wait untill update is finished. As result micro benchmark of concurrent reading of HPET counter with large number of vCPU shows over 80% better (less) latency. Signed-off-by: Igor Mammedov Reviewed-by: Peter Xu Link: https://lore.kernel.org/r/20250814160600.2327672-6-imammedo@redhat.com Signed-off-by: Paolo Bonzini --- hw/timer/hpet.c | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/hw/timer/hpet.c b/hw/timer/hpet.c index c776afc0f2..789a31d0a0 100644 --- a/hw/timer/hpet.c +++ b/hw/timer/hpet.c @@ -39,6 +39,7 @@ #include "system/address-spaces.h" #include "qom/object.h" #include "qemu/lockable.h" +#include "qemu/seqlock.h" #include "trace.h" struct hpet_fw_config hpet_fw_cfg = {.count = UINT8_MAX}; @@ -74,6 +75,7 @@ struct HPETState { MemoryRegion iomem; uint64_t hpet_offset; bool hpet_offset_saved; + QemuSeqLock state_version; qemu_irq irqs[HPET_NUM_IRQ_ROUTES]; uint32_t flags; uint8_t rtc_irq_level; @@ -430,17 +432,25 @@ static uint64_t hpet_ram_read(void *opaque, hwaddr addr, trace_hpet_ram_read(addr); addr &= ~4; - QEMU_LOCK_GUARD(&s->lock); if (addr == HPET_COUNTER) { - if (hpet_enabled(s)) { - cur_tick = hpet_get_ticks(s); - } else { - cur_tick = s->hpet_counter; - } + unsigned version; + + /* + * Write update is rare, so busywait here is unlikely to happen + */ + do { + version = seqlock_read_begin(&s->state_version); + if (unlikely(!hpet_enabled(s))) { + cur_tick = s->hpet_counter; + } else { + cur_tick = hpet_get_ticks(s); + } + } while (seqlock_read_retry(&s->state_version, version)); trace_hpet_ram_read_reading_counter(addr & 4, cur_tick); return cur_tick >> shift; } + QEMU_LOCK_GUARD(&s->lock); /*address range of all global regs*/ if (addr <= 0xff) { switch (addr) { @@ -500,6 +510,7 @@ static void hpet_ram_write(void *opaque, hwaddr addr, old_val = s->config; new_val = deposit64(old_val, shift, len, value); new_val = hpet_fixup_reg(new_val, old_val, HPET_CFG_WRITE_MASK); + seqlock_write_begin(&s->state_version); s->config = new_val; if (activating_bit(old_val, new_val, HPET_CFG_ENABLE)) { /* Enable main counter and interrupt generation. */ @@ -518,6 +529,8 @@ static void hpet_ram_write(void *opaque, hwaddr addr, hpet_del_timer(&s->timer[i]); } } + seqlock_write_end(&s->state_version); + /* i8254 and RTC output pins are disabled * when HPET is in legacy mode */ if (activating_bit(old_val, new_val, HPET_CFG_LEGACY)) { @@ -686,6 +699,7 @@ static void hpet_init(Object *obj) HPETState *s = HPET(obj); qemu_mutex_init(&s->lock); + seqlock_init(&s->state_version); /* HPET Area */ memory_region_init_io(&s->iomem, obj, &hpet_ram_ops, s, "hpet", HPET_LEN); memory_region_enable_lockless_io(&s->iomem); From 17e645c6f17999cd0306c4d18d6f6cb3db55756d Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Thu, 14 Aug 2025 18:05:59 +0200 Subject: [PATCH 0135/2396] kvm: i386: irqchip: take BQL only if there is an interrupt when kernel-irqchip=split is used, QEMU still hits BQL contention issue when reading ACPI PM/HPET timers (despite of timer[s] access being lock-less). So Windows with more than 255 cpus is still not able to boot (since it requires iommu -> split irqchip). Problematic path is in kvm_arch_pre_run() where BQL is taken unconditionally when split irqchip is in use. There are a few parts that BQL protects there: 1. interrupt check and injecting however we do not take BQL when checking for pending interrupt (even within the same function), so the patch takes the same approach for cpu->interrupt_request checks and takes BQL only if there is a job to do. 2. request_interrupt_window access CPUState::kvm_run::request_interrupt_window doesn't need BQL as it's accessed by its own vCPU thread. 3. cr8/cpu_get_apic_tpr access the same (as #2) applies to CPUState::kvm_run::cr8, and APIC registers are also cached/synced (get/put) within the vCPU thread it belongs to. Taking BQL only when is necessary, eleminates BQL bottleneck on IO/MMIO only exit path, improoving latency by 80% on HPET micro benchmark. This lets Windows to boot succesfully (in case hv-time isn't used) when more than 255 vCPUs are in use. Signed-off-by: Igor Mammedov Reviewed-by: Peter Xu Link: https://lore.kernel.org/r/20250814160600.2327672-8-imammedo@redhat.com Signed-off-by: Paolo Bonzini --- target/i386/kvm/kvm.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index a7b5c8f81b..306430a052 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -5478,9 +5478,6 @@ void kvm_arch_pre_run(CPUState *cpu, struct kvm_run *run) } } - if (!kvm_pic_in_kernel()) { - bql_lock(); - } /* Force the VCPU out of its inner loop to process any INIT requests * or (for userspace APIC, but it is cheap to combine the checks here) @@ -5489,10 +5486,10 @@ void kvm_arch_pre_run(CPUState *cpu, struct kvm_run *run) if (cpu_test_interrupt(cpu, CPU_INTERRUPT_INIT | CPU_INTERRUPT_TPR)) { if (cpu_test_interrupt(cpu, CPU_INTERRUPT_INIT) && !(env->hflags & HF_SMM_MASK)) { - cpu->exit_request = 1; + qatomic_set(&cpu->exit_request, 1); } if (cpu_test_interrupt(cpu, CPU_INTERRUPT_TPR)) { - cpu->exit_request = 1; + qatomic_set(&cpu->exit_request, 1); } } @@ -5503,6 +5500,8 @@ void kvm_arch_pre_run(CPUState *cpu, struct kvm_run *run) (env->eflags & IF_MASK)) { int irq; + bql_lock(); + cpu->interrupt_request &= ~CPU_INTERRUPT_HARD; irq = cpu_get_pic_interrupt(env); if (irq >= 0) { @@ -5517,6 +5516,7 @@ void kvm_arch_pre_run(CPUState *cpu, struct kvm_run *run) strerror(-ret)); } } + bql_unlock(); } /* If we have an interrupt but the guest is not ready to receive an @@ -5531,8 +5531,6 @@ void kvm_arch_pre_run(CPUState *cpu, struct kvm_run *run) DPRINTF("setting tpr\n"); run->cr8 = cpu_get_apic_tpr(x86_cpu->apic_state); - - bql_unlock(); } } From 83bd8e65bc70cef03a207df315004f8b1301dc53 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Thu, 14 Aug 2025 18:06:00 +0200 Subject: [PATCH 0136/2396] tcg: move interrupt caching and single step masking closer to user in cpu_handle_interrupt() the only place where cached interrupt_request might have effect is when CPU_INTERRUPT_SSTEP_MASK applied and cached interrupt_request handed over to cpu_exec_interrupt() and need_replay_interrupt(). Simplify code by moving interrupt_request caching and CPU_INTERRUPT_SSTEP_MASK masking into the block where it actually matters and drop reloading cached value from CPUState:interrupt_request as the rest of the code directly uses CPUState:interrupt_request. Signed-off-by: Igor Mammedov Link: https://lore.kernel.org/r/20250814160600.2327672-9-imammedo@redhat.com Signed-off-by: Paolo Bonzini --- accel/tcg/cpu-exec.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index 96c124aa72..8491e5badd 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -782,13 +782,7 @@ static inline bool cpu_handle_interrupt(CPUState *cpu, assert(!cpu_test_interrupt(cpu, ~0)); #else if (unlikely(cpu_test_interrupt(cpu, ~0))) { - int interrupt_request; bql_lock(); - interrupt_request = cpu->interrupt_request; - if (unlikely(cpu->singlestep_enabled & SSTEP_NOIRQ)) { - /* Mask out external interrupts for this step. */ - interrupt_request &= ~CPU_INTERRUPT_SSTEP_MASK; - } if (cpu_test_interrupt(cpu, CPU_INTERRUPT_DEBUG)) { cpu->interrupt_request &= ~CPU_INTERRUPT_DEBUG; cpu->exception_index = EXCP_DEBUG; @@ -806,6 +800,7 @@ static inline bool cpu_handle_interrupt(CPUState *cpu, return true; } else { const TCGCPUOps *tcg_ops = cpu->cc->tcg_ops; + int interrupt_request = cpu->interrupt_request; if (cpu_test_interrupt(cpu, CPU_INTERRUPT_RESET)) { replay_interrupt(); @@ -814,6 +809,11 @@ static inline bool cpu_handle_interrupt(CPUState *cpu, return true; } + if (unlikely(cpu->singlestep_enabled & SSTEP_NOIRQ)) { + /* Mask out external interrupts for this step. */ + interrupt_request &= ~CPU_INTERRUPT_SSTEP_MASK; + } + /* * The target hook has 3 exit conditions: * False when the interrupt isn't processed, @@ -838,9 +838,6 @@ static inline bool cpu_handle_interrupt(CPUState *cpu, cpu->exception_index = -1; *last_tb = NULL; } - /* The target hook may have updated the 'cpu->interrupt_request'; - * reload the 'interrupt_request' value */ - interrupt_request = cpu->interrupt_request; } if (cpu_test_interrupt(cpu, CPU_INTERRUPT_EXITTB)) { cpu->interrupt_request &= ~CPU_INTERRUPT_EXITTB; From f3d9393791e6c02bae99f920d350e65cd299fed1 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 27 Aug 2025 15:27:50 +1000 Subject: [PATCH 0137/2396] hw/core: Dump cpu_reset in the reset.exit phase MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit During reset.hold, the cpu is in an inconsistent state, where the leaf class has not had a chance to initialize state at all. This is visible as a SIGSEGV in "qemu-system-sparc64 -d cpu_reset". Move the dump to the exit phase, where all initialization is certain to be complete. Reported-by: Henk van der Laak Reviewed-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- hw/core/cpu-common.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/hw/core/cpu-common.c b/hw/core/cpu-common.c index 39e674aca2..26321be785 100644 --- a/hw/core/cpu-common.c +++ b/hw/core/cpu-common.c @@ -119,11 +119,6 @@ static void cpu_common_reset_hold(Object *obj, ResetType type) { CPUState *cpu = CPU(obj); - if (qemu_loglevel_mask(CPU_LOG_RESET)) { - qemu_log("CPU Reset (CPU %d)\n", cpu->cpu_index); - log_cpu_state(cpu, cpu->cc->reset_dump_flags); - } - cpu->interrupt_request = 0; cpu->halted = cpu->start_powered_off; cpu->mem_io_pc = 0; @@ -137,6 +132,16 @@ static void cpu_common_reset_hold(Object *obj, ResetType type) cpu_exec_reset_hold(cpu); } +static void cpu_common_reset_exit(Object *obj, ResetType type) +{ + if (qemu_loglevel_mask(CPU_LOG_RESET)) { + CPUState *cpu = CPU(obj); + + qemu_log("CPU Reset (CPU %d)\n", cpu->cpu_index); + log_cpu_state(cpu, cpu->cc->reset_dump_flags); + } +} + ObjectClass *cpu_class_by_name(const char *typename, const char *cpu_model) { ObjectClass *oc; @@ -380,6 +385,7 @@ static void cpu_common_class_init(ObjectClass *klass, const void *data) dc->realize = cpu_common_realizefn; dc->unrealize = cpu_common_unrealizefn; rc->phases.hold = cpu_common_reset_hold; + rc->phases.exit = cpu_common_reset_exit; cpu_class_init_props(dc); /* * Reason: CPUs still need special care by board code: wiring up From fd5c5032df6bea4bbf56752d8885c8a9770c4959 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 27 Aug 2025 15:34:35 +1000 Subject: [PATCH 0138/2396] hw/core: Use qemu_log_trylock/unlock in cpu_common_reset_exit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ensure that the "CPU Reset" message won't be separated from the cpu_dump_state output. Reviewed-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- hw/core/cpu-common.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/hw/core/cpu-common.c b/hw/core/cpu-common.c index 26321be785..259cf2a3c3 100644 --- a/hw/core/cpu-common.c +++ b/hw/core/cpu-common.c @@ -135,10 +135,15 @@ static void cpu_common_reset_hold(Object *obj, ResetType type) static void cpu_common_reset_exit(Object *obj, ResetType type) { if (qemu_loglevel_mask(CPU_LOG_RESET)) { - CPUState *cpu = CPU(obj); + FILE *f = qemu_log_trylock(); - qemu_log("CPU Reset (CPU %d)\n", cpu->cpu_index); - log_cpu_state(cpu, cpu->cc->reset_dump_flags); + if (f) { + CPUState *cpu = CPU(obj); + + fprintf(f, "CPU Reset (CPU %d)\n", cpu->cpu_index); + cpu_dump_state(cpu, f, cpu->cc->reset_dump_flags); + qemu_log_unlock(f); + } } } From d7cde4df1c1b1f24dfd7760fe897429b75200b19 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Aug 2025 07:36:25 +1000 Subject: [PATCH 0139/2396] linux-user: Tidy print_socket_protocol MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sink all of the qemu_log calls to the end, collecting only a string for the name, if identified. Merge separate if blocks into one switch. Reviewed-by: Daniel P. Berrangé Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- linux-user/strace.c | 102 ++++++++++++++++++++++---------------------- 1 file changed, 52 insertions(+), 50 deletions(-) diff --git a/linux-user/strace.c b/linux-user/strace.c index 786354627a..1233ebceb0 100644 --- a/linux-user/strace.c +++ b/linux-user/strace.c @@ -499,116 +499,118 @@ print_socket_type(int type) static void print_socket_protocol(int domain, int type, int protocol) { - if (domain == AF_PACKET || - (domain == AF_INET && type == TARGET_SOCK_PACKET)) { - switch (protocol) { - case 0x0003: - qemu_log("ETH_P_ALL"); - break; - default: - qemu_log("%d", protocol); - } - return; - } + const char *name = NULL; - if (domain == PF_NETLINK) { + switch (domain) { + case AF_PACKET: + switch (protocol) { + case 3: + name = "ETH_P_ALL"; + break; + } + break; + + case PF_NETLINK: switch (protocol) { case NETLINK_ROUTE: - qemu_log("NETLINK_ROUTE"); + name = "NETLINK_ROUTE"; break; case NETLINK_UNUSED: - qemu_log("NETLINK_UNUSED"); + name = "NETLINK_UNUSED"; break; case NETLINK_USERSOCK: - qemu_log("NETLINK_USERSOCK"); + name = "NETLINK_USERSOCK"; break; case NETLINK_FIREWALL: - qemu_log("NETLINK_FIREWALL"); + name = "NETLINK_FIREWALL"; break; case NETLINK_SOCK_DIAG: - qemu_log("NETLINK_SOCK_DIAG"); + name = "NETLINK_SOCK_DIAG"; break; case NETLINK_NFLOG: - qemu_log("NETLINK_NFLOG"); + name = "NETLINK_NFLOG"; break; case NETLINK_XFRM: - qemu_log("NETLINK_XFRM"); + name = "NETLINK_XFRM"; break; case NETLINK_SELINUX: - qemu_log("NETLINK_SELINUX"); + name = "NETLINK_SELINUX"; break; case NETLINK_ISCSI: - qemu_log("NETLINK_ISCSI"); + name = "NETLINK_ISCSI"; break; case NETLINK_AUDIT: - qemu_log("NETLINK_AUDIT"); + name = "NETLINK_AUDIT"; break; case NETLINK_FIB_LOOKUP: - qemu_log("NETLINK_FIB_LOOKUP"); + name = "NETLINK_FIB_LOOKUP"; break; case NETLINK_CONNECTOR: - qemu_log("NETLINK_CONNECTOR"); + name = "NETLINK_CONNECTOR"; break; case NETLINK_NETFILTER: - qemu_log("NETLINK_NETFILTER"); + name = "NETLINK_NETFILTER"; break; case NETLINK_IP6_FW: - qemu_log("NETLINK_IP6_FW"); + name = "NETLINK_IP6_FW"; break; case NETLINK_DNRTMSG: - qemu_log("NETLINK_DNRTMSG"); + name = "NETLINK_DNRTMSG"; break; case NETLINK_KOBJECT_UEVENT: - qemu_log("NETLINK_KOBJECT_UEVENT"); + name = "NETLINK_KOBJECT_UEVENT"; break; case NETLINK_GENERIC: - qemu_log("NETLINK_GENERIC"); + name = "NETLINK_GENERIC"; break; case NETLINK_SCSITRANSPORT: - qemu_log("NETLINK_SCSITRANSPORT"); + name = "NETLINK_SCSITRANSPORT"; break; case NETLINK_ECRYPTFS: - qemu_log("NETLINK_ECRYPTFS"); + name = "NETLINK_ECRYPTFS"; break; case NETLINK_RDMA: - qemu_log("NETLINK_RDMA"); + name = "NETLINK_RDMA"; break; case NETLINK_CRYPTO: - qemu_log("NETLINK_CRYPTO"); + name = "NETLINK_CRYPTO"; break; case NETLINK_SMC: - qemu_log("NETLINK_SMC"); - break; - default: - qemu_log("%d", protocol); + name = "NETLINK_SMC"; break; } - return; - } + break; - if (domain == AF_INET || domain == AF_INET6) { + case AF_INET: + case AF_INET6: switch (protocol) { + case 3: + if (domain == AF_INET && type == TARGET_SOCK_PACKET) { + name = "ETH_P_ALL"; + } + break; case IPPROTO_IP: - qemu_log("IPPROTO_IP"); + name = "IPPROTO_IP"; break; case IPPROTO_TCP: - qemu_log("IPPROTO_TCP"); + name = "IPPROTO_TCP"; break; case IPPROTO_UDP: - qemu_log("IPPROTO_UDP"); + name = "IPPROTO_UDP"; break; case IPPROTO_RAW: - qemu_log("IPPROTO_RAW"); - break; - default: - qemu_log("%d", protocol); + name = "IPPROTO_RAW"; break; } - return; + break; } - qemu_log("%d", protocol); -} + if (name) { + qemu_log("%s", name); + } else { + qemu_log("%d", protocol); + } +} #ifdef TARGET_NR__newselect static void From e61df9176d7a0107069de1a77b6f502b24b34f58 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 28 Aug 2025 17:20:12 +0100 Subject: [PATCH 0140/2396] linux-user: Drop deprecated -p option MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The user-mode '-p' option has been deprecated since 9.0 and doesn't do anything except emit a warning. We are well past our minimum deprecation period, so drop the option. Reviewed-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Thomas Huth Signed-off-by: Peter Maydell Signed-off-by: Richard Henderson Message-ID: <20250828162012.3307647-1-peter.maydell@linaro.org> --- bsd-user/main.c | 8 -------- docs/about/deprecated.rst | 10 ---------- docs/about/removed-features.rst | 8 ++++++++ docs/user/main.rst | 3 --- linux-user/main.c | 12 ------------ 5 files changed, 8 insertions(+), 33 deletions(-) diff --git a/bsd-user/main.c b/bsd-user/main.c index 7e5d4bbce0..9ba69642f5 100644 --- a/bsd-user/main.c +++ b/bsd-user/main.c @@ -367,14 +367,6 @@ int main(int argc, char **argv) } } else if (!strcmp(r, "L")) { interp_prefix = argv[optind++]; - } else if (!strcmp(r, "p")) { - unsigned size, want = qemu_real_host_page_size(); - - r = argv[optind++]; - if (qemu_strtoui(r, NULL, 10, &size) || size != want) { - warn_report("Deprecated page size option cannot " - "change host page size (%u)", want); - } } else if (!strcmp(r, "g")) { gdbstub = g_strdup(argv[optind++]); } else if (!strcmp(r, "r")) { diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst index d50645a071..5d1579dcf8 100644 --- a/docs/about/deprecated.rst +++ b/docs/about/deprecated.rst @@ -81,16 +81,6 @@ kernel since 2001. None of the board types QEMU supports need ``param_struct`` support, so this option has been deprecated and will be removed in a future QEMU version. -User-mode emulator command line arguments ------------------------------------------ - -``-p`` (since 9.0) -'''''''''''''''''' - -The ``-p`` option pretends to control the host page size. However, -it is not possible to change the host page size, and using the -option only causes failures. - QEMU Machine Protocol (QMP) commands ------------------------------------ diff --git a/docs/about/removed-features.rst b/docs/about/removed-features.rst index d7c2113fc3..25a904032c 100644 --- a/docs/about/removed-features.rst +++ b/docs/about/removed-features.rst @@ -571,6 +571,14 @@ The ``-singlestep`` option has been given a name that better reflects what it actually does. For both linux-user and bsd-user, use the ``-one-insn-per-tb`` option instead. +``-p`` (removed in 10.2) +'''''''''''''''''''''''' + +The ``-p`` option pretends to control the host page size. However, +it is not possible to change the host page size; we stopped trying +to do anything with the option except print a warning from 9.0, +and now the option is removed entirely. + QEMU Machine Protocol (QMP) commands ------------------------------------ diff --git a/docs/user/main.rst b/docs/user/main.rst index 347bdfabf8..a8ddf91424 100644 --- a/docs/user/main.rst +++ b/docs/user/main.rst @@ -262,9 +262,6 @@ Debug options: Activate logging of the specified items (use '-d help' for a list of log items) -``-p pagesize`` - Act as if the host page size was 'pagesize' bytes - ``-one-insn-per-tb`` Run the emulation with one guest instruction per translation block. This slows down emulation a lot, but can be useful in some situations, diff --git a/linux-user/main.c b/linux-user/main.c index 6edeeecef3..7b0ccb6fd6 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -340,16 +340,6 @@ static void handle_arg_ld_prefix(const char *arg) interp_prefix = strdup(arg); } -static void handle_arg_pagesize(const char *arg) -{ - unsigned size, want = qemu_real_host_page_size(); - - if (qemu_strtoui(arg, NULL, 10, &size) || size != want) { - warn_report("Deprecated page size option cannot " - "change host page size (%u)", want); - } -} - static void handle_arg_seed(const char *arg) { seed_optarg = arg; @@ -522,8 +512,6 @@ static const struct qemu_argument arg_table[] = { "range[,...]","filter logging based on address range"}, {"D", "QEMU_LOG_FILENAME", true, handle_arg_log_filename, "logfile", "write logs to 'logfile' (default stderr)"}, - {"p", "QEMU_PAGESIZE", true, handle_arg_pagesize, - "pagesize", "deprecated change to host page size"}, {"one-insn-per-tb", "QEMU_ONE_INSN_PER_TB", false, handle_arg_one_insn_per_tb, "", "run with one guest instruction per emulated TB"}, From 720dbc31cb424ba6364e99a340fca805d1837bfa Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Aug 2025 07:55:10 +1000 Subject: [PATCH 0141/2396] linux-user/x86_64: Convert target_elf_gregset_t to a struct A structure typedef may be abstract, while an array typedef cannot. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 60 +++++++++++++++++++++++--------------------- 1 file changed, 31 insertions(+), 29 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index fce4c05674..ba205c5a19 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -154,7 +154,9 @@ typedef abi_int target_pid_t; #define ELF_ARCH EM_X86_64 #define ELF_NREG 27 -typedef target_elf_greg_t target_elf_gregset_t[ELF_NREG]; +typedef struct target_elf_gregset_t { + target_elf_greg_t regs[ELF_NREG]; +} target_elf_gregset_t; /* * Note that ELF_NREG should be 29 as there should be place for @@ -163,35 +165,35 @@ typedef target_elf_greg_t target_elf_gregset_t[ELF_NREG]; * * See linux kernel: arch/x86/include/asm/elf.h */ -static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUX86State *env) +static void elf_core_copy_regs(target_elf_gregset_t *r, const CPUX86State *env) { - (*regs)[0] = tswapreg(env->regs[15]); - (*regs)[1] = tswapreg(env->regs[14]); - (*regs)[2] = tswapreg(env->regs[13]); - (*regs)[3] = tswapreg(env->regs[12]); - (*regs)[4] = tswapreg(env->regs[R_EBP]); - (*regs)[5] = tswapreg(env->regs[R_EBX]); - (*regs)[6] = tswapreg(env->regs[11]); - (*regs)[7] = tswapreg(env->regs[10]); - (*regs)[8] = tswapreg(env->regs[9]); - (*regs)[9] = tswapreg(env->regs[8]); - (*regs)[10] = tswapreg(env->regs[R_EAX]); - (*regs)[11] = tswapreg(env->regs[R_ECX]); - (*regs)[12] = tswapreg(env->regs[R_EDX]); - (*regs)[13] = tswapreg(env->regs[R_ESI]); - (*regs)[14] = tswapreg(env->regs[R_EDI]); - (*regs)[15] = tswapreg(get_task_state(env_cpu_const(env))->orig_ax); - (*regs)[16] = tswapreg(env->eip); - (*regs)[17] = tswapreg(env->segs[R_CS].selector & 0xffff); - (*regs)[18] = tswapreg(env->eflags); - (*regs)[19] = tswapreg(env->regs[R_ESP]); - (*regs)[20] = tswapreg(env->segs[R_SS].selector & 0xffff); - (*regs)[21] = tswapreg(env->segs[R_FS].selector & 0xffff); - (*regs)[22] = tswapreg(env->segs[R_GS].selector & 0xffff); - (*regs)[23] = tswapreg(env->segs[R_DS].selector & 0xffff); - (*regs)[24] = tswapreg(env->segs[R_ES].selector & 0xffff); - (*regs)[25] = tswapreg(env->segs[R_FS].selector & 0xffff); - (*regs)[26] = tswapreg(env->segs[R_GS].selector & 0xffff); + r->regs[0] = tswapreg(env->regs[15]); + r->regs[1] = tswapreg(env->regs[14]); + r->regs[2] = tswapreg(env->regs[13]); + r->regs[3] = tswapreg(env->regs[12]); + r->regs[4] = tswapreg(env->regs[R_EBP]); + r->regs[5] = tswapreg(env->regs[R_EBX]); + r->regs[6] = tswapreg(env->regs[11]); + r->regs[7] = tswapreg(env->regs[10]); + r->regs[8] = tswapreg(env->regs[9]); + r->regs[9] = tswapreg(env->regs[8]); + r->regs[10] = tswapreg(env->regs[R_EAX]); + r->regs[11] = tswapreg(env->regs[R_ECX]); + r->regs[12] = tswapreg(env->regs[R_EDX]); + r->regs[13] = tswapreg(env->regs[R_ESI]); + r->regs[14] = tswapreg(env->regs[R_EDI]); + r->regs[15] = tswapreg(get_task_state(env_cpu_const(env))->orig_ax); + r->regs[16] = tswapreg(env->eip); + r->regs[17] = tswapreg(env->segs[R_CS].selector & 0xffff); + r->regs[18] = tswapreg(env->eflags); + r->regs[19] = tswapreg(env->regs[R_ESP]); + r->regs[20] = tswapreg(env->segs[R_SS].selector & 0xffff); + r->regs[21] = tswapreg(env->segs[R_FS].selector & 0xffff); + r->regs[22] = tswapreg(env->segs[R_GS].selector & 0xffff); + r->regs[23] = tswapreg(env->segs[R_DS].selector & 0xffff); + r->regs[24] = tswapreg(env->segs[R_ES].selector & 0xffff); + r->regs[25] = tswapreg(env->segs[R_FS].selector & 0xffff); + r->regs[26] = tswapreg(env->segs[R_GS].selector & 0xffff); } #if ULONG_MAX > UINT32_MAX From 4d8db2fa6f8e1e77fd88b1c3a4b0409adad630c0 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Aug 2025 07:57:31 +1000 Subject: [PATCH 0142/2396] linux-user/i386: Convert target_elf_gregset_t to a struct Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 40 +++++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index ba205c5a19..e8a7f040ed 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -233,7 +233,9 @@ static bool init_guest_commpage(void) #define EXSTACK_DEFAULT true #define ELF_NREG 17 -typedef target_elf_greg_t target_elf_gregset_t[ELF_NREG]; +typedef struct target_elf_gregset_t { + target_elf_greg_t regs[ELF_NREG]; +} target_elf_gregset_t; /* * Note that ELF_NREG should be 19 as there should be place for @@ -242,25 +244,25 @@ typedef target_elf_greg_t target_elf_gregset_t[ELF_NREG]; * * See linux kernel: arch/x86/include/asm/elf.h */ -static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUX86State *env) +static void elf_core_copy_regs(target_elf_gregset_t *r, const CPUX86State *env) { - (*regs)[0] = tswapreg(env->regs[R_EBX]); - (*regs)[1] = tswapreg(env->regs[R_ECX]); - (*regs)[2] = tswapreg(env->regs[R_EDX]); - (*regs)[3] = tswapreg(env->regs[R_ESI]); - (*regs)[4] = tswapreg(env->regs[R_EDI]); - (*regs)[5] = tswapreg(env->regs[R_EBP]); - (*regs)[6] = tswapreg(env->regs[R_EAX]); - (*regs)[7] = tswapreg(env->segs[R_DS].selector & 0xffff); - (*regs)[8] = tswapreg(env->segs[R_ES].selector & 0xffff); - (*regs)[9] = tswapreg(env->segs[R_FS].selector & 0xffff); - (*regs)[10] = tswapreg(env->segs[R_GS].selector & 0xffff); - (*regs)[11] = tswapreg(get_task_state(env_cpu_const(env))->orig_ax); - (*regs)[12] = tswapreg(env->eip); - (*regs)[13] = tswapreg(env->segs[R_CS].selector & 0xffff); - (*regs)[14] = tswapreg(env->eflags); - (*regs)[15] = tswapreg(env->regs[R_ESP]); - (*regs)[16] = tswapreg(env->segs[R_SS].selector & 0xffff); + r->regs[0] = tswapreg(env->regs[R_EBX]); + r->regs[1] = tswapreg(env->regs[R_ECX]); + r->regs[2] = tswapreg(env->regs[R_EDX]); + r->regs[3] = tswapreg(env->regs[R_ESI]); + r->regs[4] = tswapreg(env->regs[R_EDI]); + r->regs[5] = tswapreg(env->regs[R_EBP]); + r->regs[6] = tswapreg(env->regs[R_EAX]); + r->regs[7] = tswapreg(env->segs[R_DS].selector & 0xffff); + r->regs[8] = tswapreg(env->segs[R_ES].selector & 0xffff); + r->regs[9] = tswapreg(env->segs[R_FS].selector & 0xffff); + r->regs[10] = tswapreg(env->segs[R_GS].selector & 0xffff); + r->regs[11] = tswapreg(get_task_state(env_cpu_const(env))->orig_ax); + r->regs[12] = tswapreg(env->eip); + r->regs[13] = tswapreg(env->segs[R_CS].selector & 0xffff); + r->regs[14] = tswapreg(env->eflags); + r->regs[15] = tswapreg(env->regs[R_ESP]); + r->regs[16] = tswapreg(env->segs[R_SS].selector & 0xffff); } /* From ea37ee2b2659a7fa4b1161112c0be3559180ccdd Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Aug 2025 08:02:18 +1000 Subject: [PATCH 0143/2396] linux-user/arm: Convert target_elf_gregset_t to a struct While we're at it, loop over the general registers rather than open-code them. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 30 +++++++++--------------------- 1 file changed, 9 insertions(+), 21 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index e8a7f040ed..0180f6063f 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -296,29 +296,17 @@ static void elf_core_copy_regs(target_elf_gregset_t *r, const CPUX86State *env) #define EXSTACK_DEFAULT true #define ELF_NREG 18 -typedef target_elf_greg_t target_elf_gregset_t[ELF_NREG]; +typedef struct target_elf_gregset_t { + target_elf_greg_t regs[ELF_NREG]; +} target_elf_gregset_t; -static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUARMState *env) +static void elf_core_copy_regs(target_elf_gregset_t *r, const CPUARMState *env) { - (*regs)[0] = tswapreg(env->regs[0]); - (*regs)[1] = tswapreg(env->regs[1]); - (*regs)[2] = tswapreg(env->regs[2]); - (*regs)[3] = tswapreg(env->regs[3]); - (*regs)[4] = tswapreg(env->regs[4]); - (*regs)[5] = tswapreg(env->regs[5]); - (*regs)[6] = tswapreg(env->regs[6]); - (*regs)[7] = tswapreg(env->regs[7]); - (*regs)[8] = tswapreg(env->regs[8]); - (*regs)[9] = tswapreg(env->regs[9]); - (*regs)[10] = tswapreg(env->regs[10]); - (*regs)[11] = tswapreg(env->regs[11]); - (*regs)[12] = tswapreg(env->regs[12]); - (*regs)[13] = tswapreg(env->regs[13]); - (*regs)[14] = tswapreg(env->regs[14]); - (*regs)[15] = tswapreg(env->regs[15]); - - (*regs)[16] = tswapreg(cpsr_read((CPUARMState *)env)); - (*regs)[17] = tswapreg(env->regs[0]); /* XXX */ + for (int i = 0; i < 16; ++i) { + r->regs[i] = tswapreg(env->regs[i]); + } + r->regs[16] = tswapreg(cpsr_read((CPUARMState *)env)); + r->regs[17] = tswapreg(env->regs[0]); /* XXX */ } #define USE_ELF_CORE_DUMP From 0b3357425cef78233ec0c574990a4e70e53c30e6 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Aug 2025 08:03:45 +1000 Subject: [PATCH 0144/2396] linux-user/aarch64: Convert target_elf_gregset_t to a struct Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 0180f6063f..da57c6c2ce 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -384,18 +384,17 @@ static const VdsoImageInfo *vdso_image_info(uint32_t elf_flags) #define ELF_CLASS ELFCLASS64 #define ELF_NREG 34 -typedef target_elf_greg_t target_elf_gregset_t[ELF_NREG]; +typedef struct target_elf_gregset_t { + target_elf_greg_t regs[ELF_NREG]; +} target_elf_gregset_t; -static void elf_core_copy_regs(target_elf_gregset_t *regs, - const CPUARMState *env) +static void elf_core_copy_regs(target_elf_gregset_t *r, const CPUARMState *env) { - int i; - - for (i = 0; i < 32; i++) { - (*regs)[i] = tswapreg(env->xregs[i]); + for (int i = 0; i < 32; i++) { + r->regs[i] = tswapreg(env->xregs[i]); } - (*regs)[32] = tswapreg(env->pc); - (*regs)[33] = tswapreg(pstate_read((CPUARMState *)env)); + r->regs[32] = tswapreg(env->pc); + r->regs[33] = tswapreg(pstate_read((CPUARMState *)env)); } #define USE_ELF_CORE_DUMP From 544843f2e76b2d40535b8b3d14007371cbd868f8 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Aug 2025 08:05:47 +1000 Subject: [PATCH 0145/2396] linux-user/ppc: Convert target_elf_gregset_t to a struct Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index da57c6c2ce..0dd76937f9 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -470,25 +470,27 @@ static void elf_core_copy_regs(target_elf_gregset_t *r, const CPUARMState *env) /* See linux kernel: arch/powerpc/include/asm/elf.h. */ #define ELF_NREG 48 -typedef target_elf_greg_t target_elf_gregset_t[ELF_NREG]; +typedef struct target_elf_gregset_t { + target_elf_greg_t regs[ELF_NREG]; +} target_elf_gregset_t; -static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUPPCState *env) +static void elf_core_copy_regs(target_elf_gregset_t *r, const CPUPPCState *env) { int i; target_ulong ccr = 0; for (i = 0; i < ARRAY_SIZE(env->gpr); i++) { - (*regs)[i] = tswapreg(env->gpr[i]); + r->regs[i] = tswapreg(env->gpr[i]); } - (*regs)[32] = tswapreg(env->nip); - (*regs)[33] = tswapreg(env->msr); - (*regs)[35] = tswapreg(env->ctr); - (*regs)[36] = tswapreg(env->lr); - (*regs)[37] = tswapreg(cpu_read_xer(env)); + r->regs[32] = tswapreg(env->nip); + r->regs[33] = tswapreg(env->msr); + r->regs[35] = tswapreg(env->ctr); + r->regs[36] = tswapreg(env->lr); + r->regs[37] = tswapreg(cpu_read_xer(env)); ccr = ppc_get_cr(env); - (*regs)[38] = tswapreg(ccr); + r->regs[38] = tswapreg(ccr); } #define USE_ELF_CORE_DUMP From 68bb444d358c8cbe7bf4d8f942491ff01f132443 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Aug 2025 08:07:32 +1000 Subject: [PATCH 0146/2396] linux-user/loongarch64: Convert target_elf_gregset_t to a struct Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 0dd76937f9..1e59399afa 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -518,7 +518,9 @@ static void elf_core_copy_regs(target_elf_gregset_t *r, const CPUPPCState *env) /* See linux kernel: arch/loongarch/include/asm/elf.h */ #define ELF_NREG 45 -typedef target_elf_greg_t target_elf_gregset_t[ELF_NREG]; +typedef struct target_elf_gregset_t { + target_elf_greg_t regs[ELF_NREG]; +} target_elf_gregset_t; enum { TARGET_EF_R0 = 0, @@ -526,19 +528,17 @@ enum { TARGET_EF_CSR_BADV = TARGET_EF_R0 + 34, }; -static void elf_core_copy_regs(target_elf_gregset_t *regs, +static void elf_core_copy_regs(target_elf_gregset_t *r, const CPULoongArchState *env) { - int i; + r->regs[TARGET_EF_R0] = 0; - (*regs)[TARGET_EF_R0] = 0; - - for (i = 1; i < ARRAY_SIZE(env->gpr); i++) { - (*regs)[TARGET_EF_R0 + i] = tswapreg(env->gpr[i]); + for (int i = 1; i < ARRAY_SIZE(env->gpr); i++) { + r->regs[TARGET_EF_R0 + i] = tswapreg(env->gpr[i]); } - (*regs)[TARGET_EF_CSR_ERA] = tswapreg(env->pc); - (*regs)[TARGET_EF_CSR_BADV] = tswapreg(env->CSR_BADV); + r->regs[TARGET_EF_CSR_ERA] = tswapreg(env->pc); + r->regs[TARGET_EF_CSR_BADV] = tswapreg(env->CSR_BADV); } #define USE_ELF_CORE_DUMP From 5c86402eef21c09c65c23158ddebd791fdf603c9 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Aug 2025 08:10:05 +1000 Subject: [PATCH 0147/2396] linux-user/mips: Convert target_elf_gregset_t to a struct While we're at it, merge the store of TARGET_EF_R0 into the loop over all R0 registers. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 1e59399afa..8fcdb0569b 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -564,7 +564,9 @@ static void elf_core_copy_regs(target_elf_gregset_t *r, /* See linux kernel: arch/mips/include/asm/elf.h. */ #define ELF_NREG 45 -typedef target_elf_greg_t target_elf_gregset_t[ELF_NREG]; +typedef struct target_elf_gregset_t { + target_elf_greg_t regs[ELF_NREG]; +} target_elf_gregset_t; /* See linux kernel: arch/mips/include/asm/reg.h. */ enum { @@ -584,27 +586,25 @@ enum { }; /* See linux kernel: arch/mips/kernel/process.c:elf_dump_regs. */ -static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUMIPSState *env) +static void elf_core_copy_regs(target_elf_gregset_t *r, const CPUMIPSState *env) { int i; - for (i = 0; i < TARGET_EF_R0; i++) { - (*regs)[i] = 0; + for (i = 0; i <= TARGET_EF_R0; i++) { + r->regs[i] = 0; } - (*regs)[TARGET_EF_R0] = 0; - for (i = 1; i < ARRAY_SIZE(env->active_tc.gpr); i++) { - (*regs)[TARGET_EF_R0 + i] = tswapreg(env->active_tc.gpr[i]); + r->regs[TARGET_EF_R0 + i] = tswapreg(env->active_tc.gpr[i]); } - (*regs)[TARGET_EF_R26] = 0; - (*regs)[TARGET_EF_R27] = 0; - (*regs)[TARGET_EF_LO] = tswapreg(env->active_tc.LO[0]); - (*regs)[TARGET_EF_HI] = tswapreg(env->active_tc.HI[0]); - (*regs)[TARGET_EF_CP0_EPC] = tswapreg(env->active_tc.PC); - (*regs)[TARGET_EF_CP0_BADVADDR] = tswapreg(env->CP0_BadVAddr); - (*regs)[TARGET_EF_CP0_STATUS] = tswapreg(env->CP0_Status); - (*regs)[TARGET_EF_CP0_CAUSE] = tswapreg(env->CP0_Cause); + r->regs[TARGET_EF_R26] = 0; + r->regs[TARGET_EF_R27] = 0; + r->regs[TARGET_EF_LO] = tswapreg(env->active_tc.LO[0]); + r->regs[TARGET_EF_HI] = tswapreg(env->active_tc.HI[0]); + r->regs[TARGET_EF_CP0_EPC] = tswapreg(env->active_tc.PC); + r->regs[TARGET_EF_CP0_BADVADDR] = tswapreg(env->CP0_BadVAddr); + r->regs[TARGET_EF_CP0_STATUS] = tswapreg(env->CP0_Status); + r->regs[TARGET_EF_CP0_CAUSE] = tswapreg(env->CP0_Cause); } #define USE_ELF_CORE_DUMP From b4ad80d6d3355f2ab7f02381f8eb697609b89e93 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Aug 2025 08:13:46 +1000 Subject: [PATCH 0148/2396] linux-user/microblaze: Convert target_elf_gregset_t to a struct While we're at it, drop "pos++" and simply open-code indexes. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 8fcdb0569b..40a5bcccab 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -623,23 +623,23 @@ static void elf_core_copy_regs(target_elf_gregset_t *r, const CPUMIPSState *env) #define USE_ELF_CORE_DUMP #define ELF_NREG 38 -typedef target_elf_greg_t target_elf_gregset_t[ELF_NREG]; +typedef struct target_elf_gregset_t { + target_elf_greg_t regs[ELF_NREG]; +} target_elf_gregset_t; /* See linux kernel: arch/mips/kernel/process.c:elf_dump_regs. */ -static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUMBState *env) +static void elf_core_copy_regs(target_elf_gregset_t *r, const CPUMBState *env) { - int i, pos = 0; - - for (i = 0; i < 32; i++) { - (*regs)[pos++] = tswapreg(env->regs[i]); + for (int i = 0; i < 32; i++) { + r->regs[i] = tswapreg(env->regs[i]); } - (*regs)[pos++] = tswapreg(env->pc); - (*regs)[pos++] = tswapreg(mb_cpu_read_msr(env)); - (*regs)[pos++] = 0; - (*regs)[pos++] = tswapreg(env->ear); - (*regs)[pos++] = 0; - (*regs)[pos++] = tswapreg(env->esr); + r->regs[32] = tswapreg(env->pc); + r->regs[33] = tswapreg(mb_cpu_read_msr(env)); + r->regs[34] = 0; + r->regs[35] = tswapreg(env->ear); + r->regs[36] = 0; + r->regs[37] = tswapreg(env->esr); } #endif /* TARGET_MICROBLAZE */ From aebdd808e6288d78d7393bf1d4b49aaea93f786c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Aug 2025 08:15:44 +1000 Subject: [PATCH 0149/2396] linux-user/openrisc: Convert target_elf_gregset_t to a struct Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 40a5bcccab..da034e5a76 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -655,18 +655,18 @@ static void elf_core_copy_regs(target_elf_gregset_t *r, const CPUMBState *env) /* See linux kernel arch/openrisc/include/asm/elf.h. */ #define ELF_NREG 34 /* gprs and pc, sr */ -typedef target_elf_greg_t target_elf_gregset_t[ELF_NREG]; +typedef struct target_elf_gregset_t { + target_elf_greg_t regs[ELF_NREG]; +} target_elf_gregset_t; -static void elf_core_copy_regs(target_elf_gregset_t *regs, +static void elf_core_copy_regs(target_elf_gregset_t *r, const CPUOpenRISCState *env) { - int i; - - for (i = 0; i < 32; i++) { - (*regs)[i] = tswapreg(cpu_get_gpr(env, i)); + for (int i = 0; i < 32; i++) { + r->regs[i] = tswapreg(cpu_get_gpr(env, i)); } - (*regs)[32] = tswapreg(env->pc); - (*regs)[33] = tswapreg(cpu_get_sr(env)); + r->regs[32] = tswapreg(env->pc); + r->regs[33] = tswapreg(cpu_get_sr(env)); } #endif /* TARGET_OPENRISC */ From 7bd01645d66c23ef5494dcc8ea13d15bc90d82f5 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Aug 2025 08:17:40 +1000 Subject: [PATCH 0150/2396] linux-user/sh4: Convert target_elf_gregset_t to a struct Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index da034e5a76..cc9140bf32 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -678,7 +678,9 @@ static void elf_core_copy_regs(target_elf_gregset_t *r, /* See linux kernel: arch/sh/include/asm/elf.h. */ #define ELF_NREG 23 -typedef target_elf_greg_t target_elf_gregset_t[ELF_NREG]; +typedef struct target_elf_gregset_t { + target_elf_greg_t regs[ELF_NREG]; +} target_elf_gregset_t; /* See linux kernel: arch/sh/include/asm/ptrace.h. */ enum { @@ -691,22 +693,19 @@ enum { TARGET_REG_SYSCALL = 22 }; -static inline void elf_core_copy_regs(target_elf_gregset_t *regs, - const CPUSH4State *env) +static void elf_core_copy_regs(target_elf_gregset_t *r, const CPUSH4State *env) { - int i; - - for (i = 0; i < 16; i++) { - (*regs)[i] = tswapreg(env->gregs[i]); + for (int i = 0; i < 16; i++) { + r->regs[i] = tswapreg(env->gregs[i]); } - (*regs)[TARGET_REG_PC] = tswapreg(env->pc); - (*regs)[TARGET_REG_PR] = tswapreg(env->pr); - (*regs)[TARGET_REG_SR] = tswapreg(env->sr); - (*regs)[TARGET_REG_GBR] = tswapreg(env->gbr); - (*regs)[TARGET_REG_MACH] = tswapreg(env->mach); - (*regs)[TARGET_REG_MACL] = tswapreg(env->macl); - (*regs)[TARGET_REG_SYSCALL] = 0; /* FIXME */ + r->regs[TARGET_REG_PC] = tswapreg(env->pc); + r->regs[TARGET_REG_PR] = tswapreg(env->pr); + r->regs[TARGET_REG_SR] = tswapreg(env->sr); + r->regs[TARGET_REG_GBR] = tswapreg(env->gbr); + r->regs[TARGET_REG_MACH] = tswapreg(env->mach); + r->regs[TARGET_REG_MACL] = tswapreg(env->macl); + r->regs[TARGET_REG_SYSCALL] = 0; /* FIXME */ } #define USE_ELF_CORE_DUMP From a9caaa69f081c6ae0a1adead578a757be5dec98b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Aug 2025 08:19:30 +1000 Subject: [PATCH 0151/2396] linux-user/m68k: Convert target_elf_gregset_t to a struct Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 46 +++++++++++++++++++++++--------------------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index cc9140bf32..63376fa1d6 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -720,30 +720,32 @@ static void elf_core_copy_regs(target_elf_gregset_t *r, const CPUSH4State *env) /* See linux kernel: arch/m68k/include/asm/elf.h. */ #define ELF_NREG 20 -typedef target_elf_greg_t target_elf_gregset_t[ELF_NREG]; +typedef struct target_elf_gregset_t { + target_elf_greg_t regs[ELF_NREG]; +} target_elf_gregset_t; -static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUM68KState *env) +static void elf_core_copy_regs(target_elf_gregset_t *r, const CPUM68KState *env) { - (*regs)[0] = tswapreg(env->dregs[1]); - (*regs)[1] = tswapreg(env->dregs[2]); - (*regs)[2] = tswapreg(env->dregs[3]); - (*regs)[3] = tswapreg(env->dregs[4]); - (*regs)[4] = tswapreg(env->dregs[5]); - (*regs)[5] = tswapreg(env->dregs[6]); - (*regs)[6] = tswapreg(env->dregs[7]); - (*regs)[7] = tswapreg(env->aregs[0]); - (*regs)[8] = tswapreg(env->aregs[1]); - (*regs)[9] = tswapreg(env->aregs[2]); - (*regs)[10] = tswapreg(env->aregs[3]); - (*regs)[11] = tswapreg(env->aregs[4]); - (*regs)[12] = tswapreg(env->aregs[5]); - (*regs)[13] = tswapreg(env->aregs[6]); - (*regs)[14] = tswapreg(env->dregs[0]); - (*regs)[15] = tswapreg(env->aregs[7]); - (*regs)[16] = tswapreg(env->dregs[0]); /* FIXME: orig_d0 */ - (*regs)[17] = tswapreg(env->sr); - (*regs)[18] = tswapreg(env->pc); - (*regs)[19] = 0; /* FIXME: regs->format | regs->vector */ + r->regs[0] = tswapreg(env->dregs[1]); + r->regs[1] = tswapreg(env->dregs[2]); + r->regs[2] = tswapreg(env->dregs[3]); + r->regs[3] = tswapreg(env->dregs[4]); + r->regs[4] = tswapreg(env->dregs[5]); + r->regs[5] = tswapreg(env->dregs[6]); + r->regs[6] = tswapreg(env->dregs[7]); + r->regs[7] = tswapreg(env->aregs[0]); + r->regs[8] = tswapreg(env->aregs[1]); + r->regs[9] = tswapreg(env->aregs[2]); + r->regs[10] = tswapreg(env->aregs[3]); + r->regs[11] = tswapreg(env->aregs[4]); + r->regs[12] = tswapreg(env->aregs[5]); + r->regs[13] = tswapreg(env->aregs[6]); + r->regs[14] = tswapreg(env->dregs[0]); + r->regs[15] = tswapreg(env->aregs[7]); + r->regs[16] = tswapreg(env->dregs[0]); /* FIXME: orig_d0 */ + r->regs[17] = tswapreg(env->sr); + r->regs[18] = tswapreg(env->pc); + r->regs[19] = 0; /* FIXME: regs->format | regs->vector */ } #define USE_ELF_CORE_DUMP From f2accdfac4ca431dd58054a82c2312bc58e984a2 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Aug 2025 08:30:25 +1000 Subject: [PATCH 0152/2396] linux-user/s390x: Convert target_elf_gregset_t to a struct Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 63376fa1d6..98c17d32e6 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -770,7 +770,9 @@ static void elf_core_copy_regs(target_elf_gregset_t *r, const CPUM68KState *env) /* See linux kernel: arch/s390/include/uapi/asm/ptrace.h (s390_regs). */ #define ELF_NREG 27 -typedef target_elf_greg_t target_elf_gregset_t[ELF_NREG]; +typedef struct target_elf_gregset_t { + target_elf_greg_t regs[ELF_NREG]; +} target_elf_gregset_t; enum { TARGET_REG_PSWM = 0, @@ -780,22 +782,22 @@ enum { TARGET_REG_ORIG_R2 = 26, }; -static void elf_core_copy_regs(target_elf_gregset_t *regs, +static void elf_core_copy_regs(target_elf_gregset_t *r, const CPUS390XState *env) { int i; uint32_t *aregs; - (*regs)[TARGET_REG_PSWM] = tswapreg(env->psw.mask); - (*regs)[TARGET_REG_PSWA] = tswapreg(env->psw.addr); + r->regs[TARGET_REG_PSWM] = tswapreg(env->psw.mask); + r->regs[TARGET_REG_PSWA] = tswapreg(env->psw.addr); for (i = 0; i < 16; i++) { - (*regs)[TARGET_REG_GPRS + i] = tswapreg(env->regs[i]); + r->regs[TARGET_REG_GPRS + i] = tswapreg(env->regs[i]); } - aregs = (uint32_t *)&((*regs)[TARGET_REG_ARS]); + aregs = (uint32_t *)&(r->regs[TARGET_REG_ARS]); for (i = 0; i < 16; i++) { aregs[i] = tswap32(env->aregs[i]); } - (*regs)[TARGET_REG_ORIG_R2] = 0; + r->regs[TARGET_REG_ORIG_R2] = 0; } #define USE_ELF_CORE_DUMP From 8caa6621109ea1fb71870aa52c688de1f7dc64e9 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Aug 2025 08:32:11 +1000 Subject: [PATCH 0153/2396] linux-user/xtensa: Convert target_elf_gregset_t to a struct Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 98c17d32e6..930701f08f 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -873,7 +873,9 @@ static bool init_guest_commpage(void) /* See linux kernel: arch/xtensa/include/asm/elf.h. */ #define ELF_NREG 128 -typedef target_elf_greg_t target_elf_gregset_t[ELF_NREG]; +typedef struct target_elf_gregset_t { + target_elf_greg_t regs[ELF_NREG]; +} target_elf_gregset_t; enum { TARGET_REG_PC, @@ -888,23 +890,23 @@ enum { TARGET_REG_AR0 = 64, }; -static void elf_core_copy_regs(target_elf_gregset_t *regs, +static void elf_core_copy_regs(target_elf_gregset_t *r, const CPUXtensaState *env) { unsigned i; - (*regs)[TARGET_REG_PC] = tswapreg(env->pc); - (*regs)[TARGET_REG_PS] = tswapreg(env->sregs[PS] & ~PS_EXCM); - (*regs)[TARGET_REG_LBEG] = tswapreg(env->sregs[LBEG]); - (*regs)[TARGET_REG_LEND] = tswapreg(env->sregs[LEND]); - (*regs)[TARGET_REG_LCOUNT] = tswapreg(env->sregs[LCOUNT]); - (*regs)[TARGET_REG_SAR] = tswapreg(env->sregs[SAR]); - (*regs)[TARGET_REG_WINDOWSTART] = tswapreg(env->sregs[WINDOW_START]); - (*regs)[TARGET_REG_WINDOWBASE] = tswapreg(env->sregs[WINDOW_BASE]); - (*regs)[TARGET_REG_THREADPTR] = tswapreg(env->uregs[THREADPTR]); + r->regs[TARGET_REG_PC] = tswapreg(env->pc); + r->regs[TARGET_REG_PS] = tswapreg(env->sregs[PS] & ~PS_EXCM); + r->regs[TARGET_REG_LBEG] = tswapreg(env->sregs[LBEG]); + r->regs[TARGET_REG_LEND] = tswapreg(env->sregs[LEND]); + r->regs[TARGET_REG_LCOUNT] = tswapreg(env->sregs[LCOUNT]); + r->regs[TARGET_REG_SAR] = tswapreg(env->sregs[SAR]); + r->regs[TARGET_REG_WINDOWSTART] = tswapreg(env->sregs[WINDOW_START]); + r->regs[TARGET_REG_WINDOWBASE] = tswapreg(env->sregs[WINDOW_BASE]); + r->regs[TARGET_REG_THREADPTR] = tswapreg(env->uregs[THREADPTR]); xtensa_sync_phys_from_window((CPUXtensaState *)env); for (i = 0; i < env->config->nareg; ++i) { - (*regs)[TARGET_REG_AR0 + i] = tswapreg(env->phys_regs[i]); + r->regs[TARGET_REG_AR0 + i] = tswapreg(env->phys_regs[i]); } } From 1b32b3d7d8d438ee46313312aa62ae8b89c90bcb Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Aug 2025 08:37:17 +1000 Subject: [PATCH 0154/2396] linux-user: Update comment for target_elf_gregset_t The only thing now used by generic core dump code is target_elf_gregset_t; ELF_NREG and target_elf_greg_t are now private to the implementation. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 930701f08f..74f88dfa68 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -2859,12 +2859,8 @@ int load_elf_binary(struct linux_binprm *bprm, struct image_info *info) * * #define USE_ELF_CORE_DUMP * - * Next you define type of register set used for dumping. ELF specification - * says that it needs to be array of elf_greg_t that has size of ELF_NREG. - * - * typedef target_elf_greg_t; - * #define ELF_NREG - * typedef taret_elf_greg_t target_elf_gregset_t[ELF_NREG]; + * Next you define type of register set used for dumping: + * typedef struct target_elf_gregset_t { ... } target_elf_gregset_t; * * Last step is to implement target specific function that copies registers * from given cpu into just specified register set. Prototype is: From 553bf7dbd466794955f93c07b5c6fc0a7f65abb4 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 2 Aug 2025 19:38:28 +1000 Subject: [PATCH 0155/2396] linux-user: Declare elf_core_copy_regs in loader.h Drop the static from all implementations. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 33 ++++++++++++++------------------- linux-user/loader.h | 3 +++ 2 files changed, 17 insertions(+), 19 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 74f88dfa68..5ed5b3c544 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -165,7 +165,7 @@ typedef struct target_elf_gregset_t { * * See linux kernel: arch/x86/include/asm/elf.h */ -static void elf_core_copy_regs(target_elf_gregset_t *r, const CPUX86State *env) +void elf_core_copy_regs(target_elf_gregset_t *r, const CPUX86State *env) { r->regs[0] = tswapreg(env->regs[15]); r->regs[1] = tswapreg(env->regs[14]); @@ -244,7 +244,7 @@ typedef struct target_elf_gregset_t { * * See linux kernel: arch/x86/include/asm/elf.h */ -static void elf_core_copy_regs(target_elf_gregset_t *r, const CPUX86State *env) +void elf_core_copy_regs(target_elf_gregset_t *r, const CPUX86State *env) { r->regs[0] = tswapreg(env->regs[R_EBX]); r->regs[1] = tswapreg(env->regs[R_ECX]); @@ -300,7 +300,7 @@ typedef struct target_elf_gregset_t { target_elf_greg_t regs[ELF_NREG]; } target_elf_gregset_t; -static void elf_core_copy_regs(target_elf_gregset_t *r, const CPUARMState *env) +void elf_core_copy_regs(target_elf_gregset_t *r, const CPUARMState *env) { for (int i = 0; i < 16; ++i) { r->regs[i] = tswapreg(env->regs[i]); @@ -388,7 +388,7 @@ typedef struct target_elf_gregset_t { target_elf_greg_t regs[ELF_NREG]; } target_elf_gregset_t; -static void elf_core_copy_regs(target_elf_gregset_t *r, const CPUARMState *env) +void elf_core_copy_regs(target_elf_gregset_t *r, const CPUARMState *env) { for (int i = 0; i < 32; i++) { r->regs[i] = tswapreg(env->xregs[i]); @@ -474,7 +474,7 @@ typedef struct target_elf_gregset_t { target_elf_greg_t regs[ELF_NREG]; } target_elf_gregset_t; -static void elf_core_copy_regs(target_elf_gregset_t *r, const CPUPPCState *env) +void elf_core_copy_regs(target_elf_gregset_t *r, const CPUPPCState *env) { int i; target_ulong ccr = 0; @@ -528,8 +528,7 @@ enum { TARGET_EF_CSR_BADV = TARGET_EF_R0 + 34, }; -static void elf_core_copy_regs(target_elf_gregset_t *r, - const CPULoongArchState *env) +void elf_core_copy_regs(target_elf_gregset_t *r, const CPULoongArchState *env) { r->regs[TARGET_EF_R0] = 0; @@ -586,7 +585,7 @@ enum { }; /* See linux kernel: arch/mips/kernel/process.c:elf_dump_regs. */ -static void elf_core_copy_regs(target_elf_gregset_t *r, const CPUMIPSState *env) +void elf_core_copy_regs(target_elf_gregset_t *r, const CPUMIPSState *env) { int i; @@ -628,7 +627,7 @@ typedef struct target_elf_gregset_t { } target_elf_gregset_t; /* See linux kernel: arch/mips/kernel/process.c:elf_dump_regs. */ -static void elf_core_copy_regs(target_elf_gregset_t *r, const CPUMBState *env) +void elf_core_copy_regs(target_elf_gregset_t *r, const CPUMBState *env) { for (int i = 0; i < 32; i++) { r->regs[i] = tswapreg(env->regs[i]); @@ -659,8 +658,7 @@ typedef struct target_elf_gregset_t { target_elf_greg_t regs[ELF_NREG]; } target_elf_gregset_t; -static void elf_core_copy_regs(target_elf_gregset_t *r, - const CPUOpenRISCState *env) +void elf_core_copy_regs(target_elf_gregset_t *r, const CPUOpenRISCState *env) { for (int i = 0; i < 32; i++) { r->regs[i] = tswapreg(cpu_get_gpr(env, i)); @@ -693,7 +691,7 @@ enum { TARGET_REG_SYSCALL = 22 }; -static void elf_core_copy_regs(target_elf_gregset_t *r, const CPUSH4State *env) +void elf_core_copy_regs(target_elf_gregset_t *r, const CPUSH4State *env) { for (int i = 0; i < 16; i++) { r->regs[i] = tswapreg(env->gregs[i]); @@ -724,7 +722,7 @@ typedef struct target_elf_gregset_t { target_elf_greg_t regs[ELF_NREG]; } target_elf_gregset_t; -static void elf_core_copy_regs(target_elf_gregset_t *r, const CPUM68KState *env) +void elf_core_copy_regs(target_elf_gregset_t *r, const CPUM68KState *env) { r->regs[0] = tswapreg(env->dregs[1]); r->regs[1] = tswapreg(env->dregs[2]); @@ -782,8 +780,7 @@ enum { TARGET_REG_ORIG_R2 = 26, }; -static void elf_core_copy_regs(target_elf_gregset_t *r, - const CPUS390XState *env) +void elf_core_copy_regs(target_elf_gregset_t *r, const CPUS390XState *env) { int i; uint32_t *aregs; @@ -890,8 +887,7 @@ enum { TARGET_REG_AR0 = 64, }; -static void elf_core_copy_regs(target_elf_gregset_t *r, - const CPUXtensaState *env) +void elf_core_copy_regs(target_elf_gregset_t *r, const CPUXtensaState *env) { unsigned i; @@ -2865,8 +2861,7 @@ int load_elf_binary(struct linux_binprm *bprm, struct image_info *info) * Last step is to implement target specific function that copies registers * from given cpu into just specified register set. Prototype is: * - * static void elf_core_copy_regs(taret_elf_gregset_t *regs, - * const CPUArchState *env); + * void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUArchState *env); * * Parameters: * regs - copy register values into here (allocated and zeroed by caller) diff --git a/linux-user/loader.h b/linux-user/loader.h index 6482c7c90c..8f4a7f69ac 100644 --- a/linux-user/loader.h +++ b/linux-user/loader.h @@ -106,4 +106,7 @@ const char *elf_hwcap2_str(uint32_t bit); const char *get_elf_platform(CPUState *cs); const char *get_elf_base_platform(CPUState *cs); +struct target_elf_gregset_t; +void elf_core_copy_regs(struct target_elf_gregset_t *, const CPUArchState *); + #endif /* LINUX_USER_LOADER_H */ From bb130c1758863f9d5c825ed7bf21669c15d6deff Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Aug 2025 09:46:08 +1000 Subject: [PATCH 0156/2396] linux-user: Rename USE_ELF_CORE_DUMP to HAVE_ELF_CORE_DUMP The other knobs in target_elf.h are all HAVE_*. Rename this USE_ELF_CORE_DUMP to match. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 5ed5b3c544..af31a34594 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -281,7 +281,7 @@ void elf_core_copy_regs(target_elf_gregset_t *r, const CPUX86State *env) #define VDSO_HEADER "vdso.c.inc" -#define USE_ELF_CORE_DUMP +#define HAVE_ELF_CORE_DUMP #define ELF_EXEC_PAGESIZE 4096 #endif /* TARGET_I386 */ @@ -309,7 +309,7 @@ void elf_core_copy_regs(target_elf_gregset_t *r, const CPUARMState *env) r->regs[17] = tswapreg(env->regs[0]); /* XXX */ } -#define USE_ELF_CORE_DUMP +#define HAVE_ELF_CORE_DUMP #define ELF_EXEC_PAGESIZE 4096 /* The commpage only exists for 32 bit kernels */ @@ -397,7 +397,7 @@ void elf_core_copy_regs(target_elf_gregset_t *r, const CPUARMState *env) r->regs[33] = tswapreg(pstate_read((CPUARMState *)env)); } -#define USE_ELF_CORE_DUMP +#define HAVE_ELF_CORE_DUMP #define ELF_EXEC_PAGESIZE 4096 #if TARGET_BIG_ENDIAN @@ -493,7 +493,7 @@ void elf_core_copy_regs(target_elf_gregset_t *r, const CPUPPCState *env) r->regs[38] = tswapreg(ccr); } -#define USE_ELF_CORE_DUMP +#define HAVE_ELF_CORE_DUMP #define ELF_EXEC_PAGESIZE 4096 #ifndef TARGET_PPC64 @@ -540,7 +540,7 @@ void elf_core_copy_regs(target_elf_gregset_t *r, const CPULoongArchState *env) r->regs[TARGET_EF_CSR_BADV] = tswapreg(env->CSR_BADV); } -#define USE_ELF_CORE_DUMP +#define HAVE_ELF_CORE_DUMP #define ELF_EXEC_PAGESIZE 4096 #endif /* TARGET_LOONGARCH64 */ @@ -606,7 +606,7 @@ void elf_core_copy_regs(target_elf_gregset_t *r, const CPUMIPSState *env) r->regs[TARGET_EF_CP0_CAUSE] = tswapreg(env->CP0_Cause); } -#define USE_ELF_CORE_DUMP +#define HAVE_ELF_CORE_DUMP #define ELF_EXEC_PAGESIZE 4096 #endif /* TARGET_MIPS */ @@ -620,7 +620,7 @@ void elf_core_copy_regs(target_elf_gregset_t *r, const CPUMIPSState *env) #define ELF_EXEC_PAGESIZE 4096 -#define USE_ELF_CORE_DUMP +#define HAVE_ELF_CORE_DUMP #define ELF_NREG 38 typedef struct target_elf_gregset_t { target_elf_greg_t regs[ELF_NREG]; @@ -649,7 +649,7 @@ void elf_core_copy_regs(target_elf_gregset_t *r, const CPUMBState *env) #define ELF_CLASS ELFCLASS32 #define ELF_DATA ELFDATA2MSB -#define USE_ELF_CORE_DUMP +#define HAVE_ELF_CORE_DUMP #define ELF_EXEC_PAGESIZE 8192 /* See linux kernel arch/openrisc/include/asm/elf.h. */ @@ -706,7 +706,7 @@ void elf_core_copy_regs(target_elf_gregset_t *r, const CPUSH4State *env) r->regs[TARGET_REG_SYSCALL] = 0; /* FIXME */ } -#define USE_ELF_CORE_DUMP +#define HAVE_ELF_CORE_DUMP #define ELF_EXEC_PAGESIZE 4096 #endif @@ -746,7 +746,7 @@ void elf_core_copy_regs(target_elf_gregset_t *r, const CPUM68KState *env) r->regs[19] = 0; /* FIXME: regs->format | regs->vector */ } -#define USE_ELF_CORE_DUMP +#define HAVE_ELF_CORE_DUMP #define ELF_EXEC_PAGESIZE 8192 #endif @@ -797,7 +797,7 @@ void elf_core_copy_regs(target_elf_gregset_t *r, const CPUS390XState *env) r->regs[TARGET_REG_ORIG_R2] = 0; } -#define USE_ELF_CORE_DUMP +#define HAVE_ELF_CORE_DUMP #define ELF_EXEC_PAGESIZE 4096 #define VDSO_HEADER "vdso.c.inc" @@ -906,7 +906,7 @@ void elf_core_copy_regs(target_elf_gregset_t *r, const CPUXtensaState *env) } } -#define USE_ELF_CORE_DUMP +#define HAVE_ELF_CORE_DUMP #define ELF_EXEC_PAGESIZE 4096 #endif /* TARGET_XTENSA */ @@ -1115,9 +1115,9 @@ static void bswap_mips_abiflags(Mips_elf_abiflags_v0 *abiflags) } #endif -#ifdef USE_ELF_CORE_DUMP +#ifdef HAVE_ELF_CORE_DUMP static int elf_core_dump(int, const CPUArchState *); -#endif /* USE_ELF_CORE_DUMP */ +#endif /* HAVE_ELF_CORE_DUMP */ static void load_symbols(struct elfhdr *hdr, const ImageSource *src, abi_ulong load_bias); @@ -2827,14 +2827,14 @@ int load_elf_binary(struct linux_binprm *bprm, struct image_info *info) g_free(elf_interpreter); } -#ifdef USE_ELF_CORE_DUMP +#ifdef HAVE_ELF_CORE_DUMP bprm->core_dump = &elf_core_dump; #endif return 0; } -#ifdef USE_ELF_CORE_DUMP +#ifdef HAVE_ELF_CORE_DUMP /* * Definitions to generate Intel SVR4-like core files. @@ -2850,10 +2850,10 @@ int load_elf_binary(struct linux_binprm *bprm, struct image_info *info) * Core dump code is copied from linux kernel (fs/binfmt_elf.c). * * Porting ELF coredump for target is (quite) simple process. First you - * define USE_ELF_CORE_DUMP in target ELF code (where init_thread() for + * define HAVE_ELF_CORE_DUMP in target ELF code (where init_thread() for * the target resides): * - * #define USE_ELF_CORE_DUMP + * #define HAVE_ELF_CORE_DUMP * * Next you define type of register set used for dumping: * typedef struct target_elf_gregset_t { ... } target_elf_gregset_t; @@ -3392,4 +3392,4 @@ static int elf_core_dump(int signr, const CPUArchState *env) } return ret; } -#endif /* USE_ELF_CORE_DUMP */ +#endif /* HAVE_ELF_CORE_DUMP */ From 93c62ca6fe31dadbdb38688f67c741ee00448a24 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Aug 2025 09:48:07 +1000 Subject: [PATCH 0157/2396] linux-user: Move elf_core_copy_regs to {i386,x86_64}/elfload.c Move elf_core_copy_regs to elfload.c. Move HAVE_ELF_CORE_DUMP, ELF_NREGS, target_elf_gregset_t to target_elf.h. For now, duplicate the definitions of target_elf_greg_t and tswapreg. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 77 ---------------------------------- linux-user/i386/elfload.c | 24 +++++++++++ linux-user/i386/target_elf.h | 15 +++++++ linux-user/x86_64/elfload.c | 34 +++++++++++++++ linux-user/x86_64/target_elf.h | 15 +++++++ 5 files changed, 88 insertions(+), 77 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index af31a34594..e4f821f8c8 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -153,49 +153,6 @@ typedef abi_int target_pid_t; #define ELF_CLASS ELFCLASS64 #define ELF_ARCH EM_X86_64 -#define ELF_NREG 27 -typedef struct target_elf_gregset_t { - target_elf_greg_t regs[ELF_NREG]; -} target_elf_gregset_t; - -/* - * Note that ELF_NREG should be 29 as there should be place for - * TRAPNO and ERR "registers" as well but linux doesn't dump - * those. - * - * See linux kernel: arch/x86/include/asm/elf.h - */ -void elf_core_copy_regs(target_elf_gregset_t *r, const CPUX86State *env) -{ - r->regs[0] = tswapreg(env->regs[15]); - r->regs[1] = tswapreg(env->regs[14]); - r->regs[2] = tswapreg(env->regs[13]); - r->regs[3] = tswapreg(env->regs[12]); - r->regs[4] = tswapreg(env->regs[R_EBP]); - r->regs[5] = tswapreg(env->regs[R_EBX]); - r->regs[6] = tswapreg(env->regs[11]); - r->regs[7] = tswapreg(env->regs[10]); - r->regs[8] = tswapreg(env->regs[9]); - r->regs[9] = tswapreg(env->regs[8]); - r->regs[10] = tswapreg(env->regs[R_EAX]); - r->regs[11] = tswapreg(env->regs[R_ECX]); - r->regs[12] = tswapreg(env->regs[R_EDX]); - r->regs[13] = tswapreg(env->regs[R_ESI]); - r->regs[14] = tswapreg(env->regs[R_EDI]); - r->regs[15] = tswapreg(get_task_state(env_cpu_const(env))->orig_ax); - r->regs[16] = tswapreg(env->eip); - r->regs[17] = tswapreg(env->segs[R_CS].selector & 0xffff); - r->regs[18] = tswapreg(env->eflags); - r->regs[19] = tswapreg(env->regs[R_ESP]); - r->regs[20] = tswapreg(env->segs[R_SS].selector & 0xffff); - r->regs[21] = tswapreg(env->segs[R_FS].selector & 0xffff); - r->regs[22] = tswapreg(env->segs[R_GS].selector & 0xffff); - r->regs[23] = tswapreg(env->segs[R_DS].selector & 0xffff); - r->regs[24] = tswapreg(env->segs[R_ES].selector & 0xffff); - r->regs[25] = tswapreg(env->segs[R_FS].selector & 0xffff); - r->regs[26] = tswapreg(env->segs[R_GS].selector & 0xffff); -} - #if ULONG_MAX > UINT32_MAX #define INIT_GUEST_COMMPAGE static bool init_guest_commpage(void) @@ -232,39 +189,6 @@ static bool init_guest_commpage(void) #define EXSTACK_DEFAULT true -#define ELF_NREG 17 -typedef struct target_elf_gregset_t { - target_elf_greg_t regs[ELF_NREG]; -} target_elf_gregset_t; - -/* - * Note that ELF_NREG should be 19 as there should be place for - * TRAPNO and ERR "registers" as well but linux doesn't dump - * those. - * - * See linux kernel: arch/x86/include/asm/elf.h - */ -void elf_core_copy_regs(target_elf_gregset_t *r, const CPUX86State *env) -{ - r->regs[0] = tswapreg(env->regs[R_EBX]); - r->regs[1] = tswapreg(env->regs[R_ECX]); - r->regs[2] = tswapreg(env->regs[R_EDX]); - r->regs[3] = tswapreg(env->regs[R_ESI]); - r->regs[4] = tswapreg(env->regs[R_EDI]); - r->regs[5] = tswapreg(env->regs[R_EBP]); - r->regs[6] = tswapreg(env->regs[R_EAX]); - r->regs[7] = tswapreg(env->segs[R_DS].selector & 0xffff); - r->regs[8] = tswapreg(env->segs[R_ES].selector & 0xffff); - r->regs[9] = tswapreg(env->segs[R_FS].selector & 0xffff); - r->regs[10] = tswapreg(env->segs[R_GS].selector & 0xffff); - r->regs[11] = tswapreg(get_task_state(env_cpu_const(env))->orig_ax); - r->regs[12] = tswapreg(env->eip); - r->regs[13] = tswapreg(env->segs[R_CS].selector & 0xffff); - r->regs[14] = tswapreg(env->eflags); - r->regs[15] = tswapreg(env->regs[R_ESP]); - r->regs[16] = tswapreg(env->segs[R_SS].selector & 0xffff); -} - /* * i386 is the only target which supplies AT_SYSINFO for the vdso. * All others only supply AT_SYSINFO_EHDR. @@ -281,7 +205,6 @@ void elf_core_copy_regs(target_elf_gregset_t *r, const CPUX86State *env) #define VDSO_HEADER "vdso.c.inc" -#define HAVE_ELF_CORE_DUMP #define ELF_EXEC_PAGESIZE 4096 #endif /* TARGET_I386 */ diff --git a/linux-user/i386/elfload.c b/linux-user/i386/elfload.c index ef3a6c35d2..279aeb8116 100644 --- a/linux-user/i386/elfload.c +++ b/linux-user/i386/elfload.c @@ -3,6 +3,7 @@ #include "qemu/osdep.h" #include "qemu.h" #include "loader.h" +#include "target_elf.h" const char *get_elf_cpu_model(uint32_t eflags) @@ -23,3 +24,26 @@ const char *get_elf_platform(CPUState *cs) family = MAX(MIN(family, 6), 3); return elf_platform[family - 3]; } + +#define tswapreg(ptr) tswapal(ptr) + +void elf_core_copy_regs(target_elf_gregset_t *r, const CPUX86State *env) +{ + r->regs[0] = tswapreg(env->regs[R_EBX]); + r->regs[1] = tswapreg(env->regs[R_ECX]); + r->regs[2] = tswapreg(env->regs[R_EDX]); + r->regs[3] = tswapreg(env->regs[R_ESI]); + r->regs[4] = tswapreg(env->regs[R_EDI]); + r->regs[5] = tswapreg(env->regs[R_EBP]); + r->regs[6] = tswapreg(env->regs[R_EAX]); + r->regs[7] = tswapreg(env->segs[R_DS].selector & 0xffff); + r->regs[8] = tswapreg(env->segs[R_ES].selector & 0xffff); + r->regs[9] = tswapreg(env->segs[R_FS].selector & 0xffff); + r->regs[10] = tswapreg(env->segs[R_GS].selector & 0xffff); + r->regs[11] = tswapreg(get_task_state(env_cpu_const(env))->orig_ax); + r->regs[12] = tswapreg(env->eip); + r->regs[13] = tswapreg(env->segs[R_CS].selector & 0xffff); + r->regs[14] = tswapreg(env->eflags); + r->regs[15] = tswapreg(env->regs[R_ESP]); + r->regs[16] = tswapreg(env->segs[R_SS].selector & 0xffff); +} diff --git a/linux-user/i386/target_elf.h b/linux-user/i386/target_elf.h index 44dde1ac4a..eb286868e1 100644 --- a/linux-user/i386/target_elf.h +++ b/linux-user/i386/target_elf.h @@ -10,5 +10,20 @@ #define HAVE_ELF_HWCAP 1 #define HAVE_ELF_PLATFORM 1 +#define HAVE_ELF_CORE_DUMP 1 + +/* + * Note that ELF_NREG should be 19 as there should be place for + * TRAPNO and ERR "registers" as well but linux doesn't dump those. + * + * See linux kernel: arch/x86/include/asm/elf.h + */ +#define ELF_NREG 17 + +typedef abi_ulong target_elf_greg_t; + +typedef struct target_elf_gregset_t { + target_elf_greg_t regs[ELF_NREG]; +} target_elf_gregset_t; #endif diff --git a/linux-user/x86_64/elfload.c b/linux-user/x86_64/elfload.c index 88541ea45e..76cf5c1509 100644 --- a/linux-user/x86_64/elfload.c +++ b/linux-user/x86_64/elfload.c @@ -3,6 +3,7 @@ #include "qemu/osdep.h" #include "qemu.h" #include "loader.h" +#include "target_elf.h" const char *get_elf_cpu_model(uint32_t eflags) @@ -19,3 +20,36 @@ const char *get_elf_platform(CPUState *cs) { return "x86_64"; } + +#define tswapreg(ptr) tswapal(ptr) + +void elf_core_copy_regs(target_elf_gregset_t *r, const CPUX86State *env) +{ + r->regs[0] = tswapreg(env->regs[15]); + r->regs[1] = tswapreg(env->regs[14]); + r->regs[2] = tswapreg(env->regs[13]); + r->regs[3] = tswapreg(env->regs[12]); + r->regs[4] = tswapreg(env->regs[R_EBP]); + r->regs[5] = tswapreg(env->regs[R_EBX]); + r->regs[6] = tswapreg(env->regs[11]); + r->regs[7] = tswapreg(env->regs[10]); + r->regs[8] = tswapreg(env->regs[9]); + r->regs[9] = tswapreg(env->regs[8]); + r->regs[10] = tswapreg(env->regs[R_EAX]); + r->regs[11] = tswapreg(env->regs[R_ECX]); + r->regs[12] = tswapreg(env->regs[R_EDX]); + r->regs[13] = tswapreg(env->regs[R_ESI]); + r->regs[14] = tswapreg(env->regs[R_EDI]); + r->regs[15] = tswapreg(get_task_state(env_cpu_const(env))->orig_ax); + r->regs[16] = tswapreg(env->eip); + r->regs[17] = tswapreg(env->segs[R_CS].selector & 0xffff); + r->regs[18] = tswapreg(env->eflags); + r->regs[19] = tswapreg(env->regs[R_ESP]); + r->regs[20] = tswapreg(env->segs[R_SS].selector & 0xffff); + r->regs[21] = tswapreg(env->segs[R_FS].selector & 0xffff); + r->regs[22] = tswapreg(env->segs[R_GS].selector & 0xffff); + r->regs[23] = tswapreg(env->segs[R_DS].selector & 0xffff); + r->regs[24] = tswapreg(env->segs[R_ES].selector & 0xffff); + r->regs[25] = tswapreg(env->segs[R_FS].selector & 0xffff); + r->regs[26] = tswapreg(env->segs[R_GS].selector & 0xffff); +} diff --git a/linux-user/x86_64/target_elf.h b/linux-user/x86_64/target_elf.h index 498c3f7e4e..74a77d94cd 100644 --- a/linux-user/x86_64/target_elf.h +++ b/linux-user/x86_64/target_elf.h @@ -10,5 +10,20 @@ #define HAVE_ELF_HWCAP 1 #define HAVE_ELF_PLATFORM 1 +#define HAVE_ELF_CORE_DUMP 1 + +/* + * Note that ELF_NREG should be 29 as there should be place for + * TRAPNO and ERR "registers" as well but linux doesn't dump those. + * + * See linux kernel: arch/x86/include/asm/elf.h + */ +#define ELF_NREG 27 + +typedef abi_ulong target_elf_greg_t; + +typedef struct target_elf_gregset_t { + target_elf_greg_t regs[ELF_NREG]; +} target_elf_gregset_t; #endif From 53c6724cc96acc64bf6213e5820e3cd610e7eaa4 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 29 Jul 2025 05:51:30 -1000 Subject: [PATCH 0158/2396] linux-user: Move elf_core_copy_regs to arm/elfload.c Move elf_core_copy_regs to elfload.c. Move HAVE_ELF_CORE_DUMP, ELF_NREGS, target_elf_gregset_t to target_elf.h. For now, duplicate the definitions of target_elf_greg_t and tswapreg. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/arm/elfload.c | 12 ++++++++++++ linux-user/arm/target_elf.h | 8 ++++++++ linux-user/elfload.c | 15 --------------- 3 files changed, 20 insertions(+), 15 deletions(-) diff --git a/linux-user/arm/elfload.c b/linux-user/arm/elfload.c index 7de1f13f4b..47fe16a1a6 100644 --- a/linux-user/arm/elfload.c +++ b/linux-user/arm/elfload.c @@ -4,6 +4,7 @@ #include "qemu.h" #include "loader.h" #include "target/arm/cpu-features.h" +#include "target_elf.h" const char *get_elf_cpu_model(uint32_t eflags) @@ -199,3 +200,14 @@ const char *get_elf_platform(CPUState *cs) #undef END } + +#define tswapreg(ptr) tswapal(ptr) + +void elf_core_copy_regs(target_elf_gregset_t *r, const CPUARMState *env) +{ + for (int i = 0; i < 16; ++i) { + r->regs[i] = tswapreg(env->regs[i]); + } + r->regs[16] = tswapreg(cpsr_read((CPUARMState *)env)); + r->regs[17] = tswapreg(env->regs[0]); /* XXX */ +} diff --git a/linux-user/arm/target_elf.h b/linux-user/arm/target_elf.h index 856ca41b16..94db3738e8 100644 --- a/linux-user/arm/target_elf.h +++ b/linux-user/arm/target_elf.h @@ -11,5 +11,13 @@ #define HAVE_ELF_HWCAP 1 #define HAVE_ELF_HWCAP2 1 #define HAVE_ELF_PLATFORM 1 +#define HAVE_ELF_CORE_DUMP 1 + +typedef abi_ulong target_elf_greg_t; + +#define ELF_NREG 18 +typedef struct target_elf_gregset_t { + target_elf_greg_t regs[ELF_NREG]; +} target_elf_gregset_t; #endif diff --git a/linux-user/elfload.c b/linux-user/elfload.c index e4f821f8c8..72a291e51f 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -218,21 +218,6 @@ static bool init_guest_commpage(void) #define ELF_CLASS ELFCLASS32 #define EXSTACK_DEFAULT true -#define ELF_NREG 18 -typedef struct target_elf_gregset_t { - target_elf_greg_t regs[ELF_NREG]; -} target_elf_gregset_t; - -void elf_core_copy_regs(target_elf_gregset_t *r, const CPUARMState *env) -{ - for (int i = 0; i < 16; ++i) { - r->regs[i] = tswapreg(env->regs[i]); - } - r->regs[16] = tswapreg(cpsr_read((CPUARMState *)env)); - r->regs[17] = tswapreg(env->regs[0]); /* XXX */ -} - -#define HAVE_ELF_CORE_DUMP #define ELF_EXEC_PAGESIZE 4096 /* The commpage only exists for 32 bit kernels */ From b71b68b233dd828960e57702580a97b11f3fd26f Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 29 Jul 2025 05:53:29 -1000 Subject: [PATCH 0159/2396] linux-user: Move elf_core_copy_regs to aarch64/elfload.c Move elf_core_copy_regs to elfload.c. Move HAVE_ELF_CORE_DUMP, ELF_NREGS, target_elf_gregset_t to target_elf.h. For now, duplicate the definitions of target_elf_greg_t and tswapreg. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/aarch64/elfload.c | 12 ++++++++++++ linux-user/aarch64/target_elf.h | 8 ++++++++ linux-user/elfload.c | 15 --------------- 3 files changed, 20 insertions(+), 15 deletions(-) diff --git a/linux-user/aarch64/elfload.c b/linux-user/aarch64/elfload.c index 1030cb8094..00550f9fdf 100644 --- a/linux-user/aarch64/elfload.c +++ b/linux-user/aarch64/elfload.c @@ -4,6 +4,7 @@ #include "qemu.h" #include "loader.h" #include "target/arm/cpu-features.h" +#include "target_elf.h" const char *get_elf_cpu_model(uint32_t eflags) @@ -347,3 +348,14 @@ const char *get_elf_platform(CPUState *cs) { return TARGET_BIG_ENDIAN ? "aarch64_be" : "aarch64"; } + +#define tswapreg(ptr) tswapal(ptr) + +void elf_core_copy_regs(target_elf_gregset_t *r, const CPUARMState *env) +{ + for (int i = 0; i < 32; i++) { + r->regs[i] = tswapreg(env->xregs[i]); + } + r->regs[32] = tswapreg(env->pc); + r->regs[33] = tswapreg(pstate_read((CPUARMState *)env)); +} diff --git a/linux-user/aarch64/target_elf.h b/linux-user/aarch64/target_elf.h index dee79ce0c6..b0728a1008 100644 --- a/linux-user/aarch64/target_elf.h +++ b/linux-user/aarch64/target_elf.h @@ -11,5 +11,13 @@ #define HAVE_ELF_HWCAP 1 #define HAVE_ELF_HWCAP2 1 #define HAVE_ELF_PLATFORM 1 +#define HAVE_ELF_CORE_DUMP 1 + +typedef abi_ulong target_elf_greg_t; + +#define ELF_NREG 34 +typedef struct target_elf_gregset_t { + target_elf_greg_t regs[ELF_NREG]; +} target_elf_gregset_t; #endif diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 72a291e51f..017346b82d 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -291,21 +291,6 @@ static const VdsoImageInfo *vdso_image_info(uint32_t elf_flags) #define ELF_ARCH EM_AARCH64 #define ELF_CLASS ELFCLASS64 -#define ELF_NREG 34 -typedef struct target_elf_gregset_t { - target_elf_greg_t regs[ELF_NREG]; -} target_elf_gregset_t; - -void elf_core_copy_regs(target_elf_gregset_t *r, const CPUARMState *env) -{ - for (int i = 0; i < 32; i++) { - r->regs[i] = tswapreg(env->xregs[i]); - } - r->regs[32] = tswapreg(env->pc); - r->regs[33] = tswapreg(pstate_read((CPUARMState *)env)); -} - -#define HAVE_ELF_CORE_DUMP #define ELF_EXEC_PAGESIZE 4096 #if TARGET_BIG_ENDIAN From a67e20d629a849d9d9d5ef527963462cd0718e78 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 29 Jul 2025 05:55:56 -1000 Subject: [PATCH 0160/2396] linux-user: Move elf_core_copy_regs to ppc/elfload.c Move elf_core_copy_regs to elfload.c. Move HAVE_ELF_CORE_DUMP, ELF_NREGS, target_elf_gregset_t to target_elf.h. For now, duplicate the definitions of target_elf_greg_t and tswapreg. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 26 -------------------------- linux-user/ppc/elfload.c | 22 ++++++++++++++++++++++ linux-user/ppc/target_elf.h | 9 +++++++++ 3 files changed, 31 insertions(+), 26 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 017346b82d..d1d0a112fb 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -361,32 +361,6 @@ static const VdsoImageInfo *vdso_image_info(uint32_t elf_flags) NEW_AUX_ENT(AT_UCACHEBSIZE, 0); \ } while (0) -/* See linux kernel: arch/powerpc/include/asm/elf.h. */ -#define ELF_NREG 48 -typedef struct target_elf_gregset_t { - target_elf_greg_t regs[ELF_NREG]; -} target_elf_gregset_t; - -void elf_core_copy_regs(target_elf_gregset_t *r, const CPUPPCState *env) -{ - int i; - target_ulong ccr = 0; - - for (i = 0; i < ARRAY_SIZE(env->gpr); i++) { - r->regs[i] = tswapreg(env->gpr[i]); - } - - r->regs[32] = tswapreg(env->nip); - r->regs[33] = tswapreg(env->msr); - r->regs[35] = tswapreg(env->ctr); - r->regs[36] = tswapreg(env->lr); - r->regs[37] = tswapreg(cpu_read_xer(env)); - - ccr = ppc_get_cr(env); - r->regs[38] = tswapreg(ccr); -} - -#define HAVE_ELF_CORE_DUMP #define ELF_EXEC_PAGESIZE 4096 #ifndef TARGET_PPC64 diff --git a/linux-user/ppc/elfload.c b/linux-user/ppc/elfload.c index a214675650..114e40a358 100644 --- a/linux-user/ppc/elfload.c +++ b/linux-user/ppc/elfload.c @@ -3,6 +3,7 @@ #include "qemu/osdep.h" #include "qemu.h" #include "loader.h" +#include "target_elf.h" const char *get_elf_cpu_model(uint32_t eflags) @@ -129,3 +130,24 @@ abi_ulong get_elf_hwcap2(CPUState *cs) return features; } + +#define tswapreg(ptr) tswapal(ptr) + +void elf_core_copy_regs(target_elf_gregset_t *r, const CPUPPCState *env) +{ + int i; + target_ulong ccr = 0; + + for (i = 0; i < ARRAY_SIZE(env->gpr); i++) { + r->regs[i] = tswapreg(env->gpr[i]); + } + + r->regs[32] = tswapreg(env->nip); + r->regs[33] = tswapreg(env->msr); + r->regs[35] = tswapreg(env->ctr); + r->regs[36] = tswapreg(env->lr); + r->regs[37] = tswapreg(cpu_read_xer(env)); + + ccr = ppc_get_cr(env); + r->regs[38] = tswapreg(ccr); +} diff --git a/linux-user/ppc/target_elf.h b/linux-user/ppc/target_elf.h index 4203a89d66..72615553ea 100644 --- a/linux-user/ppc/target_elf.h +++ b/linux-user/ppc/target_elf.h @@ -10,5 +10,14 @@ #define HAVE_ELF_HWCAP 1 #define HAVE_ELF_HWCAP2 1 +#define HAVE_ELF_CORE_DUMP 1 + +typedef abi_ulong target_elf_greg_t; + +/* See linux kernel: arch/powerpc/include/asm/elf.h. */ +#define ELF_NREG 48 +typedef struct target_elf_gregset_t { + target_elf_greg_t regs[ELF_NREG]; +} target_elf_gregset_t; #endif From cf334829cb2a68ba1582923c773557b6fd20e123 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 29 Jul 2025 05:58:48 -1000 Subject: [PATCH 0161/2396] linux-user: Move elf_core_copy_regs to loongarch64/elfload.c Move elf_core_copy_regs to elfload.c. Move HAVE_ELF_CORE_DUMP, ELF_NREGS, target_elf_gregset_t to target_elf.h. For now, duplicate the definitions of target_elf_greg_t and tswapreg. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 25 ------------------------- linux-user/loongarch64/elfload.c | 21 +++++++++++++++++++++ linux-user/loongarch64/target_elf.h | 9 +++++++++ 3 files changed, 30 insertions(+), 25 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index d1d0a112fb..4acd7b9ffe 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -383,31 +383,6 @@ static const VdsoImageInfo *vdso_image_info(uint32_t elf_flags) #define VDSO_HEADER "vdso.c.inc" -/* See linux kernel: arch/loongarch/include/asm/elf.h */ -#define ELF_NREG 45 -typedef struct target_elf_gregset_t { - target_elf_greg_t regs[ELF_NREG]; -} target_elf_gregset_t; - -enum { - TARGET_EF_R0 = 0, - TARGET_EF_CSR_ERA = TARGET_EF_R0 + 33, - TARGET_EF_CSR_BADV = TARGET_EF_R0 + 34, -}; - -void elf_core_copy_regs(target_elf_gregset_t *r, const CPULoongArchState *env) -{ - r->regs[TARGET_EF_R0] = 0; - - for (int i = 1; i < ARRAY_SIZE(env->gpr); i++) { - r->regs[TARGET_EF_R0 + i] = tswapreg(env->gpr[i]); - } - - r->regs[TARGET_EF_CSR_ERA] = tswapreg(env->pc); - r->regs[TARGET_EF_CSR_BADV] = tswapreg(env->CSR_BADV); -} - -#define HAVE_ELF_CORE_DUMP #define ELF_EXEC_PAGESIZE 4096 #endif /* TARGET_LOONGARCH64 */ diff --git a/linux-user/loongarch64/elfload.c b/linux-user/loongarch64/elfload.c index 911352840f..832890de10 100644 --- a/linux-user/loongarch64/elfload.c +++ b/linux-user/loongarch64/elfload.c @@ -3,6 +3,7 @@ #include "qemu/osdep.h" #include "qemu.h" #include "loader.h" +#include "target_elf.h" const char *get_elf_cpu_model(uint32_t eflags) @@ -61,3 +62,23 @@ const char *get_elf_platform(CPUState *cs) { return "loongarch"; } + +#define tswapreg(ptr) tswapal(ptr) + +enum { + TARGET_EF_R0 = 0, + TARGET_EF_CSR_ERA = TARGET_EF_R0 + 33, + TARGET_EF_CSR_BADV = TARGET_EF_R0 + 34, +}; + +void elf_core_copy_regs(target_elf_gregset_t *r, const CPULoongArchState *env) +{ + r->regs[TARGET_EF_R0] = 0; + + for (int i = 1; i < ARRAY_SIZE(env->gpr); i++) { + r->regs[TARGET_EF_R0 + i] = tswapreg(env->gpr[i]); + } + + r->regs[TARGET_EF_CSR_ERA] = tswapreg(env->pc); + r->regs[TARGET_EF_CSR_BADV] = tswapreg(env->CSR_BADV); +} diff --git a/linux-user/loongarch64/target_elf.h b/linux-user/loongarch64/target_elf.h index eb17927325..90bca4499d 100644 --- a/linux-user/loongarch64/target_elf.h +++ b/linux-user/loongarch64/target_elf.h @@ -8,5 +8,14 @@ #define HAVE_ELF_HWCAP 1 #define HAVE_ELF_PLATFORM 1 +#define HAVE_ELF_CORE_DUMP 1 + +typedef abi_ulong target_elf_greg_t; + +/* See linux kernel: arch/loongarch/include/asm/elf.h */ +#define ELF_NREG 45 +typedef struct target_elf_gregset_t { + target_elf_greg_t regs[ELF_NREG]; +} target_elf_gregset_t; #endif From a8081da18de8f3558b593e9c1ff12b9319c1d892 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 29 Jul 2025 06:03:38 -1000 Subject: [PATCH 0162/2396] linux-user: Move elf_core_copy_regs to mips/elfload.c Move elf_core_copy_regs to elfload.c. Move HAVE_ELF_CORE_DUMP, ELF_NREGS, target_elf_gregset_t to target_elf.h. For now, duplicate the definitions of target_elf_greg_t and tswapreg. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 46 ---------------------------------- linux-user/mips/elfload.c | 46 ++++++++++++++++++++++++++++++++++ linux-user/mips/target_elf.h | 9 +++++++ linux-user/mips64/target_elf.h | 13 ++++++++++ 4 files changed, 68 insertions(+), 46 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 4acd7b9ffe..5a3a5cfc39 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -403,52 +403,6 @@ static const VdsoImageInfo *vdso_image_info(uint32_t elf_flags) #define elf_check_abi(x) (!((x) & EF_MIPS_ABI2)) #endif -/* See linux kernel: arch/mips/include/asm/elf.h. */ -#define ELF_NREG 45 -typedef struct target_elf_gregset_t { - target_elf_greg_t regs[ELF_NREG]; -} target_elf_gregset_t; - -/* See linux kernel: arch/mips/include/asm/reg.h. */ -enum { -#ifdef TARGET_MIPS64 - TARGET_EF_R0 = 0, -#else - TARGET_EF_R0 = 6, -#endif - TARGET_EF_R26 = TARGET_EF_R0 + 26, - TARGET_EF_R27 = TARGET_EF_R0 + 27, - TARGET_EF_LO = TARGET_EF_R0 + 32, - TARGET_EF_HI = TARGET_EF_R0 + 33, - TARGET_EF_CP0_EPC = TARGET_EF_R0 + 34, - TARGET_EF_CP0_BADVADDR = TARGET_EF_R0 + 35, - TARGET_EF_CP0_STATUS = TARGET_EF_R0 + 36, - TARGET_EF_CP0_CAUSE = TARGET_EF_R0 + 37 -}; - -/* See linux kernel: arch/mips/kernel/process.c:elf_dump_regs. */ -void elf_core_copy_regs(target_elf_gregset_t *r, const CPUMIPSState *env) -{ - int i; - - for (i = 0; i <= TARGET_EF_R0; i++) { - r->regs[i] = 0; - } - for (i = 1; i < ARRAY_SIZE(env->active_tc.gpr); i++) { - r->regs[TARGET_EF_R0 + i] = tswapreg(env->active_tc.gpr[i]); - } - - r->regs[TARGET_EF_R26] = 0; - r->regs[TARGET_EF_R27] = 0; - r->regs[TARGET_EF_LO] = tswapreg(env->active_tc.LO[0]); - r->regs[TARGET_EF_HI] = tswapreg(env->active_tc.HI[0]); - r->regs[TARGET_EF_CP0_EPC] = tswapreg(env->active_tc.PC); - r->regs[TARGET_EF_CP0_BADVADDR] = tswapreg(env->CP0_BadVAddr); - r->regs[TARGET_EF_CP0_STATUS] = tswapreg(env->CP0_Status); - r->regs[TARGET_EF_CP0_CAUSE] = tswapreg(env->CP0_Cause); -} - -#define HAVE_ELF_CORE_DUMP #define ELF_EXEC_PAGESIZE 4096 #endif /* TARGET_MIPS */ diff --git a/linux-user/mips/elfload.c b/linux-user/mips/elfload.c index c353ccc1ad..6e884911af 100644 --- a/linux-user/mips/elfload.c +++ b/linux-user/mips/elfload.c @@ -4,6 +4,7 @@ #include "qemu.h" #include "loader.h" #include "elf.h" +#include "target_elf.h" const char *get_elf_cpu_model(uint32_t eflags) @@ -122,3 +123,48 @@ const char *get_elf_base_platform(CPUState *cs) } #undef MATCH_PLATFORM_INSN + +#ifdef TARGET_ABI_MIPSN32 +#define tswapreg(ptr) tswap64(ptr) +#else +#define tswapreg(ptr) tswapal(ptr) +#endif + +/* See linux kernel: arch/mips/include/asm/reg.h. */ +enum { +#ifdef TARGET_MIPS64 + TARGET_EF_R0 = 0, +#else + TARGET_EF_R0 = 6, +#endif + TARGET_EF_R26 = TARGET_EF_R0 + 26, + TARGET_EF_R27 = TARGET_EF_R0 + 27, + TARGET_EF_LO = TARGET_EF_R0 + 32, + TARGET_EF_HI = TARGET_EF_R0 + 33, + TARGET_EF_CP0_EPC = TARGET_EF_R0 + 34, + TARGET_EF_CP0_BADVADDR = TARGET_EF_R0 + 35, + TARGET_EF_CP0_STATUS = TARGET_EF_R0 + 36, + TARGET_EF_CP0_CAUSE = TARGET_EF_R0 + 37 +}; + +/* See linux kernel: arch/mips/kernel/process.c:elf_dump_regs. */ +void elf_core_copy_regs(target_elf_gregset_t *r, const CPUMIPSState *env) +{ + int i; + + for (i = 0; i <= TARGET_EF_R0; i++) { + r->regs[i] = 0; + } + for (i = 1; i < ARRAY_SIZE(env->active_tc.gpr); i++) { + r->regs[TARGET_EF_R0 + i] = tswapreg(env->active_tc.gpr[i]); + } + + r->regs[TARGET_EF_R26] = 0; + r->regs[TARGET_EF_R27] = 0; + r->regs[TARGET_EF_LO] = tswapreg(env->active_tc.LO[0]); + r->regs[TARGET_EF_HI] = tswapreg(env->active_tc.HI[0]); + r->regs[TARGET_EF_CP0_EPC] = tswapreg(env->active_tc.PC); + r->regs[TARGET_EF_CP0_BADVADDR] = tswapreg(env->CP0_BadVAddr); + r->regs[TARGET_EF_CP0_STATUS] = tswapreg(env->CP0_Status); + r->regs[TARGET_EF_CP0_CAUSE] = tswapreg(env->CP0_Cause); +} diff --git a/linux-user/mips/target_elf.h b/linux-user/mips/target_elf.h index 08e699c085..f767767eaa 100644 --- a/linux-user/mips/target_elf.h +++ b/linux-user/mips/target_elf.h @@ -10,5 +10,14 @@ #define HAVE_ELF_HWCAP 1 #define HAVE_ELF_BASE_PLATFORM 1 +#define HAVE_ELF_CORE_DUMP 1 + +typedef abi_ulong target_elf_greg_t; + +/* See linux kernel: arch/mips/include/asm/elf.h. */ +#define ELF_NREG 45 +typedef struct target_elf_gregset_t { + target_elf_greg_t regs[ELF_NREG]; +} target_elf_gregset_t; #endif diff --git a/linux-user/mips64/target_elf.h b/linux-user/mips64/target_elf.h index 24bb7fcd3f..046a165eef 100644 --- a/linux-user/mips64/target_elf.h +++ b/linux-user/mips64/target_elf.h @@ -10,5 +10,18 @@ #define HAVE_ELF_HWCAP 1 #define HAVE_ELF_BASE_PLATFORM 1 +#define HAVE_ELF_CORE_DUMP 1 + +#ifdef TARGET_ABI_MIPSN32 +typedef abi_ullong target_elf_greg_t; +#else +typedef abi_ulong target_elf_greg_t; +#endif + +/* See linux kernel: arch/mips/include/asm/elf.h. */ +#define ELF_NREG 45 +typedef struct target_elf_gregset_t { + target_elf_greg_t regs[ELF_NREG]; +} target_elf_gregset_t; #endif From e06b9c34eaa388e0503426c7831a2db977a472fd Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 29 Jul 2025 06:06:03 -1000 Subject: [PATCH 0163/2396] linux-user: Move elf_core_copy_regs to microblaze/elfload.c Move elf_core_copy_regs to elfload.c. Move HAVE_ELF_CORE_DUMP, ELF_NREGS, target_elf_gregset_t to target_elf.h. For now, duplicate the definitions of target_elf_greg_t and tswapreg. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 21 --------------------- linux-user/microblaze/elfload.c | 17 +++++++++++++++++ linux-user/microblaze/target_elf.h | 9 +++++++++ 3 files changed, 26 insertions(+), 21 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 5a3a5cfc39..96ed6b6515 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -416,27 +416,6 @@ static const VdsoImageInfo *vdso_image_info(uint32_t elf_flags) #define ELF_EXEC_PAGESIZE 4096 -#define HAVE_ELF_CORE_DUMP -#define ELF_NREG 38 -typedef struct target_elf_gregset_t { - target_elf_greg_t regs[ELF_NREG]; -} target_elf_gregset_t; - -/* See linux kernel: arch/mips/kernel/process.c:elf_dump_regs. */ -void elf_core_copy_regs(target_elf_gregset_t *r, const CPUMBState *env) -{ - for (int i = 0; i < 32; i++) { - r->regs[i] = tswapreg(env->regs[i]); - } - - r->regs[32] = tswapreg(env->pc); - r->regs[33] = tswapreg(mb_cpu_read_msr(env)); - r->regs[34] = 0; - r->regs[35] = tswapreg(env->ear); - r->regs[36] = 0; - r->regs[37] = tswapreg(env->esr); -} - #endif /* TARGET_MICROBLAZE */ #ifdef TARGET_OPENRISC diff --git a/linux-user/microblaze/elfload.c b/linux-user/microblaze/elfload.c index b92442dfeb..89250dbd63 100644 --- a/linux-user/microblaze/elfload.c +++ b/linux-user/microblaze/elfload.c @@ -3,9 +3,26 @@ #include "qemu/osdep.h" #include "qemu.h" #include "loader.h" +#include "target_elf.h" const char *get_elf_cpu_model(uint32_t eflags) { return "any"; } + +#define tswapreg(ptr) tswapal(ptr) + +void elf_core_copy_regs(target_elf_gregset_t *r, const CPUMBState *env) +{ + for (int i = 0; i < 32; i++) { + r->regs[i] = tswapreg(env->regs[i]); + } + + r->regs[32] = tswapreg(env->pc); + r->regs[33] = tswapreg(mb_cpu_read_msr(env)); + r->regs[34] = 0; + r->regs[35] = tswapreg(env->ear); + r->regs[36] = 0; + r->regs[37] = tswapreg(env->esr); +} diff --git a/linux-user/microblaze/target_elf.h b/linux-user/microblaze/target_elf.h index bfe2997fd2..cc5cc0477e 100644 --- a/linux-user/microblaze/target_elf.h +++ b/linux-user/microblaze/target_elf.h @@ -8,4 +8,13 @@ #ifndef MICROBLAZE_TARGET_ELF_H #define MICROBLAZE_TARGET_ELF_H +#define HAVE_ELF_CORE_DUMP 1 + +typedef abi_ulong target_elf_greg_t; + +#define ELF_NREG 38 +typedef struct target_elf_gregset_t { + target_elf_greg_t regs[ELF_NREG]; +} target_elf_gregset_t; + #endif From 28c7d60b54d7e6e3afa064ceae7a4786375d8b4b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 29 Jul 2025 06:12:01 -1000 Subject: [PATCH 0164/2396] linux-user: Move elf_core_copy_regs to openrisc/elfload.c Move elf_core_copy_regs to elfload.c. Move HAVE_ELF_CORE_DUMP, ELF_NREGS, target_elf_gregset_t to target_elf.h. For now, duplicate the definitions of target_elf_greg_t and tswapreg. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 16 ---------------- linux-user/openrisc/elfload.c | 12 ++++++++++++ linux-user/openrisc/target_elf.h | 10 ++++++++++ 3 files changed, 22 insertions(+), 16 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 96ed6b6515..8c3ef41312 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -424,24 +424,8 @@ static const VdsoImageInfo *vdso_image_info(uint32_t elf_flags) #define ELF_CLASS ELFCLASS32 #define ELF_DATA ELFDATA2MSB -#define HAVE_ELF_CORE_DUMP #define ELF_EXEC_PAGESIZE 8192 -/* See linux kernel arch/openrisc/include/asm/elf.h. */ -#define ELF_NREG 34 /* gprs and pc, sr */ -typedef struct target_elf_gregset_t { - target_elf_greg_t regs[ELF_NREG]; -} target_elf_gregset_t; - -void elf_core_copy_regs(target_elf_gregset_t *r, const CPUOpenRISCState *env) -{ - for (int i = 0; i < 32; i++) { - r->regs[i] = tswapreg(cpu_get_gpr(env, i)); - } - r->regs[32] = tswapreg(env->pc); - r->regs[33] = tswapreg(cpu_get_sr(env)); -} - #endif /* TARGET_OPENRISC */ #ifdef TARGET_SH4 diff --git a/linux-user/openrisc/elfload.c b/linux-user/openrisc/elfload.c index b92442dfeb..bb5ad96711 100644 --- a/linux-user/openrisc/elfload.c +++ b/linux-user/openrisc/elfload.c @@ -3,9 +3,21 @@ #include "qemu/osdep.h" #include "qemu.h" #include "loader.h" +#include "target_elf.h" const char *get_elf_cpu_model(uint32_t eflags) { return "any"; } + +#define tswapreg(ptr) tswapal(ptr) + +void elf_core_copy_regs(target_elf_gregset_t *r, const CPUOpenRISCState *env) +{ + for (int i = 0; i < 32; i++) { + r->regs[i] = tswapreg(cpu_get_gpr(env, i)); + } + r->regs[32] = tswapreg(env->pc); + r->regs[33] = tswapreg(cpu_get_sr(env)); +} diff --git a/linux-user/openrisc/target_elf.h b/linux-user/openrisc/target_elf.h index b34f2ff672..e97bdc11ed 100644 --- a/linux-user/openrisc/target_elf.h +++ b/linux-user/openrisc/target_elf.h @@ -8,4 +8,14 @@ #ifndef OPENRISC_TARGET_ELF_H #define OPENRISC_TARGET_ELF_H +#define HAVE_ELF_CORE_DUMP 1 + +typedef abi_ulong target_elf_greg_t; + +/* See linux kernel arch/openrisc/include/asm/elf.h. */ +#define ELF_NREG 34 /* gprs and pc, sr */ +typedef struct target_elf_gregset_t { + target_elf_greg_t regs[ELF_NREG]; +} target_elf_gregset_t; + #endif From a4ea8c30e7c8ae49e2e50e2c410581ad53c3e1fb Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 29 Jul 2025 06:12:27 -1000 Subject: [PATCH 0165/2396] linux-user: Move elf_core_copy_regs to sh4/elfload.c Move elf_core_copy_regs to elfload.c. Move HAVE_ELF_CORE_DUMP, ELF_NREGS, target_elf_gregset_t to target_elf.h. For now, duplicate the definitions of target_elf_greg_t and tswapreg. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 33 --------------------------------- linux-user/sh4/elfload.c | 29 +++++++++++++++++++++++++++++ linux-user/sh4/target_elf.h | 9 +++++++++ 3 files changed, 38 insertions(+), 33 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 8c3ef41312..69532faddb 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -433,39 +433,6 @@ static const VdsoImageInfo *vdso_image_info(uint32_t elf_flags) #define ELF_CLASS ELFCLASS32 #define ELF_ARCH EM_SH -/* See linux kernel: arch/sh/include/asm/elf.h. */ -#define ELF_NREG 23 -typedef struct target_elf_gregset_t { - target_elf_greg_t regs[ELF_NREG]; -} target_elf_gregset_t; - -/* See linux kernel: arch/sh/include/asm/ptrace.h. */ -enum { - TARGET_REG_PC = 16, - TARGET_REG_PR = 17, - TARGET_REG_SR = 18, - TARGET_REG_GBR = 19, - TARGET_REG_MACH = 20, - TARGET_REG_MACL = 21, - TARGET_REG_SYSCALL = 22 -}; - -void elf_core_copy_regs(target_elf_gregset_t *r, const CPUSH4State *env) -{ - for (int i = 0; i < 16; i++) { - r->regs[i] = tswapreg(env->gregs[i]); - } - - r->regs[TARGET_REG_PC] = tswapreg(env->pc); - r->regs[TARGET_REG_PR] = tswapreg(env->pr); - r->regs[TARGET_REG_SR] = tswapreg(env->sr); - r->regs[TARGET_REG_GBR] = tswapreg(env->gbr); - r->regs[TARGET_REG_MACH] = tswapreg(env->mach); - r->regs[TARGET_REG_MACL] = tswapreg(env->macl); - r->regs[TARGET_REG_SYSCALL] = 0; /* FIXME */ -} - -#define HAVE_ELF_CORE_DUMP #define ELF_EXEC_PAGESIZE 4096 #endif diff --git a/linux-user/sh4/elfload.c b/linux-user/sh4/elfload.c index 99ad4f6334..71cae9703e 100644 --- a/linux-user/sh4/elfload.c +++ b/linux-user/sh4/elfload.c @@ -3,6 +3,7 @@ #include "qemu/osdep.h" #include "qemu.h" #include "loader.h" +#include "target_elf.h" const char *get_elf_cpu_model(uint32_t eflags) @@ -36,3 +37,31 @@ abi_ulong get_elf_hwcap(CPUState *cs) return hwcap; } + +#define tswapreg(ptr) tswapal(ptr) + +/* See linux kernel: arch/sh/include/asm/ptrace.h. */ +enum { + TARGET_REG_PC = 16, + TARGET_REG_PR = 17, + TARGET_REG_SR = 18, + TARGET_REG_GBR = 19, + TARGET_REG_MACH = 20, + TARGET_REG_MACL = 21, + TARGET_REG_SYSCALL = 22 +}; + +void elf_core_copy_regs(target_elf_gregset_t *r, const CPUSH4State *env) +{ + for (int i = 0; i < 16; i++) { + r->regs[i] = tswapreg(env->gregs[i]); + } + + r->regs[TARGET_REG_PC] = tswapreg(env->pc); + r->regs[TARGET_REG_PR] = tswapreg(env->pr); + r->regs[TARGET_REG_SR] = tswapreg(env->sr); + r->regs[TARGET_REG_GBR] = tswapreg(env->gbr); + r->regs[TARGET_REG_MACH] = tswapreg(env->mach); + r->regs[TARGET_REG_MACL] = tswapreg(env->macl); + r->regs[TARGET_REG_SYSCALL] = 0; /* FIXME */ +} diff --git a/linux-user/sh4/target_elf.h b/linux-user/sh4/target_elf.h index badd0f5371..f7443ddbac 100644 --- a/linux-user/sh4/target_elf.h +++ b/linux-user/sh4/target_elf.h @@ -9,5 +9,14 @@ #define SH4_TARGET_ELF_H #define HAVE_ELF_HWCAP 1 +#define HAVE_ELF_CORE_DUMP 1 + +typedef abi_ulong target_elf_greg_t; + +/* See linux kernel: arch/sh/include/asm/elf.h. */ +#define ELF_NREG 23 +typedef struct target_elf_gregset_t { + target_elf_greg_t regs[ELF_NREG]; +} target_elf_gregset_t; #endif From bcaebf6e5bac13352a17eb4949464787c34829f9 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 29 Jul 2025 06:14:26 -1000 Subject: [PATCH 0166/2396] linux-user: Move elf_core_copy_regs to m68k/elfload.c Move elf_core_copy_regs to elfload.c. Move HAVE_ELF_CORE_DUMP, ELF_NREGS, target_elf_gregset_t to target_elf.h. For now, duplicate the definitions of target_elf_greg_t and tswapreg. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 31 ------------------------------- linux-user/m68k/elfload.c | 27 +++++++++++++++++++++++++++ linux-user/m68k/target_elf.h | 10 ++++++++++ 3 files changed, 37 insertions(+), 31 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 69532faddb..e92c424faf 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -442,37 +442,6 @@ static const VdsoImageInfo *vdso_image_info(uint32_t elf_flags) #define ELF_CLASS ELFCLASS32 #define ELF_ARCH EM_68K -/* See linux kernel: arch/m68k/include/asm/elf.h. */ -#define ELF_NREG 20 -typedef struct target_elf_gregset_t { - target_elf_greg_t regs[ELF_NREG]; -} target_elf_gregset_t; - -void elf_core_copy_regs(target_elf_gregset_t *r, const CPUM68KState *env) -{ - r->regs[0] = tswapreg(env->dregs[1]); - r->regs[1] = tswapreg(env->dregs[2]); - r->regs[2] = tswapreg(env->dregs[3]); - r->regs[3] = tswapreg(env->dregs[4]); - r->regs[4] = tswapreg(env->dregs[5]); - r->regs[5] = tswapreg(env->dregs[6]); - r->regs[6] = tswapreg(env->dregs[7]); - r->regs[7] = tswapreg(env->aregs[0]); - r->regs[8] = tswapreg(env->aregs[1]); - r->regs[9] = tswapreg(env->aregs[2]); - r->regs[10] = tswapreg(env->aregs[3]); - r->regs[11] = tswapreg(env->aregs[4]); - r->regs[12] = tswapreg(env->aregs[5]); - r->regs[13] = tswapreg(env->aregs[6]); - r->regs[14] = tswapreg(env->dregs[0]); - r->regs[15] = tswapreg(env->aregs[7]); - r->regs[16] = tswapreg(env->dregs[0]); /* FIXME: orig_d0 */ - r->regs[17] = tswapreg(env->sr); - r->regs[18] = tswapreg(env->pc); - r->regs[19] = 0; /* FIXME: regs->format | regs->vector */ -} - -#define HAVE_ELF_CORE_DUMP #define ELF_EXEC_PAGESIZE 8192 #endif diff --git a/linux-user/m68k/elfload.c b/linux-user/m68k/elfload.c index 561ac5b3b3..2970ff7dec 100644 --- a/linux-user/m68k/elfload.c +++ b/linux-user/m68k/elfload.c @@ -4,6 +4,7 @@ #include "qemu.h" #include "loader.h" #include "elf.h" +#include "target_elf.h" const char *get_elf_cpu_model(uint32_t eflags) @@ -16,3 +17,29 @@ const char *get_elf_cpu_model(uint32_t eflags) /* Coldfire */ return "any"; } + +#define tswapreg(ptr) tswapal(ptr) + +void elf_core_copy_regs(target_elf_gregset_t *r, const CPUM68KState *env) +{ + r->regs[0] = tswapreg(env->dregs[1]); + r->regs[1] = tswapreg(env->dregs[2]); + r->regs[2] = tswapreg(env->dregs[3]); + r->regs[3] = tswapreg(env->dregs[4]); + r->regs[4] = tswapreg(env->dregs[5]); + r->regs[5] = tswapreg(env->dregs[6]); + r->regs[6] = tswapreg(env->dregs[7]); + r->regs[7] = tswapreg(env->aregs[0]); + r->regs[8] = tswapreg(env->aregs[1]); + r->regs[9] = tswapreg(env->aregs[2]); + r->regs[10] = tswapreg(env->aregs[3]); + r->regs[11] = tswapreg(env->aregs[4]); + r->regs[12] = tswapreg(env->aregs[5]); + r->regs[13] = tswapreg(env->aregs[6]); + r->regs[14] = tswapreg(env->dregs[0]); + r->regs[15] = tswapreg(env->aregs[7]); + r->regs[16] = tswapreg(env->dregs[0]); /* FIXME: orig_d0 */ + r->regs[17] = tswapreg(env->sr); + r->regs[18] = tswapreg(env->pc); + r->regs[19] = 0; /* FIXME: regs->format | regs->vector */ +} diff --git a/linux-user/m68k/target_elf.h b/linux-user/m68k/target_elf.h index 62ff9d38d4..cd6908ab57 100644 --- a/linux-user/m68k/target_elf.h +++ b/linux-user/m68k/target_elf.h @@ -8,4 +8,14 @@ #ifndef M68K_TARGET_ELF_H #define M68K_TARGET_ELF_H +#define HAVE_ELF_CORE_DUMP 1 + +typedef abi_ulong target_elf_greg_t; + +/* See linux kernel: arch/m68k/include/asm/elf.h. */ +#define ELF_NREG 20 +typedef struct target_elf_gregset_t { + target_elf_greg_t regs[ELF_NREG]; +} target_elf_gregset_t; + #endif From 59b51b4e7cacca78d0f60e3817150d2c29423d54 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 29 Jul 2025 06:16:07 -1000 Subject: [PATCH 0167/2396] linux-user: Move elf_core_copy_regs to s390x/elfload.c Move elf_core_copy_regs to elfload.c. Move HAVE_ELF_CORE_DUMP, ELF_NREGS, target_elf_gregset_t to target_elf.h. For now, duplicate the definitions of target_elf_greg_t and tswapreg. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 32 -------------------------------- linux-user/s390x/elfload.c | 28 ++++++++++++++++++++++++++++ linux-user/s390x/target_elf.h | 9 +++++++++ 3 files changed, 37 insertions(+), 32 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index e92c424faf..7c783b74d4 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -461,38 +461,6 @@ static const VdsoImageInfo *vdso_image_info(uint32_t elf_flags) #define ELF_DATA ELFDATA2MSB #define ELF_ARCH EM_S390 -/* See linux kernel: arch/s390/include/uapi/asm/ptrace.h (s390_regs). */ -#define ELF_NREG 27 -typedef struct target_elf_gregset_t { - target_elf_greg_t regs[ELF_NREG]; -} target_elf_gregset_t; - -enum { - TARGET_REG_PSWM = 0, - TARGET_REG_PSWA = 1, - TARGET_REG_GPRS = 2, - TARGET_REG_ARS = 18, - TARGET_REG_ORIG_R2 = 26, -}; - -void elf_core_copy_regs(target_elf_gregset_t *r, const CPUS390XState *env) -{ - int i; - uint32_t *aregs; - - r->regs[TARGET_REG_PSWM] = tswapreg(env->psw.mask); - r->regs[TARGET_REG_PSWA] = tswapreg(env->psw.addr); - for (i = 0; i < 16; i++) { - r->regs[TARGET_REG_GPRS + i] = tswapreg(env->regs[i]); - } - aregs = (uint32_t *)&(r->regs[TARGET_REG_ARS]); - for (i = 0; i < 16; i++) { - aregs[i] = tswap32(env->aregs[i]); - } - r->regs[TARGET_REG_ORIG_R2] = 0; -} - -#define HAVE_ELF_CORE_DUMP #define ELF_EXEC_PAGESIZE 4096 #define VDSO_HEADER "vdso.c.inc" diff --git a/linux-user/s390x/elfload.c b/linux-user/s390x/elfload.c index 79ceaba51d..4113273b72 100644 --- a/linux-user/s390x/elfload.c +++ b/linux-user/s390x/elfload.c @@ -4,6 +4,7 @@ #include "qemu.h" #include "loader.h" #include "elf.h" +#include "target_elf.h" const char *get_elf_cpu_model(uint32_t eflags) @@ -66,3 +67,30 @@ const char *elf_hwcap_str(uint32_t bit) return bit < ARRAY_SIZE(hwcap_str) ? hwcap_str[bit] : NULL; } + +#define tswapreg(ptr) tswapal(ptr) + +enum { + TARGET_REG_PSWM = 0, + TARGET_REG_PSWA = 1, + TARGET_REG_GPRS = 2, + TARGET_REG_ARS = 18, + TARGET_REG_ORIG_R2 = 26, +}; + +void elf_core_copy_regs(target_elf_gregset_t *r, const CPUS390XState *env) +{ + int i; + uint32_t *aregs; + + r->regs[TARGET_REG_PSWM] = tswapreg(env->psw.mask); + r->regs[TARGET_REG_PSWA] = tswapreg(env->psw.addr); + for (i = 0; i < 16; i++) { + r->regs[TARGET_REG_GPRS + i] = tswapreg(env->regs[i]); + } + aregs = (uint32_t *)&(r->regs[TARGET_REG_ARS]); + for (i = 0; i < 16; i++) { + aregs[i] = tswap32(env->aregs[i]); + } + r->regs[TARGET_REG_ORIG_R2] = 0; +} diff --git a/linux-user/s390x/target_elf.h b/linux-user/s390x/target_elf.h index cebace949a..b7d863ee66 100644 --- a/linux-user/s390x/target_elf.h +++ b/linux-user/s390x/target_elf.h @@ -9,5 +9,14 @@ #define S390X_TARGET_ELF_H #define HAVE_ELF_HWCAP 1 +#define HAVE_ELF_CORE_DUMP 1 + +typedef abi_ulong target_elf_greg_t; + +/* See linux kernel: arch/s390/include/uapi/asm/ptrace.h (s390_regs). */ +#define ELF_NREG 27 +typedef struct target_elf_gregset_t { + target_elf_greg_t regs[ELF_NREG]; +} target_elf_gregset_t; #endif From 952916bb8fae405fd9f25be6c3ad0f6f8525ddbf Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 29 Jul 2025 06:17:52 -1000 Subject: [PATCH 0168/2396] linux-user: Move elf_core_copy_regs to xtensa/elfload.c Move elf_core_copy_regs to elfload.c. Move HAVE_ELF_CORE_DUMP, ELF_NREGS, target_elf_gregset_t to target_elf.h. For now, duplicate the definitions of target_elf_greg_t and tswapreg. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 39 ---------------------------------- linux-user/xtensa/elfload.c | 35 ++++++++++++++++++++++++++++++ linux-user/xtensa/target_elf.h | 10 +++++++++ 3 files changed, 45 insertions(+), 39 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 7c783b74d4..5cdbdc20d9 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -531,45 +531,6 @@ static bool init_guest_commpage(void) #define ELF_CLASS ELFCLASS32 #define ELF_ARCH EM_XTENSA -/* See linux kernel: arch/xtensa/include/asm/elf.h. */ -#define ELF_NREG 128 -typedef struct target_elf_gregset_t { - target_elf_greg_t regs[ELF_NREG]; -} target_elf_gregset_t; - -enum { - TARGET_REG_PC, - TARGET_REG_PS, - TARGET_REG_LBEG, - TARGET_REG_LEND, - TARGET_REG_LCOUNT, - TARGET_REG_SAR, - TARGET_REG_WINDOWSTART, - TARGET_REG_WINDOWBASE, - TARGET_REG_THREADPTR, - TARGET_REG_AR0 = 64, -}; - -void elf_core_copy_regs(target_elf_gregset_t *r, const CPUXtensaState *env) -{ - unsigned i; - - r->regs[TARGET_REG_PC] = tswapreg(env->pc); - r->regs[TARGET_REG_PS] = tswapreg(env->sregs[PS] & ~PS_EXCM); - r->regs[TARGET_REG_LBEG] = tswapreg(env->sregs[LBEG]); - r->regs[TARGET_REG_LEND] = tswapreg(env->sregs[LEND]); - r->regs[TARGET_REG_LCOUNT] = tswapreg(env->sregs[LCOUNT]); - r->regs[TARGET_REG_SAR] = tswapreg(env->sregs[SAR]); - r->regs[TARGET_REG_WINDOWSTART] = tswapreg(env->sregs[WINDOW_START]); - r->regs[TARGET_REG_WINDOWBASE] = tswapreg(env->sregs[WINDOW_BASE]); - r->regs[TARGET_REG_THREADPTR] = tswapreg(env->uregs[THREADPTR]); - xtensa_sync_phys_from_window((CPUXtensaState *)env); - for (i = 0; i < env->config->nareg; ++i) { - r->regs[TARGET_REG_AR0 + i] = tswapreg(env->phys_regs[i]); - } -} - -#define HAVE_ELF_CORE_DUMP #define ELF_EXEC_PAGESIZE 4096 #endif /* TARGET_XTENSA */ diff --git a/linux-user/xtensa/elfload.c b/linux-user/xtensa/elfload.c index e35ba69a10..49e709a094 100644 --- a/linux-user/xtensa/elfload.c +++ b/linux-user/xtensa/elfload.c @@ -3,9 +3,44 @@ #include "qemu/osdep.h" #include "qemu.h" #include "loader.h" +#include "target_elf.h" const char *get_elf_cpu_model(uint32_t eflags) { return XTENSA_DEFAULT_CPU_MODEL; } + +#define tswapreg(ptr) tswapal(ptr) + +enum { + TARGET_REG_PC, + TARGET_REG_PS, + TARGET_REG_LBEG, + TARGET_REG_LEND, + TARGET_REG_LCOUNT, + TARGET_REG_SAR, + TARGET_REG_WINDOWSTART, + TARGET_REG_WINDOWBASE, + TARGET_REG_THREADPTR, + TARGET_REG_AR0 = 64, +}; + +void elf_core_copy_regs(target_elf_gregset_t *r, const CPUXtensaState *env) +{ + unsigned i; + + r->regs[TARGET_REG_PC] = tswapreg(env->pc); + r->regs[TARGET_REG_PS] = tswapreg(env->sregs[PS] & ~PS_EXCM); + r->regs[TARGET_REG_LBEG] = tswapreg(env->sregs[LBEG]); + r->regs[TARGET_REG_LEND] = tswapreg(env->sregs[LEND]); + r->regs[TARGET_REG_LCOUNT] = tswapreg(env->sregs[LCOUNT]); + r->regs[TARGET_REG_SAR] = tswapreg(env->sregs[SAR]); + r->regs[TARGET_REG_WINDOWSTART] = tswapreg(env->sregs[WINDOW_START]); + r->regs[TARGET_REG_WINDOWBASE] = tswapreg(env->sregs[WINDOW_BASE]); + r->regs[TARGET_REG_THREADPTR] = tswapreg(env->uregs[THREADPTR]); + xtensa_sync_phys_from_window((CPUXtensaState *)env); + for (i = 0; i < env->config->nareg; ++i) { + r->regs[TARGET_REG_AR0 + i] = tswapreg(env->phys_regs[i]); + } +} diff --git a/linux-user/xtensa/target_elf.h b/linux-user/xtensa/target_elf.h index 2c55c22e14..43e241aac1 100644 --- a/linux-user/xtensa/target_elf.h +++ b/linux-user/xtensa/target_elf.h @@ -8,4 +8,14 @@ #ifndef XTENSA_TARGET_ELF_H #define XTENSA_TARGET_ELF_H +#define HAVE_ELF_CORE_DUMP 1 + +typedef abi_ulong target_elf_greg_t; + +/* See linux kernel: arch/xtensa/include/asm/elf.h. */ +#define ELF_NREG 128 +typedef struct target_elf_gregset_t { + target_elf_greg_t regs[ELF_NREG]; +} target_elf_gregset_t; + #endif From 4540a4e6044870dc98bdaacb1593012aef6f5df9 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Aug 2025 10:53:13 +1000 Subject: [PATCH 0169/2396] linux-user: Remove target_elf_greg_t, tswapreg from elfload.c These are no longer used within the generic file. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 5cdbdc20d9..07d83c674d 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -130,14 +130,6 @@ int info_is_fdpic(struct image_info *info) #define ELF_DATA ELFDATA2LSB #endif -#ifdef TARGET_ABI_MIPSN32 -typedef abi_ullong target_elf_greg_t; -#define tswapreg(ptr) tswap64(ptr) -#else -typedef abi_ulong target_elf_greg_t; -#define tswapreg(ptr) tswapal(ptr) -#endif - #ifdef USE_UID16 typedef abi_ushort target_uid_t; typedef abi_ushort target_gid_t; From c47407ef2f4e6b2965b726618cd600aa24149f13 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Aug 2025 14:19:04 +1000 Subject: [PATCH 0170/2396] linux-user/i386: Create target_ptrace.h Remove the target_pt_regs structure from target_syscall.h. Add target_user_regs_struct to target_ptrace.h, which is what is actually used by ELF_CORE_COPY_REGS; the layout of the two structure definitions is identical. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/i386/target_ptrace.h | 32 ++++++++++++++++++++++++++++++++ linux-user/i386/target_syscall.h | 18 ------------------ 2 files changed, 32 insertions(+), 18 deletions(-) create mode 100644 linux-user/i386/target_ptrace.h diff --git a/linux-user/i386/target_ptrace.h b/linux-user/i386/target_ptrace.h new file mode 100644 index 0000000000..bc57926f25 --- /dev/null +++ b/linux-user/i386/target_ptrace.h @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef I386_TARGET_PTRACE_H +#define I386_TARGET_PTRACE_H + +/* + * Note that arch/x86/include/uapi/asm/ptrace.h (struct pt_regs) and + * arch/x86/include/asm/user_32.h (struct user_regs_struct) have the + * same layout, though the exact types differ (int vs long vs unsigned). + * Define user_regs_struct because that's what's actually used. + */ +struct target_user_regs_struct { + abi_ulong bx; + abi_ulong cx; + abi_ulong dx; + abi_ulong si; + abi_ulong di; + abi_ulong bp; + abi_ulong ax; + abi_ulong ds; + abi_ulong es; + abi_ulong fs; + abi_ulong gs; + abi_ulong orig_ax; + abi_ulong ip; + abi_ulong cs; + abi_ulong flags; + abi_ulong sp; + abi_ulong ss; +}; + +#endif /* I386_TARGET_PTRACE_H */ diff --git a/linux-user/i386/target_syscall.h b/linux-user/i386/target_syscall.h index aaade06b13..c214a909a6 100644 --- a/linux-user/i386/target_syscall.h +++ b/linux-user/i386/target_syscall.h @@ -5,24 +5,6 @@ #define __USER_CS (0x23) #define __USER_DS (0x2B) -struct target_pt_regs { - long ebx; - long ecx; - long edx; - long esi; - long edi; - long ebp; - long eax; - int xds; - int xes; - long orig_eax; - long eip; - int xcs; - long eflags; - long esp; - int xss; -}; - /* ioctls */ #define TARGET_LDT_ENTRIES 8192 From ff15e62d3f472890b6c93262f85447bfb9eec642 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Aug 2025 11:09:03 +1000 Subject: [PATCH 0171/2396] linux-user/i386: Expand target_elf_gregset_t The comment re ELF_NREG is incorrect or out-of-date. Make use of the fact that target_elf_gregset_t is a proper structure by using target_user_regs_struct. Drop target_elf_greg_t and tswapreg. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/i386/elfload.c | 36 +++++++++++++++++------------------- linux-user/i386/target_elf.h | 14 +++++--------- 2 files changed, 22 insertions(+), 28 deletions(-) diff --git a/linux-user/i386/elfload.c b/linux-user/i386/elfload.c index 279aeb8116..26b12001a3 100644 --- a/linux-user/i386/elfload.c +++ b/linux-user/i386/elfload.c @@ -25,25 +25,23 @@ const char *get_elf_platform(CPUState *cs) return elf_platform[family - 3]; } -#define tswapreg(ptr) tswapal(ptr) - void elf_core_copy_regs(target_elf_gregset_t *r, const CPUX86State *env) { - r->regs[0] = tswapreg(env->regs[R_EBX]); - r->regs[1] = tswapreg(env->regs[R_ECX]); - r->regs[2] = tswapreg(env->regs[R_EDX]); - r->regs[3] = tswapreg(env->regs[R_ESI]); - r->regs[4] = tswapreg(env->regs[R_EDI]); - r->regs[5] = tswapreg(env->regs[R_EBP]); - r->regs[6] = tswapreg(env->regs[R_EAX]); - r->regs[7] = tswapreg(env->segs[R_DS].selector & 0xffff); - r->regs[8] = tswapreg(env->segs[R_ES].selector & 0xffff); - r->regs[9] = tswapreg(env->segs[R_FS].selector & 0xffff); - r->regs[10] = tswapreg(env->segs[R_GS].selector & 0xffff); - r->regs[11] = tswapreg(get_task_state(env_cpu_const(env))->orig_ax); - r->regs[12] = tswapreg(env->eip); - r->regs[13] = tswapreg(env->segs[R_CS].selector & 0xffff); - r->regs[14] = tswapreg(env->eflags); - r->regs[15] = tswapreg(env->regs[R_ESP]); - r->regs[16] = tswapreg(env->segs[R_SS].selector & 0xffff); + r->pt.bx = tswapal(env->regs[R_EBX]); + r->pt.cx = tswapal(env->regs[R_ECX]); + r->pt.dx = tswapal(env->regs[R_EDX]); + r->pt.si = tswapal(env->regs[R_ESI]); + r->pt.di = tswapal(env->regs[R_EDI]); + r->pt.bp = tswapal(env->regs[R_EBP]); + r->pt.ax = tswapal(env->regs[R_EAX]); + r->pt.ds = tswapal(env->segs[R_DS].selector & 0xffff); + r->pt.es = tswapal(env->segs[R_ES].selector & 0xffff); + r->pt.fs = tswapal(env->segs[R_FS].selector & 0xffff); + r->pt.gs = tswapal(env->segs[R_GS].selector & 0xffff); + r->pt.orig_ax = tswapal(get_task_state(env_cpu_const(env))->orig_ax); + r->pt.ip = tswapal(env->eip); + r->pt.cs = tswapal(env->segs[R_CS].selector & 0xffff); + r->pt.flags = tswapal(env->eflags); + r->pt.sp = tswapal(env->regs[R_ESP]); + r->pt.ss = tswapal(env->segs[R_SS].selector & 0xffff); } diff --git a/linux-user/i386/target_elf.h b/linux-user/i386/target_elf.h index eb286868e1..f89ac0b611 100644 --- a/linux-user/i386/target_elf.h +++ b/linux-user/i386/target_elf.h @@ -8,22 +8,18 @@ #ifndef I386_TARGET_ELF_H #define I386_TARGET_ELF_H +#include "target_ptrace.h" + #define HAVE_ELF_HWCAP 1 #define HAVE_ELF_PLATFORM 1 #define HAVE_ELF_CORE_DUMP 1 /* - * Note that ELF_NREG should be 19 as there should be place for - * TRAPNO and ERR "registers" as well but linux doesn't dump those. - * - * See linux kernel: arch/x86/include/asm/elf.h + * See linux kernel: arch/x86/include/asm/elf.h, where elf_gregset_t + * is mapped to struct user_regs_struct via sizeof. */ -#define ELF_NREG 17 - -typedef abi_ulong target_elf_greg_t; - typedef struct target_elf_gregset_t { - target_elf_greg_t regs[ELF_NREG]; + struct target_user_regs_struct pt; } target_elf_gregset_t; #endif From 399313bb6d94e5f3ce4ce533f9a8c5fe504f6cda Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Aug 2025 14:41:40 +1000 Subject: [PATCH 0172/2396] linux-user/x86_64: Create target_ptrace.h Remove the target_pt_regs structure from target_syscall.h. Add target_user_regs_struct to target_ptrace.h, which matches what is actually used on x86_64. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/x86_64/target_ptrace.h | 40 ++++++++++++++++++++++++++++++ linux-user/x86_64/target_syscall.h | 28 --------------------- 2 files changed, 40 insertions(+), 28 deletions(-) create mode 100644 linux-user/x86_64/target_ptrace.h diff --git a/linux-user/x86_64/target_ptrace.h b/linux-user/x86_64/target_ptrace.h new file mode 100644 index 0000000000..33527127cb --- /dev/null +++ b/linux-user/x86_64/target_ptrace.h @@ -0,0 +1,40 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef X86_64_TARGET_PTRACE_H +#define X86_64_TARGET_PTRACE_H + +/* + * The struct pt_regs in arch/x86/include/uapi/asm/ptrace.h has missing + * register values and is not used. See arch/x86/include/asm/user_64.h. + */ +struct target_user_regs_struct { + abi_ulong r15; + abi_ulong r14; + abi_ulong r13; + abi_ulong r12; + abi_ulong bp; + abi_ulong bx; + abi_ulong r11; + abi_ulong r10; + abi_ulong r9; + abi_ulong r8; + abi_ulong ax; + abi_ulong cx; + abi_ulong dx; + abi_ulong si; + abi_ulong di; + abi_ulong orig_ax; + abi_ulong ip; + abi_ulong cs; + abi_ulong flags; + abi_ulong sp; + abi_ulong ss; + abi_ulong fs_base; + abi_ulong gs_base; + abi_ulong ds; + abi_ulong es; + abi_ulong fs; + abi_ulong gs; +}; + +#endif /* X86_64_TARGET_PTRACE_H */ diff --git a/linux-user/x86_64/target_syscall.h b/linux-user/x86_64/target_syscall.h index fb558345d3..68f55f8e7b 100644 --- a/linux-user/x86_64/target_syscall.h +++ b/linux-user/x86_64/target_syscall.h @@ -4,34 +4,6 @@ #define __USER_CS (0x33) #define __USER_DS (0x2B) -struct target_pt_regs { - abi_ulong r15; - abi_ulong r14; - abi_ulong r13; - abi_ulong r12; - abi_ulong rbp; - abi_ulong rbx; -/* arguments: non interrupts/non tracing syscalls only save up to here */ - abi_ulong r11; - abi_ulong r10; - abi_ulong r9; - abi_ulong r8; - abi_ulong rax; - abi_ulong rcx; - abi_ulong rdx; - abi_ulong rsi; - abi_ulong rdi; - abi_ulong orig_rax; -/* end of arguments */ -/* cpu exception frame or undefined */ - abi_ulong rip; - abi_ulong cs; - abi_ulong eflags; - abi_ulong rsp; - abi_ulong ss; -/* top of stack page */ -}; - /* Maximum number of LDT entries supported. */ #define TARGET_LDT_ENTRIES 8192 /* The size of each LDT entry. */ From 9c49798e18c00eb07bf6832aa50a1a889a145ec5 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Aug 2025 11:17:02 +1000 Subject: [PATCH 0173/2396] linux-user/x86_64: Expand target_elf_gregset_t The comment re ELF_NREG is incorrect or out-of-date. Make use of the fact that target_elf_gregset_t is a proper structure by using target_user_regs_struct. Drop target_elf_greg_t and tswapreg. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/x86_64/elfload.c | 56 ++++++++++++++++------------------ linux-user/x86_64/target_elf.h | 14 +++------ 2 files changed, 32 insertions(+), 38 deletions(-) diff --git a/linux-user/x86_64/elfload.c b/linux-user/x86_64/elfload.c index 76cf5c1509..18d632ec34 100644 --- a/linux-user/x86_64/elfload.c +++ b/linux-user/x86_64/elfload.c @@ -21,35 +21,33 @@ const char *get_elf_platform(CPUState *cs) return "x86_64"; } -#define tswapreg(ptr) tswapal(ptr) - void elf_core_copy_regs(target_elf_gregset_t *r, const CPUX86State *env) { - r->regs[0] = tswapreg(env->regs[15]); - r->regs[1] = tswapreg(env->regs[14]); - r->regs[2] = tswapreg(env->regs[13]); - r->regs[3] = tswapreg(env->regs[12]); - r->regs[4] = tswapreg(env->regs[R_EBP]); - r->regs[5] = tswapreg(env->regs[R_EBX]); - r->regs[6] = tswapreg(env->regs[11]); - r->regs[7] = tswapreg(env->regs[10]); - r->regs[8] = tswapreg(env->regs[9]); - r->regs[9] = tswapreg(env->regs[8]); - r->regs[10] = tswapreg(env->regs[R_EAX]); - r->regs[11] = tswapreg(env->regs[R_ECX]); - r->regs[12] = tswapreg(env->regs[R_EDX]); - r->regs[13] = tswapreg(env->regs[R_ESI]); - r->regs[14] = tswapreg(env->regs[R_EDI]); - r->regs[15] = tswapreg(get_task_state(env_cpu_const(env))->orig_ax); - r->regs[16] = tswapreg(env->eip); - r->regs[17] = tswapreg(env->segs[R_CS].selector & 0xffff); - r->regs[18] = tswapreg(env->eflags); - r->regs[19] = tswapreg(env->regs[R_ESP]); - r->regs[20] = tswapreg(env->segs[R_SS].selector & 0xffff); - r->regs[21] = tswapreg(env->segs[R_FS].selector & 0xffff); - r->regs[22] = tswapreg(env->segs[R_GS].selector & 0xffff); - r->regs[23] = tswapreg(env->segs[R_DS].selector & 0xffff); - r->regs[24] = tswapreg(env->segs[R_ES].selector & 0xffff); - r->regs[25] = tswapreg(env->segs[R_FS].selector & 0xffff); - r->regs[26] = tswapreg(env->segs[R_GS].selector & 0xffff); + r->pt.r15 = tswapal(env->regs[15]); + r->pt.r14 = tswapal(env->regs[14]); + r->pt.r13 = tswapal(env->regs[13]); + r->pt.r12 = tswapal(env->regs[12]); + r->pt.bp = tswapal(env->regs[R_EBP]); + r->pt.bx = tswapal(env->regs[R_EBX]); + r->pt.r11 = tswapal(env->regs[11]); + r->pt.r10 = tswapal(env->regs[10]); + r->pt.r9 = tswapal(env->regs[9]); + r->pt.r8 = tswapal(env->regs[8]); + r->pt.ax = tswapal(env->regs[R_EAX]); + r->pt.cx = tswapal(env->regs[R_ECX]); + r->pt.dx = tswapal(env->regs[R_EDX]); + r->pt.si = tswapal(env->regs[R_ESI]); + r->pt.di = tswapal(env->regs[R_EDI]); + r->pt.orig_ax = tswapal(get_task_state(env_cpu_const(env))->orig_ax); + r->pt.ip = tswapal(env->eip); + r->pt.cs = tswapal(env->segs[R_CS].selector & 0xffff); + r->pt.flags = tswapal(env->eflags); + r->pt.sp = tswapal(env->regs[R_ESP]); + r->pt.ss = tswapal(env->segs[R_SS].selector & 0xffff); + r->pt.fs_base = tswapal(env->segs[R_FS].selector & 0xffff); + r->pt.gs_base = tswapal(env->segs[R_GS].selector & 0xffff); + r->pt.ds = tswapal(env->segs[R_DS].selector & 0xffff); + r->pt.es = tswapal(env->segs[R_ES].selector & 0xffff); + r->pt.fs = tswapal(env->segs[R_FS].selector & 0xffff); + r->pt.gs = tswapal(env->segs[R_GS].selector & 0xffff); } diff --git a/linux-user/x86_64/target_elf.h b/linux-user/x86_64/target_elf.h index 74a77d94cd..32a9eec431 100644 --- a/linux-user/x86_64/target_elf.h +++ b/linux-user/x86_64/target_elf.h @@ -8,22 +8,18 @@ #ifndef X86_64_TARGET_ELF_H #define X86_64_TARGET_ELF_H +#include "target_ptrace.h" + #define HAVE_ELF_HWCAP 1 #define HAVE_ELF_PLATFORM 1 #define HAVE_ELF_CORE_DUMP 1 /* - * Note that ELF_NREG should be 29 as there should be place for - * TRAPNO and ERR "registers" as well but linux doesn't dump those. - * - * See linux kernel: arch/x86/include/asm/elf.h + * See linux kernel: arch/x86/include/asm/elf.h, where + * elf_gregset_t is mapped to struct user_regs_struct via sizeof. */ -#define ELF_NREG 27 - -typedef abi_ulong target_elf_greg_t; - typedef struct target_elf_gregset_t { - target_elf_greg_t regs[ELF_NREG]; + struct target_user_regs_struct pt; } target_elf_gregset_t; #endif From 31d0ef2c85ca481266d0a8793afdce38ab0b687c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Aug 2025 14:50:12 +1000 Subject: [PATCH 0174/2396] linux-user/x86_64: Fix dump of fs_base, gs_base We were storing the selector, not the base. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/x86_64/elfload.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/linux-user/x86_64/elfload.c b/linux-user/x86_64/elfload.c index 18d632ec34..12de1c54c7 100644 --- a/linux-user/x86_64/elfload.c +++ b/linux-user/x86_64/elfload.c @@ -44,8 +44,8 @@ void elf_core_copy_regs(target_elf_gregset_t *r, const CPUX86State *env) r->pt.flags = tswapal(env->eflags); r->pt.sp = tswapal(env->regs[R_ESP]); r->pt.ss = tswapal(env->segs[R_SS].selector & 0xffff); - r->pt.fs_base = tswapal(env->segs[R_FS].selector & 0xffff); - r->pt.gs_base = tswapal(env->segs[R_GS].selector & 0xffff); + r->pt.fs_base = tswapal(env->segs[R_FS].base); + r->pt.gs_base = tswapal(env->segs[R_GS].base); r->pt.ds = tswapal(env->segs[R_DS].selector & 0xffff); r->pt.es = tswapal(env->segs[R_ES].selector & 0xffff); r->pt.fs = tswapal(env->segs[R_FS].selector & 0xffff); From 7ee71b02fbc21f9de31140f5237c849e36ee30d5 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Aug 2025 14:50:33 +1000 Subject: [PATCH 0175/2396] linux-user/aarch64: Create target_ptrace.h Move the target_pt_regs structure from target_syscall.h, and rename to target_user_pt_regs, to match what's in ptrace.h. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/aarch64/target_ptrace.h | 14 ++++++++++++++ linux-user/aarch64/target_syscall.h | 7 ------- 2 files changed, 14 insertions(+), 7 deletions(-) create mode 100644 linux-user/aarch64/target_ptrace.h diff --git a/linux-user/aarch64/target_ptrace.h b/linux-user/aarch64/target_ptrace.h new file mode 100644 index 0000000000..10681338ba --- /dev/null +++ b/linux-user/aarch64/target_ptrace.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef AARCH64_TARGET_PTRACE_H +#define AARCH64_TARGET_PTRACE_H + +/* See arch/arm64/include/uapi/asm/ptrace.h. */ +struct target_user_pt_regs { + uint64_t regs[31]; + uint64_t sp; + uint64_t pc; + uint64_t pstate; +}; + +#endif /* AARCH64_TARGET_PTRACE_H */ diff --git a/linux-user/aarch64/target_syscall.h b/linux-user/aarch64/target_syscall.h index c055133725..bd05f6c7fe 100644 --- a/linux-user/aarch64/target_syscall.h +++ b/linux-user/aarch64/target_syscall.h @@ -1,13 +1,6 @@ #ifndef AARCH64_TARGET_SYSCALL_H #define AARCH64_TARGET_SYSCALL_H -struct target_pt_regs { - uint64_t regs[31]; - uint64_t sp; - uint64_t pc; - uint64_t pstate; -}; - #if TARGET_BIG_ENDIAN #define UNAME_MACHINE "aarch64_be" #else From ff22166d3f68786e5f129d2fb34a38cfdc0631b3 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Aug 2025 11:24:05 +1000 Subject: [PATCH 0176/2396] linux-user/aarch64: Expand target_elf_gregset_t Make use of the fact that target_elf_gregset_t is a proper structure by using target_user_pt_regs. Drop ELF_NREG, target_elf_greg_t, and tswapreg. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/aarch64/elfload.c | 11 +++++------ linux-user/aarch64/target_elf.h | 11 +++++++---- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/linux-user/aarch64/elfload.c b/linux-user/aarch64/elfload.c index 00550f9fdf..07a0c3f844 100644 --- a/linux-user/aarch64/elfload.c +++ b/linux-user/aarch64/elfload.c @@ -349,13 +349,12 @@ const char *get_elf_platform(CPUState *cs) return TARGET_BIG_ENDIAN ? "aarch64_be" : "aarch64"; } -#define tswapreg(ptr) tswapal(ptr) - void elf_core_copy_regs(target_elf_gregset_t *r, const CPUARMState *env) { - for (int i = 0; i < 32; i++) { - r->regs[i] = tswapreg(env->xregs[i]); + for (int i = 0; i < 31; i++) { + r->pt.regs[i] = tswap64(env->xregs[i]); } - r->regs[32] = tswapreg(env->pc); - r->regs[33] = tswapreg(pstate_read((CPUARMState *)env)); + r->pt.sp = tswap64(env->xregs[31]); + r->pt.pc = tswap64(env->pc); + r->pt.pstate = tswap64(pstate_read((CPUARMState *)env)); } diff --git a/linux-user/aarch64/target_elf.h b/linux-user/aarch64/target_elf.h index b0728a1008..9eb8bb547e 100644 --- a/linux-user/aarch64/target_elf.h +++ b/linux-user/aarch64/target_elf.h @@ -8,16 +8,19 @@ #ifndef AARCH64_TARGET_ELF_H #define AARCH64_TARGET_ELF_H +#include "target_ptrace.h" + #define HAVE_ELF_HWCAP 1 #define HAVE_ELF_HWCAP2 1 #define HAVE_ELF_PLATFORM 1 #define HAVE_ELF_CORE_DUMP 1 -typedef abi_ulong target_elf_greg_t; - -#define ELF_NREG 34 +/* + * See linux kernel: arch/arm64/include/asm/elf.h, where + * elf_gregset_t is mapped to struct user_pt_regs via sizeof. + */ typedef struct target_elf_gregset_t { - target_elf_greg_t regs[ELF_NREG]; + struct target_user_pt_regs pt; } target_elf_gregset_t; #endif From db58f1667e72b3fa4c0a9e45f0b6b683e836295b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Aug 2025 14:56:57 +1000 Subject: [PATCH 0177/2396] linux-user/arm: Create target_ptrace.h Move the target_pt_regs structure from target_syscall.h. Replace the array with proper structure fields. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/arm/target_ptrace.h | 16 ++++++++++++++++ linux-user/arm/target_syscall.h | 8 -------- 2 files changed, 16 insertions(+), 8 deletions(-) create mode 100644 linux-user/arm/target_ptrace.h diff --git a/linux-user/arm/target_ptrace.h b/linux-user/arm/target_ptrace.h new file mode 100644 index 0000000000..1610b8e03c --- /dev/null +++ b/linux-user/arm/target_ptrace.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef ARM_TARGET_PTRACE_H +#define ARM_TARGET_PTRACE_H + +/* + * See arch/arm/include/uapi/asm/ptrace.h. + * Instead of an array and ARM_xx defines, use proper fields. + */ +struct target_pt_regs { + abi_ulong regs[16]; + abi_ulong cpsr; + abi_ulong orig_r0; +}; + +#endif /* ARM_TARGET_PTRACE_H */ diff --git a/linux-user/arm/target_syscall.h b/linux-user/arm/target_syscall.h index 412ad434cf..8c4ddba717 100644 --- a/linux-user/arm/target_syscall.h +++ b/linux-user/arm/target_syscall.h @@ -1,14 +1,6 @@ #ifndef ARM_TARGET_SYSCALL_H #define ARM_TARGET_SYSCALL_H -/* this struct defines the way the registers are stored on the - stack during a system call. */ - -/* uregs[0..15] are r0 to r15; uregs[16] is CPSR; uregs[17] is ORIG_r0 */ -struct target_pt_regs { - abi_long uregs[18]; -}; - #define ARM_SYSCALL_BASE 0x900000 #define ARM_THUMB_SYSCALL 0 From af6da9e6b845f31675343c58786b2389b2f4a0ec Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Aug 2025 11:30:59 +1000 Subject: [PATCH 0178/2396] linux-user/arm: Expand target_elf_gregset_t Make use of the fact that target_elf_gregset_t is a proper structure. Drop ELF_NREG, target_elf_greg_t, and tswapreg. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/arm/elfload.c | 8 +++----- linux-user/arm/target_elf.h | 11 +++++++---- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/linux-user/arm/elfload.c b/linux-user/arm/elfload.c index 47fe16a1a6..f811c2f07a 100644 --- a/linux-user/arm/elfload.c +++ b/linux-user/arm/elfload.c @@ -201,13 +201,11 @@ const char *get_elf_platform(CPUState *cs) #undef END } -#define tswapreg(ptr) tswapal(ptr) - void elf_core_copy_regs(target_elf_gregset_t *r, const CPUARMState *env) { for (int i = 0; i < 16; ++i) { - r->regs[i] = tswapreg(env->regs[i]); + r->pt.regs[i] = tswapal(env->regs[i]); } - r->regs[16] = tswapreg(cpsr_read((CPUARMState *)env)); - r->regs[17] = tswapreg(env->regs[0]); /* XXX */ + r->pt.cpsr = tswapal(cpsr_read((CPUARMState *)env)); + r->pt.orig_r0 = tswapal(env->regs[0]); /* FIXME */ } diff --git a/linux-user/arm/target_elf.h b/linux-user/arm/target_elf.h index 94db3738e8..fa8f8af2f3 100644 --- a/linux-user/arm/target_elf.h +++ b/linux-user/arm/target_elf.h @@ -8,16 +8,19 @@ #ifndef ARM_TARGET_ELF_H #define ARM_TARGET_ELF_H +#include "target_ptrace.h" + #define HAVE_ELF_HWCAP 1 #define HAVE_ELF_HWCAP2 1 #define HAVE_ELF_PLATFORM 1 #define HAVE_ELF_CORE_DUMP 1 -typedef abi_ulong target_elf_greg_t; - -#define ELF_NREG 18 +/* + * See linux kernel: arch/arm/include/asm/elf.h, where + * elf_gregset_t is mapped to struct pt_regs via sizeof. + */ typedef struct target_elf_gregset_t { - target_elf_greg_t regs[ELF_NREG]; + struct target_pt_regs pt; } target_elf_gregset_t; #endif From 9308223c714d1efb275cf21c439ee85f5e6cad0d Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Aug 2025 15:03:40 +1000 Subject: [PATCH 0179/2396] linux-user/loongarch64: Create target_ptrace.h Remove the target_pt_regs structure from target_syscall.h. Add target_user_pt_regs to target_ptrace.h, which matches what is actually used on loongarch64. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/loongarch64/target_ptrace.h | 15 +++++++++++++++ linux-user/loongarch64/target_syscall.h | 23 ----------------------- 2 files changed, 15 insertions(+), 23 deletions(-) create mode 100644 linux-user/loongarch64/target_ptrace.h diff --git a/linux-user/loongarch64/target_ptrace.h b/linux-user/loongarch64/target_ptrace.h new file mode 100644 index 0000000000..2578e09207 --- /dev/null +++ b/linux-user/loongarch64/target_ptrace.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef LOONGARCH64_TARGET_PTRACE_H +#define LOONGARCH64_TARGET_PTRACE_H + +/* See arch/loongarch/include/uapi/asm/ptrace.h. */ +struct target_user_pt_regs { + abi_ulong regs[32]; + abi_ulong orig_a0; + abi_ulong csr_era; + abi_ulong csr_badv; + abi_ulong reserved[10]; +}; + +#endif /* LOONGARCH64_TARGET_PTRACE_H */ diff --git a/linux-user/loongarch64/target_syscall.h b/linux-user/loongarch64/target_syscall.h index 39f229bb9c..f7ced7b2be 100644 --- a/linux-user/loongarch64/target_syscall.h +++ b/linux-user/loongarch64/target_syscall.h @@ -8,29 +8,6 @@ #include "qemu/units.h" -/* - * this struct defines the way the registers are stored on the - * stack during a system call. - */ - -struct target_pt_regs { - /* Saved main processor registers. */ - target_ulong regs[32]; - - /* Saved special registers. */ - struct { - target_ulong era; - target_ulong badv; - target_ulong crmd; - target_ulong prmd; - target_ulong euen; - target_ulong ecfg; - target_ulong estat; - } csr; - target_ulong orig_a0; - target_ulong __last[0]; -}; - #define UNAME_MACHINE "loongarch64" #define UNAME_MINIMUM_RELEASE "5.19.0" From 190cc717f8cf50b7d38b8f9b8a2045540f71c571 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Aug 2025 11:37:08 +1000 Subject: [PATCH 0180/2396] linux-user/loongarch64: Expand target_elf_gregset_t Make use of the fact that target_elf_gregset_t is a proper structure. Note that the kernel's uses an array, and then it has a bunch of defines to create symbolic offsets. Modulo some reserved fields, which we do not implement here, this is the same layout as struct user_pt_regs. Drop ELF_NREG, target_elf_greg_t, and tswapreg. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/loongarch64/elfload.c | 14 ++++---------- linux-user/loongarch64/target_elf.h | 7 +++---- 2 files changed, 7 insertions(+), 14 deletions(-) diff --git a/linux-user/loongarch64/elfload.c b/linux-user/loongarch64/elfload.c index 832890de10..ce3bd0c607 100644 --- a/linux-user/loongarch64/elfload.c +++ b/linux-user/loongarch64/elfload.c @@ -65,20 +65,14 @@ const char *get_elf_platform(CPUState *cs) #define tswapreg(ptr) tswapal(ptr) -enum { - TARGET_EF_R0 = 0, - TARGET_EF_CSR_ERA = TARGET_EF_R0 + 33, - TARGET_EF_CSR_BADV = TARGET_EF_R0 + 34, -}; - void elf_core_copy_regs(target_elf_gregset_t *r, const CPULoongArchState *env) { - r->regs[TARGET_EF_R0] = 0; + r->pt.regs[0] = 0; for (int i = 1; i < ARRAY_SIZE(env->gpr); i++) { - r->regs[TARGET_EF_R0 + i] = tswapreg(env->gpr[i]); + r->pt.regs[i] = tswapreg(env->gpr[i]); } - r->regs[TARGET_EF_CSR_ERA] = tswapreg(env->pc); - r->regs[TARGET_EF_CSR_BADV] = tswapreg(env->CSR_BADV); + r->pt.csr_era = tswapreg(env->pc); + r->pt.csr_badv = tswapreg(env->CSR_BADV); } diff --git a/linux-user/loongarch64/target_elf.h b/linux-user/loongarch64/target_elf.h index 90bca4499d..1f40419af2 100644 --- a/linux-user/loongarch64/target_elf.h +++ b/linux-user/loongarch64/target_elf.h @@ -6,16 +6,15 @@ #ifndef LOONGARCH_TARGET_ELF_H #define LOONGARCH_TARGET_ELF_H +#include "target_ptrace.h" + #define HAVE_ELF_HWCAP 1 #define HAVE_ELF_PLATFORM 1 #define HAVE_ELF_CORE_DUMP 1 -typedef abi_ulong target_elf_greg_t; - /* See linux kernel: arch/loongarch/include/asm/elf.h */ -#define ELF_NREG 45 typedef struct target_elf_gregset_t { - target_elf_greg_t regs[ELF_NREG]; + struct target_user_pt_regs pt; } target_elf_gregset_t; #endif From 5c3a0884efe7d9a8651bc847571584804146f3f1 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Aug 2025 11:44:40 +1000 Subject: [PATCH 0181/2396] linux-user/m68k: Expand target_elf_gregset_t Make use of the fact that target_elf_gregset_t is a proper structure. Drop ELF_NREG, target_elf_greg_t, and tswapreg. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/m68k/elfload.c | 42 +++++++++++++++++------------------- linux-user/m68k/target_elf.h | 24 ++++++++++++++++----- 2 files changed, 39 insertions(+), 27 deletions(-) diff --git a/linux-user/m68k/elfload.c b/linux-user/m68k/elfload.c index 2970ff7dec..423d1f680a 100644 --- a/linux-user/m68k/elfload.c +++ b/linux-user/m68k/elfload.c @@ -18,28 +18,26 @@ const char *get_elf_cpu_model(uint32_t eflags) return "any"; } -#define tswapreg(ptr) tswapal(ptr) - void elf_core_copy_regs(target_elf_gregset_t *r, const CPUM68KState *env) { - r->regs[0] = tswapreg(env->dregs[1]); - r->regs[1] = tswapreg(env->dregs[2]); - r->regs[2] = tswapreg(env->dregs[3]); - r->regs[3] = tswapreg(env->dregs[4]); - r->regs[4] = tswapreg(env->dregs[5]); - r->regs[5] = tswapreg(env->dregs[6]); - r->regs[6] = tswapreg(env->dregs[7]); - r->regs[7] = tswapreg(env->aregs[0]); - r->regs[8] = tswapreg(env->aregs[1]); - r->regs[9] = tswapreg(env->aregs[2]); - r->regs[10] = tswapreg(env->aregs[3]); - r->regs[11] = tswapreg(env->aregs[4]); - r->regs[12] = tswapreg(env->aregs[5]); - r->regs[13] = tswapreg(env->aregs[6]); - r->regs[14] = tswapreg(env->dregs[0]); - r->regs[15] = tswapreg(env->aregs[7]); - r->regs[16] = tswapreg(env->dregs[0]); /* FIXME: orig_d0 */ - r->regs[17] = tswapreg(env->sr); - r->regs[18] = tswapreg(env->pc); - r->regs[19] = 0; /* FIXME: regs->format | regs->vector */ + r->d1 = tswapal(env->dregs[1]); + r->d2 = tswapal(env->dregs[2]); + r->d3 = tswapal(env->dregs[3]); + r->d4 = tswapal(env->dregs[4]); + r->d5 = tswapal(env->dregs[5]); + r->d6 = tswapal(env->dregs[6]); + r->d7 = tswapal(env->dregs[7]); + r->a0 = tswapal(env->aregs[0]); + r->a1 = tswapal(env->aregs[1]); + r->a2 = tswapal(env->aregs[2]); + r->a3 = tswapal(env->aregs[3]); + r->a4 = tswapal(env->aregs[4]); + r->a5 = tswapal(env->aregs[5]); + r->a6 = tswapal(env->aregs[6]); + r->d0 = tswapal(env->dregs[0]); + r->usp = tswapal(env->aregs[7]); + r->orig_d0 = tswapal(env->dregs[0]); /* FIXME */ + r->sr = tswapal(env->sr); + r->pc = tswapal(env->pc); + /* FIXME: regs->format | regs->vector */ } diff --git a/linux-user/m68k/target_elf.h b/linux-user/m68k/target_elf.h index cd6908ab57..0737412cee 100644 --- a/linux-user/m68k/target_elf.h +++ b/linux-user/m68k/target_elf.h @@ -10,12 +10,26 @@ #define HAVE_ELF_CORE_DUMP 1 -typedef abi_ulong target_elf_greg_t; - -/* See linux kernel: arch/m68k/include/asm/elf.h. */ -#define ELF_NREG 20 +/* + * See linux kernel: arch/m68k/include/asm/elf.h, where + * elf_gregset_t is mapped to struct user_regs_struct via sizeof. + * + * Note that user_regs_struct has + * short stkadj, sr; + * ... + * short fmtvec, __fill; + * but ELF_CORE_COPY_REGS writes to unsigned longs. + * Therefore adjust the sr and fmtvec fields to match. + */ typedef struct target_elf_gregset_t { - target_elf_greg_t regs[ELF_NREG]; + abi_ulong d1, d2, d3, d4, d5, d6, d7; + abi_ulong a0, a1, a2, a3, a4, a5, a6; + abi_ulong d0; + abi_ulong usp; + abi_ulong orig_d0; + abi_ulong sr; + abi_ulong pc; + abi_ulong fmtvec; } target_elf_gregset_t; #endif From cf33264f19b566cda4b50ccfec9e87734b08971f Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Aug 2025 15:22:14 +1000 Subject: [PATCH 0182/2396] linux-user/microblaze: Create target_ptrace.h Move the target_pt_regs structure from target_syscall.h. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/microblaze/signal.c | 1 + linux-user/microblaze/target_ptrace.h | 50 ++++++++++++++++++++++++++ linux-user/microblaze/target_syscall.h | 44 ----------------------- 3 files changed, 51 insertions(+), 44 deletions(-) create mode 100644 linux-user/microblaze/target_ptrace.h diff --git a/linux-user/microblaze/signal.c b/linux-user/microblaze/signal.c index f6d47d76ff..7aef781314 100644 --- a/linux-user/microblaze/signal.c +++ b/linux-user/microblaze/signal.c @@ -21,6 +21,7 @@ #include "user-internals.h" #include "signal-common.h" #include "linux-user/trace.h" +#include "target_ptrace.h" struct target_sigcontext { struct target_pt_regs regs; /* needs to be first */ diff --git a/linux-user/microblaze/target_ptrace.h b/linux-user/microblaze/target_ptrace.h new file mode 100644 index 0000000000..a46c8cb7bc --- /dev/null +++ b/linux-user/microblaze/target_ptrace.h @@ -0,0 +1,50 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef MICROBLAZE_TARGET_PTRACE_H +#define MICROBLAZE_TARGET_PTRACE_H + +/* We use microblaze_reg_t to keep things similar to the kernel sources. */ +typedef uint32_t microblaze_reg_t; + +struct target_pt_regs { + microblaze_reg_t r0; + microblaze_reg_t r1; + microblaze_reg_t r2; + microblaze_reg_t r3; + microblaze_reg_t r4; + microblaze_reg_t r5; + microblaze_reg_t r6; + microblaze_reg_t r7; + microblaze_reg_t r8; + microblaze_reg_t r9; + microblaze_reg_t r10; + microblaze_reg_t r11; + microblaze_reg_t r12; + microblaze_reg_t r13; + microblaze_reg_t r14; + microblaze_reg_t r15; + microblaze_reg_t r16; + microblaze_reg_t r17; + microblaze_reg_t r18; + microblaze_reg_t r19; + microblaze_reg_t r20; + microblaze_reg_t r21; + microblaze_reg_t r22; + microblaze_reg_t r23; + microblaze_reg_t r24; + microblaze_reg_t r25; + microblaze_reg_t r26; + microblaze_reg_t r27; + microblaze_reg_t r28; + microblaze_reg_t r29; + microblaze_reg_t r30; + microblaze_reg_t r31; + microblaze_reg_t pc; + microblaze_reg_t msr; + microblaze_reg_t ear; + microblaze_reg_t esr; + microblaze_reg_t fsr; + uint32_t kernel_mode; +}; + +#endif /* MICROBLAZE_TARGET_PTRACE_H */ diff --git a/linux-user/microblaze/target_syscall.h b/linux-user/microblaze/target_syscall.h index 43362a1664..66f5a9ebe2 100644 --- a/linux-user/microblaze/target_syscall.h +++ b/linux-user/microblaze/target_syscall.h @@ -4,50 +4,6 @@ #define UNAME_MACHINE "microblaze" #define UNAME_MINIMUM_RELEASE "2.6.32" -/* We use microblaze_reg_t to keep things similar to the kernel sources. */ -typedef uint32_t microblaze_reg_t; - -struct target_pt_regs { - microblaze_reg_t r0; - microblaze_reg_t r1; - microblaze_reg_t r2; - microblaze_reg_t r3; - microblaze_reg_t r4; - microblaze_reg_t r5; - microblaze_reg_t r6; - microblaze_reg_t r7; - microblaze_reg_t r8; - microblaze_reg_t r9; - microblaze_reg_t r10; - microblaze_reg_t r11; - microblaze_reg_t r12; - microblaze_reg_t r13; - microblaze_reg_t r14; - microblaze_reg_t r15; - microblaze_reg_t r16; - microblaze_reg_t r17; - microblaze_reg_t r18; - microblaze_reg_t r19; - microblaze_reg_t r20; - microblaze_reg_t r21; - microblaze_reg_t r22; - microblaze_reg_t r23; - microblaze_reg_t r24; - microblaze_reg_t r25; - microblaze_reg_t r26; - microblaze_reg_t r27; - microblaze_reg_t r28; - microblaze_reg_t r29; - microblaze_reg_t r30; - microblaze_reg_t r31; - microblaze_reg_t pc; - microblaze_reg_t msr; - microblaze_reg_t ear; - microblaze_reg_t esr; - microblaze_reg_t fsr; - uint32_t kernel_mode; -}; - #define TARGET_CLONE_BACKWARDS #define TARGET_MCL_CURRENT 1 #define TARGET_MCL_FUTURE 2 From e5c31ef558da16fb9cbb9e3a3c7247b60fdfd05b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Aug 2025 15:28:11 +1000 Subject: [PATCH 0183/2396] linux-user/microblaze: Fold target_pt_regs.r* to an array Separately enumerating all 32 registers is not helpful. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/microblaze/signal.c | 70 +++------------------------ linux-user/microblaze/target_ptrace.h | 34 +------------ 2 files changed, 8 insertions(+), 96 deletions(-) diff --git a/linux-user/microblaze/signal.c b/linux-user/microblaze/signal.c index 7aef781314..e874e4def1 100644 --- a/linux-user/microblaze/signal.c +++ b/linux-user/microblaze/signal.c @@ -51,75 +51,17 @@ struct target_rt_sigframe { static void setup_sigcontext(struct target_sigcontext *sc, CPUMBState *env) { - __put_user(env->regs[0], &sc->regs.r0); - __put_user(env->regs[1], &sc->regs.r1); - __put_user(env->regs[2], &sc->regs.r2); - __put_user(env->regs[3], &sc->regs.r3); - __put_user(env->regs[4], &sc->regs.r4); - __put_user(env->regs[5], &sc->regs.r5); - __put_user(env->regs[6], &sc->regs.r6); - __put_user(env->regs[7], &sc->regs.r7); - __put_user(env->regs[8], &sc->regs.r8); - __put_user(env->regs[9], &sc->regs.r9); - __put_user(env->regs[10], &sc->regs.r10); - __put_user(env->regs[11], &sc->regs.r11); - __put_user(env->regs[12], &sc->regs.r12); - __put_user(env->regs[13], &sc->regs.r13); - __put_user(env->regs[14], &sc->regs.r14); - __put_user(env->regs[15], &sc->regs.r15); - __put_user(env->regs[16], &sc->regs.r16); - __put_user(env->regs[17], &sc->regs.r17); - __put_user(env->regs[18], &sc->regs.r18); - __put_user(env->regs[19], &sc->regs.r19); - __put_user(env->regs[20], &sc->regs.r20); - __put_user(env->regs[21], &sc->regs.r21); - __put_user(env->regs[22], &sc->regs.r22); - __put_user(env->regs[23], &sc->regs.r23); - __put_user(env->regs[24], &sc->regs.r24); - __put_user(env->regs[25], &sc->regs.r25); - __put_user(env->regs[26], &sc->regs.r26); - __put_user(env->regs[27], &sc->regs.r27); - __put_user(env->regs[28], &sc->regs.r28); - __put_user(env->regs[29], &sc->regs.r29); - __put_user(env->regs[30], &sc->regs.r30); - __put_user(env->regs[31], &sc->regs.r31); + for (int i = 0; i < 32; ++i) { + __put_user(env->regs[i], &sc->regs.r[i]); + } __put_user(env->pc, &sc->regs.pc); } static void restore_sigcontext(struct target_sigcontext *sc, CPUMBState *env) { - __get_user(env->regs[0], &sc->regs.r0); - __get_user(env->regs[1], &sc->regs.r1); - __get_user(env->regs[2], &sc->regs.r2); - __get_user(env->regs[3], &sc->regs.r3); - __get_user(env->regs[4], &sc->regs.r4); - __get_user(env->regs[5], &sc->regs.r5); - __get_user(env->regs[6], &sc->regs.r6); - __get_user(env->regs[7], &sc->regs.r7); - __get_user(env->regs[8], &sc->regs.r8); - __get_user(env->regs[9], &sc->regs.r9); - __get_user(env->regs[10], &sc->regs.r10); - __get_user(env->regs[11], &sc->regs.r11); - __get_user(env->regs[12], &sc->regs.r12); - __get_user(env->regs[13], &sc->regs.r13); - __get_user(env->regs[14], &sc->regs.r14); - __get_user(env->regs[15], &sc->regs.r15); - __get_user(env->regs[16], &sc->regs.r16); - __get_user(env->regs[17], &sc->regs.r17); - __get_user(env->regs[18], &sc->regs.r18); - __get_user(env->regs[19], &sc->regs.r19); - __get_user(env->regs[20], &sc->regs.r20); - __get_user(env->regs[21], &sc->regs.r21); - __get_user(env->regs[22], &sc->regs.r22); - __get_user(env->regs[23], &sc->regs.r23); - __get_user(env->regs[24], &sc->regs.r24); - __get_user(env->regs[25], &sc->regs.r25); - __get_user(env->regs[26], &sc->regs.r26); - __get_user(env->regs[27], &sc->regs.r27); - __get_user(env->regs[28], &sc->regs.r28); - __get_user(env->regs[29], &sc->regs.r29); - __get_user(env->regs[30], &sc->regs.r30); - __get_user(env->regs[31], &sc->regs.r31); + for (int i = 0; i < 32; ++i) { + __get_user(env->regs[i], &sc->regs.r[i]); + } __get_user(env->pc, &sc->regs.pc); } diff --git a/linux-user/microblaze/target_ptrace.h b/linux-user/microblaze/target_ptrace.h index a46c8cb7bc..ead913e5a4 100644 --- a/linux-user/microblaze/target_ptrace.h +++ b/linux-user/microblaze/target_ptrace.h @@ -7,38 +7,8 @@ typedef uint32_t microblaze_reg_t; struct target_pt_regs { - microblaze_reg_t r0; - microblaze_reg_t r1; - microblaze_reg_t r2; - microblaze_reg_t r3; - microblaze_reg_t r4; - microblaze_reg_t r5; - microblaze_reg_t r6; - microblaze_reg_t r7; - microblaze_reg_t r8; - microblaze_reg_t r9; - microblaze_reg_t r10; - microblaze_reg_t r11; - microblaze_reg_t r12; - microblaze_reg_t r13; - microblaze_reg_t r14; - microblaze_reg_t r15; - microblaze_reg_t r16; - microblaze_reg_t r17; - microblaze_reg_t r18; - microblaze_reg_t r19; - microblaze_reg_t r20; - microblaze_reg_t r21; - microblaze_reg_t r22; - microblaze_reg_t r23; - microblaze_reg_t r24; - microblaze_reg_t r25; - microblaze_reg_t r26; - microblaze_reg_t r27; - microblaze_reg_t r28; - microblaze_reg_t r29; - microblaze_reg_t r30; - microblaze_reg_t r31; + /* Note the kernel enumerates all 32 registers. */ + microblaze_reg_t r[32]; microblaze_reg_t pc; microblaze_reg_t msr; microblaze_reg_t ear; From e803a48c1ac72ed47b6490e3d7cf6b0cc7372e85 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Aug 2025 11:55:27 +1000 Subject: [PATCH 0184/2396] linux-user/microblaze: Expand target_elf_gregset_t Make use of the fact that target_elf_gregset_t is a proper structure. Drop ELF_NREG, target_elf_greg_t, and tswapreg. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/microblaze/elfload.c | 14 +++++--------- linux-user/microblaze/target_elf.h | 11 +++++++---- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/linux-user/microblaze/elfload.c b/linux-user/microblaze/elfload.c index 89250dbd63..7eb1b26d17 100644 --- a/linux-user/microblaze/elfload.c +++ b/linux-user/microblaze/elfload.c @@ -11,18 +11,14 @@ const char *get_elf_cpu_model(uint32_t eflags) return "any"; } -#define tswapreg(ptr) tswapal(ptr) - void elf_core_copy_regs(target_elf_gregset_t *r, const CPUMBState *env) { for (int i = 0; i < 32; i++) { - r->regs[i] = tswapreg(env->regs[i]); + r->pt.r[i] = tswapal(env->regs[i]); } - r->regs[32] = tswapreg(env->pc); - r->regs[33] = tswapreg(mb_cpu_read_msr(env)); - r->regs[34] = 0; - r->regs[35] = tswapreg(env->ear); - r->regs[36] = 0; - r->regs[37] = tswapreg(env->esr); + r->pt.pc = tswapal(env->pc); + r->pt.msr = tswapal(mb_cpu_read_msr(env)); + r->pt.ear = tswapal(env->ear); + r->pt.esr = tswapal(env->esr); } diff --git a/linux-user/microblaze/target_elf.h b/linux-user/microblaze/target_elf.h index cc5cc0477e..56de77d4f3 100644 --- a/linux-user/microblaze/target_elf.h +++ b/linux-user/microblaze/target_elf.h @@ -8,13 +8,16 @@ #ifndef MICROBLAZE_TARGET_ELF_H #define MICROBLAZE_TARGET_ELF_H +#include "target_ptrace.h" + #define HAVE_ELF_CORE_DUMP 1 -typedef abi_ulong target_elf_greg_t; - -#define ELF_NREG 38 +/* + * See linux kernel: arch/microblaze/include/asm/elf.h, where + * elf_gregset_t is mapped to struct pt_regs via sizeof. + */ typedef struct target_elf_gregset_t { - target_elf_greg_t regs[ELF_NREG]; + struct target_pt_regs pt; } target_elf_gregset_t; #endif From 0dcef5773000f4d72277c6b41200f35031bdcbb5 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Aug 2025 15:34:41 +1000 Subject: [PATCH 0185/2396] linux-user/mips: Create target_ptrace.h Move the target_pt_regs structure from target_syscall.h. Fix the incorrect ordering of the fields. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/mips/target_ptrace.h | 17 +++++++++++++++++ linux-user/mips/target_syscall.h | 19 ------------------- linux-user/mips64/target_ptrace.h | 16 ++++++++++++++++ linux-user/mips64/target_syscall.h | 16 ---------------- 4 files changed, 33 insertions(+), 35 deletions(-) create mode 100644 linux-user/mips/target_ptrace.h create mode 100644 linux-user/mips64/target_ptrace.h diff --git a/linux-user/mips/target_ptrace.h b/linux-user/mips/target_ptrace.h new file mode 100644 index 0000000000..2f63b27ac4 --- /dev/null +++ b/linux-user/mips/target_ptrace.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef MIPS_TARGET_PTRACE_H +#define MIPS_TARGET_PTRACE_H + +struct target_pt_regs { + abi_ulong pad0[6]; + abi_ulong regs[32]; + abi_ulong lo; + abi_ulong hi; + abi_ulong cp0_epc; + abi_ulong cp0_badvaddr; + abi_ulong cp0_status; + abi_ulong cp0_cause; +}; + +#endif /* MIPS_TARGET_PTRACE_H */ diff --git a/linux-user/mips/target_syscall.h b/linux-user/mips/target_syscall.h index 08ead67810..dfcdf320b7 100644 --- a/linux-user/mips/target_syscall.h +++ b/linux-user/mips/target_syscall.h @@ -1,25 +1,6 @@ #ifndef MIPS_TARGET_SYSCALL_H #define MIPS_TARGET_SYSCALL_H -/* this struct defines the way the registers are stored on the - stack during a system call. */ - -struct target_pt_regs { - /* Pad bytes for argument save space on the stack. */ - abi_ulong pad0[6]; - - /* Saved main processor registers. */ - abi_ulong regs[32]; - - /* Saved special registers. */ - abi_ulong cp0_status; - abi_ulong lo; - abi_ulong hi; - abi_ulong cp0_badvaddr; - abi_ulong cp0_cause; - abi_ulong cp0_epc; -}; - #define UNAME_MACHINE "mips" #define UNAME_MINIMUM_RELEASE "2.6.32" diff --git a/linux-user/mips64/target_ptrace.h b/linux-user/mips64/target_ptrace.h new file mode 100644 index 0000000000..41f0bf6c1c --- /dev/null +++ b/linux-user/mips64/target_ptrace.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef MIPS64_TARGET_PTRACE_H +#define MIPS64_TARGET_PTRACE_H + +struct target_pt_regs { + target_ulong regs[32]; + target_ulong lo; + target_ulong hi; + target_ulong cp0_epc; + target_ulong cp0_badvaddr; + target_ulong cp0_status; + target_ulong cp0_cause; +}; + +#endif /* MIPS64_TARGET_PTRACE_H */ diff --git a/linux-user/mips64/target_syscall.h b/linux-user/mips64/target_syscall.h index 358dc2d64c..9135bf5e8b 100644 --- a/linux-user/mips64/target_syscall.h +++ b/linux-user/mips64/target_syscall.h @@ -1,22 +1,6 @@ #ifndef MIPS64_TARGET_SYSCALL_H #define MIPS64_TARGET_SYSCALL_H -/* this struct defines the way the registers are stored on the - stack during a system call. */ - -struct target_pt_regs { - /* Saved main processor registers. */ - target_ulong regs[32]; - - /* Saved special registers. */ - target_ulong cp0_status; - target_ulong lo; - target_ulong hi; - target_ulong cp0_badvaddr; - target_ulong cp0_cause; - target_ulong cp0_epc; -}; - #define UNAME_MACHINE "mips64" #define UNAME_MINIMUM_RELEASE "2.6.32" From c61b88fbe49e428e39eff501cddbcd53f12486cb Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Aug 2025 12:05:05 +1000 Subject: [PATCH 0186/2396] linux-user/mips: Use target_ulong for target_elf_greg_t Make use of the fact that target_elf_gregset_t is a proper structure. The target_ulong type matches the abi_ulong/abi_ullong selection within mips64/target_elf.h. Drop ELF_NREG, target_elf_greg_t, and tswapreg. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/mips/elfload.c | 48 +++++++--------------------------- linux-user/mips/target_elf.h | 10 ++++--- linux-user/mips64/target_elf.h | 14 +++++----- 3 files changed, 22 insertions(+), 50 deletions(-) diff --git a/linux-user/mips/elfload.c b/linux-user/mips/elfload.c index 6e884911af..e0c50f50ed 100644 --- a/linux-user/mips/elfload.c +++ b/linux-user/mips/elfload.c @@ -124,47 +124,19 @@ const char *get_elf_base_platform(CPUState *cs) #undef MATCH_PLATFORM_INSN -#ifdef TARGET_ABI_MIPSN32 -#define tswapreg(ptr) tswap64(ptr) -#else -#define tswapreg(ptr) tswapal(ptr) -#endif - -/* See linux kernel: arch/mips/include/asm/reg.h. */ -enum { -#ifdef TARGET_MIPS64 - TARGET_EF_R0 = 0, -#else - TARGET_EF_R0 = 6, -#endif - TARGET_EF_R26 = TARGET_EF_R0 + 26, - TARGET_EF_R27 = TARGET_EF_R0 + 27, - TARGET_EF_LO = TARGET_EF_R0 + 32, - TARGET_EF_HI = TARGET_EF_R0 + 33, - TARGET_EF_CP0_EPC = TARGET_EF_R0 + 34, - TARGET_EF_CP0_BADVADDR = TARGET_EF_R0 + 35, - TARGET_EF_CP0_STATUS = TARGET_EF_R0 + 36, - TARGET_EF_CP0_CAUSE = TARGET_EF_R0 + 37 -}; - /* See linux kernel: arch/mips/kernel/process.c:elf_dump_regs. */ void elf_core_copy_regs(target_elf_gregset_t *r, const CPUMIPSState *env) { - int i; - - for (i = 0; i <= TARGET_EF_R0; i++) { - r->regs[i] = 0; - } - for (i = 1; i < ARRAY_SIZE(env->active_tc.gpr); i++) { - r->regs[TARGET_EF_R0 + i] = tswapreg(env->active_tc.gpr[i]); + for (int i = 1; i < ARRAY_SIZE(env->active_tc.gpr); i++) { + r->pt.regs[i] = tswapl(env->active_tc.gpr[i]); } - r->regs[TARGET_EF_R26] = 0; - r->regs[TARGET_EF_R27] = 0; - r->regs[TARGET_EF_LO] = tswapreg(env->active_tc.LO[0]); - r->regs[TARGET_EF_HI] = tswapreg(env->active_tc.HI[0]); - r->regs[TARGET_EF_CP0_EPC] = tswapreg(env->active_tc.PC); - r->regs[TARGET_EF_CP0_BADVADDR] = tswapreg(env->CP0_BadVAddr); - r->regs[TARGET_EF_CP0_STATUS] = tswapreg(env->CP0_Status); - r->regs[TARGET_EF_CP0_CAUSE] = tswapreg(env->CP0_Cause); + r->pt.regs[26] = 0; + r->pt.regs[27] = 0; + r->pt.lo = tswapl(env->active_tc.LO[0]); + r->pt.hi = tswapl(env->active_tc.HI[0]); + r->pt.cp0_epc = tswapl(env->active_tc.PC); + r->pt.cp0_badvaddr = tswapl(env->CP0_BadVAddr); + r->pt.cp0_status = tswapl(env->CP0_Status); + r->pt.cp0_cause = tswapl(env->CP0_Cause); } diff --git a/linux-user/mips/target_elf.h b/linux-user/mips/target_elf.h index f767767eaa..a4b7fadbd6 100644 --- a/linux-user/mips/target_elf.h +++ b/linux-user/mips/target_elf.h @@ -8,16 +8,18 @@ #ifndef MIPS_TARGET_ELF_H #define MIPS_TARGET_ELF_H +#include "target_ptrace.h" + #define HAVE_ELF_HWCAP 1 #define HAVE_ELF_BASE_PLATFORM 1 #define HAVE_ELF_CORE_DUMP 1 -typedef abi_ulong target_elf_greg_t; - /* See linux kernel: arch/mips/include/asm/elf.h. */ -#define ELF_NREG 45 typedef struct target_elf_gregset_t { - target_elf_greg_t regs[ELF_NREG]; + union { + abi_ulong reserved[45]; + struct target_pt_regs pt; + }; } target_elf_gregset_t; #endif diff --git a/linux-user/mips64/target_elf.h b/linux-user/mips64/target_elf.h index 046a165eef..67bc963134 100644 --- a/linux-user/mips64/target_elf.h +++ b/linux-user/mips64/target_elf.h @@ -8,20 +8,18 @@ #ifndef MIPS64_TARGET_ELF_H #define MIPS64_TARGET_ELF_H +#include "target_ptrace.h" + #define HAVE_ELF_HWCAP 1 #define HAVE_ELF_BASE_PLATFORM 1 #define HAVE_ELF_CORE_DUMP 1 -#ifdef TARGET_ABI_MIPSN32 -typedef abi_ullong target_elf_greg_t; -#else -typedef abi_ulong target_elf_greg_t; -#endif - /* See linux kernel: arch/mips/include/asm/elf.h. */ -#define ELF_NREG 45 typedef struct target_elf_gregset_t { - target_elf_greg_t regs[ELF_NREG]; + union { + target_ulong reserved[45]; + struct target_pt_regs pt; + }; } target_elf_gregset_t; #endif From 7a4512db0a52b5005999f839cec934a77f32437b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Aug 2025 15:47:43 +1000 Subject: [PATCH 0187/2396] linux-user/openrisc: Create target_ptrace.h Move the target_pt_regs structure from target_syscall.h and rename to target_user_regs_struct, obviating the comment. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/openrisc/signal.c | 3 ++- linux-user/openrisc/target_ptrace.h | 13 +++++++++++++ linux-user/openrisc/target_syscall.h | 11 ----------- 3 files changed, 15 insertions(+), 12 deletions(-) create mode 100644 linux-user/openrisc/target_ptrace.h diff --git a/linux-user/openrisc/signal.c b/linux-user/openrisc/signal.c index cb74a9fe5e..40249095f2 100644 --- a/linux-user/openrisc/signal.c +++ b/linux-user/openrisc/signal.c @@ -21,9 +21,10 @@ #include "user-internals.h" #include "signal-common.h" #include "linux-user/trace.h" +#include "target_ptrace.h" typedef struct target_sigcontext { - struct target_pt_regs regs; + struct target_user_regs_struct regs; abi_ulong oldmask; } target_sigcontext; diff --git a/linux-user/openrisc/target_ptrace.h b/linux-user/openrisc/target_ptrace.h new file mode 100644 index 0000000000..563c648525 --- /dev/null +++ b/linux-user/openrisc/target_ptrace.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef OPENRISC_TARGET_PTRACE_H +#define OPENRISC_TARGET_PTRACE_H + +/* See arch/openrisc/include/uapi/asm/ptrace.h. */ +struct target_user_regs_struct { + abi_ulong gpr[32]; + abi_ulong pc; + abi_ulong sr; +}; + +#endif /* OPENRISC_TARGET_PTRACE_H */ diff --git a/linux-user/openrisc/target_syscall.h b/linux-user/openrisc/target_syscall.h index 7fe5b73d3b..c8394e9dcd 100644 --- a/linux-user/openrisc/target_syscall.h +++ b/linux-user/openrisc/target_syscall.h @@ -1,17 +1,6 @@ #ifndef OPENRISC_TARGET_SYSCALL_H #define OPENRISC_TARGET_SYSCALL_H -/* Note that in linux/arch/openrisc/include/uapi/asm/ptrace.h, - * this is called user_regs_struct. Given that this is what - * is used within struct sigcontext we need this definition. - * However, elfload.c wants this name. - */ -struct target_pt_regs { - abi_ulong gpr[32]; - abi_ulong pc; - abi_ulong sr; -}; - #define UNAME_MACHINE "openrisc" #define UNAME_MINIMUM_RELEASE "2.6.32" From 611dd00a45860027f51281fc31e6428aefc8a003 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Aug 2025 12:09:26 +1000 Subject: [PATCH 0188/2396] linux-user/openrisc: Expand target_elf_gregset_t Make use of the fact that target_elf_gregset_t is a proper structure. Drop ELF_NREG, target_elf_greg_t, and tswapreg. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/openrisc/elfload.c | 8 +++----- linux-user/openrisc/target_elf.h | 12 +++++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/linux-user/openrisc/elfload.c b/linux-user/openrisc/elfload.c index bb5ad96711..6bf02bf58d 100644 --- a/linux-user/openrisc/elfload.c +++ b/linux-user/openrisc/elfload.c @@ -11,13 +11,11 @@ const char *get_elf_cpu_model(uint32_t eflags) return "any"; } -#define tswapreg(ptr) tswapal(ptr) - void elf_core_copy_regs(target_elf_gregset_t *r, const CPUOpenRISCState *env) { for (int i = 0; i < 32; i++) { - r->regs[i] = tswapreg(cpu_get_gpr(env, i)); + r->pt.gpr[i] = tswapal(cpu_get_gpr(env, i)); } - r->regs[32] = tswapreg(env->pc); - r->regs[33] = tswapreg(cpu_get_sr(env)); + r->pt.pc = tswapal(env->pc); + r->pt.sr = tswapal(cpu_get_sr(env)); } diff --git a/linux-user/openrisc/target_elf.h b/linux-user/openrisc/target_elf.h index e97bdc11ed..ad80e4b41a 100644 --- a/linux-user/openrisc/target_elf.h +++ b/linux-user/openrisc/target_elf.h @@ -8,14 +8,16 @@ #ifndef OPENRISC_TARGET_ELF_H #define OPENRISC_TARGET_ELF_H +#include "target_ptrace.h" + #define HAVE_ELF_CORE_DUMP 1 -typedef abi_ulong target_elf_greg_t; - -/* See linux kernel arch/openrisc/include/asm/elf.h. */ -#define ELF_NREG 34 /* gprs and pc, sr */ +/* + * See linux kernel: arch/openrisc/include/uapi/asm/elf.h, where + * elf_gregset_t is mapped to struct user_regs_struct via sizeof. + */ typedef struct target_elf_gregset_t { - target_elf_greg_t regs[ELF_NREG]; + struct target_user_regs_struct pt; } target_elf_gregset_t; #endif From 584c21f34eb0419858d74420df7d0fd5f3ea419a Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Aug 2025 21:02:35 +1000 Subject: [PATCH 0189/2396] linux-user/ppc: Create target_ptrace.h Move the target_pt_regs structure from target_syscall.h. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/ppc/target_ptrace.h | 26 ++++++++++++++++++++++++++ linux-user/ppc/target_syscall.h | 28 ---------------------------- 2 files changed, 26 insertions(+), 28 deletions(-) create mode 100644 linux-user/ppc/target_ptrace.h diff --git a/linux-user/ppc/target_ptrace.h b/linux-user/ppc/target_ptrace.h new file mode 100644 index 0000000000..df77bfde73 --- /dev/null +++ b/linux-user/ppc/target_ptrace.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef PPC_TARGET_PTRACE_H +#define PPC_TARGET_PTRACE_H + +struct target_pt_regs { + abi_ulong gpr[32]; + abi_ulong nip; + abi_ulong msr; + abi_ulong orig_gpr3; /* Used for restarting system calls */ + abi_ulong ctr; + abi_ulong link; + abi_ulong xer; + abi_ulong ccr; +#if defined(TARGET_PPC64) + abi_ulong softe; +#else + abi_ulong mq; /* 601 only (not used at present) */ +#endif + abi_ulong trap; /* Reason for being here */ + abi_ulong dar; /* Fault registers */ + abi_ulong dsisr; + abi_ulong result; /* Result of a system call */ +}; + +#endif /* PPC_TARGET_PTRACE_H */ diff --git a/linux-user/ppc/target_syscall.h b/linux-user/ppc/target_syscall.h index 77b36d0b46..976b4bb7e9 100644 --- a/linux-user/ppc/target_syscall.h +++ b/linux-user/ppc/target_syscall.h @@ -20,34 +20,6 @@ #ifndef PPC_TARGET_SYSCALL_H #define PPC_TARGET_SYSCALL_H -/* XXX: ABSOLUTELY BUGGY: - * for now, this is quite just a cut-and-paste from i386 target... - */ - -/* default linux values for the selectors */ -#define __USER_DS (1) - -struct target_pt_regs { - abi_ulong gpr[32]; - abi_ulong nip; - abi_ulong msr; - abi_ulong orig_gpr3; /* Used for restarting system calls */ - abi_ulong ctr; - abi_ulong link; - abi_ulong xer; - abi_ulong ccr; -#if defined(TARGET_PPC64) - abi_ulong softe; -#else - abi_ulong mq; /* 601 only (not used at present) */ -#endif - /* Used on APUS to hold IPL value. */ - abi_ulong trap; /* Reason for being here */ - abi_ulong dar; /* Fault registers */ - abi_ulong dsisr; - abi_ulong result; /* Result of a system call */ -}; - /* ioctls */ struct target_revectored_struct { abi_ulong __map[8]; /* 256 bits */ From 8ad5f12426dd6218db4753722ab240a16a235259 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Aug 2025 12:22:55 +1000 Subject: [PATCH 0190/2396] linux-user/ppc: Expand target_elf_gregset_t Make use of the fact that target_elf_gregset_t is a proper structure. Drop ELF_NREG, target_elf_greg_t, and tswapreg. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/ppc/elfload.c | 23 ++++++++--------------- linux-user/ppc/target_elf.h | 16 +++++++++++----- 2 files changed, 19 insertions(+), 20 deletions(-) diff --git a/linux-user/ppc/elfload.c b/linux-user/ppc/elfload.c index 114e40a358..0d54da9803 100644 --- a/linux-user/ppc/elfload.c +++ b/linux-user/ppc/elfload.c @@ -131,23 +131,16 @@ abi_ulong get_elf_hwcap2(CPUState *cs) return features; } -#define tswapreg(ptr) tswapal(ptr) - void elf_core_copy_regs(target_elf_gregset_t *r, const CPUPPCState *env) { - int i; - target_ulong ccr = 0; - - for (i = 0; i < ARRAY_SIZE(env->gpr); i++) { - r->regs[i] = tswapreg(env->gpr[i]); + for (int i = 0; i < ARRAY_SIZE(env->gpr); i++) { + r->pt.gpr[i] = tswapal(env->gpr[i]); } - r->regs[32] = tswapreg(env->nip); - r->regs[33] = tswapreg(env->msr); - r->regs[35] = tswapreg(env->ctr); - r->regs[36] = tswapreg(env->lr); - r->regs[37] = tswapreg(cpu_read_xer(env)); - - ccr = ppc_get_cr(env); - r->regs[38] = tswapreg(ccr); + r->pt.nip = tswapal(env->nip); + r->pt.msr = tswapal(env->msr); + r->pt.ctr = tswapal(env->ctr); + r->pt.link = tswapal(env->lr); + r->pt.xer = tswapal(cpu_read_xer(env)); + r->pt.ccr = tswapal(ppc_get_cr(env)); } diff --git a/linux-user/ppc/target_elf.h b/linux-user/ppc/target_elf.h index 72615553ea..2a61cd2896 100644 --- a/linux-user/ppc/target_elf.h +++ b/linux-user/ppc/target_elf.h @@ -8,16 +8,22 @@ #ifndef PPC_TARGET_ELF_H #define PPC_TARGET_ELF_H +#include "target_ptrace.h" + #define HAVE_ELF_HWCAP 1 #define HAVE_ELF_HWCAP2 1 #define HAVE_ELF_CORE_DUMP 1 -typedef abi_ulong target_elf_greg_t; - -/* See linux kernel: arch/powerpc/include/asm/elf.h. */ -#define ELF_NREG 48 +/* + * The size of 48 words is set in arch/powerpc/include/uapi/asm/elf.h. + * However PPC_ELF_CORE_COPY_REGS in arch/powerpc/include/asm/elf.h + * open-codes a memcpy from struct pt_regs, then zeros the rest. + */ typedef struct target_elf_gregset_t { - target_elf_greg_t regs[ELF_NREG]; + union { + struct target_pt_regs pt; + abi_ulong reserved[48]; + }; } target_elf_gregset_t; #endif From 71c2c79815fafe6d509ca1a76bc67d6041aaa37b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Aug 2025 21:13:03 +1000 Subject: [PATCH 0191/2396] linux-user/s390x: Create target_ptrace.h Move target_psw_t to target_ptrace.h. Note that abi_ulong already has an attribute for 8-byte alignment, so there's no need to carry another on target_psw_t. Remove the target_pt_regs; add target_s390x_reg to target_ptrace.h, which matches what is actually used. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/s390x/signal.c | 1 + linux-user/s390x/target_ptrace.h | 18 ++++++++++++++++++ linux-user/s390x/target_syscall.h | 22 ---------------------- 3 files changed, 19 insertions(+), 22 deletions(-) create mode 100644 linux-user/s390x/target_ptrace.h diff --git a/linux-user/s390x/signal.c b/linux-user/s390x/signal.c index df49c24708..96d1c8d11c 100644 --- a/linux-user/s390x/signal.c +++ b/linux-user/s390x/signal.c @@ -22,6 +22,7 @@ #include "signal-common.h" #include "linux-user/trace.h" #include "vdso-asmoffset.h" +#include "target_ptrace.h" #define __NUM_GPRS 16 #define __NUM_FPRS 16 diff --git a/linux-user/s390x/target_ptrace.h b/linux-user/s390x/target_ptrace.h new file mode 100644 index 0000000000..a5ceb75a74 --- /dev/null +++ b/linux-user/s390x/target_ptrace.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef S390X_TARGET_PTRACE_H +#define S390X_TARGET_PTRACE_H + +typedef struct { + abi_ulong mask; + abi_ulong addr; +} target_psw_t; + +struct target_s390_regs { + target_psw_t psw; + abi_ulong gprs[16]; + abi_uint acrs[16]; + abi_ulong orig_gpr2; +}; + +#endif /* S390X_TARGET_PTRACE_H */ diff --git a/linux-user/s390x/target_syscall.h b/linux-user/s390x/target_syscall.h index 4018988a25..f01f9a0baa 100644 --- a/linux-user/s390x/target_syscall.h +++ b/linux-user/s390x/target_syscall.h @@ -1,28 +1,6 @@ #ifndef S390X_TARGET_SYSCALL_H #define S390X_TARGET_SYSCALL_H -/* this typedef defines how a Program Status Word looks like */ -typedef struct { - abi_ulong mask; - abi_ulong addr; -} __attribute__ ((aligned(8))) target_psw_t; - -/* - * The pt_regs struct defines the way the registers are stored on - * the stack during a system call. - */ - -#define TARGET_NUM_GPRS 16 - -struct target_pt_regs { - abi_ulong args[1]; - target_psw_t psw; - abi_ulong gprs[TARGET_NUM_GPRS]; - abi_ulong orig_gpr2; - unsigned short ilen; - unsigned short trap; -}; - #define UNAME_MACHINE "s390x" #define UNAME_MINIMUM_RELEASE "2.6.32" From dabda4f36a992dffde252919124a25fe7054fe42 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Aug 2025 12:31:49 +1000 Subject: [PATCH 0192/2396] linux-user/s390x: Expand target_elf_gregset_t Make use of the fact that target_elf_gregset_t is a proper structure. This lets us drop the ugly cast to uint32_t* in the middle. Drop ELF_NREG, target_elf_greg_t, and tswapreg. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/s390x/elfload.c | 28 +++++++--------------------- linux-user/s390x/target_elf.h | 12 +++++++----- 2 files changed, 14 insertions(+), 26 deletions(-) diff --git a/linux-user/s390x/elfload.c b/linux-user/s390x/elfload.c index 4113273b72..27109279e2 100644 --- a/linux-user/s390x/elfload.c +++ b/linux-user/s390x/elfload.c @@ -68,29 +68,15 @@ const char *elf_hwcap_str(uint32_t bit) return bit < ARRAY_SIZE(hwcap_str) ? hwcap_str[bit] : NULL; } -#define tswapreg(ptr) tswapal(ptr) - -enum { - TARGET_REG_PSWM = 0, - TARGET_REG_PSWA = 1, - TARGET_REG_GPRS = 2, - TARGET_REG_ARS = 18, - TARGET_REG_ORIG_R2 = 26, -}; - void elf_core_copy_regs(target_elf_gregset_t *r, const CPUS390XState *env) { - int i; - uint32_t *aregs; - - r->regs[TARGET_REG_PSWM] = tswapreg(env->psw.mask); - r->regs[TARGET_REG_PSWA] = tswapreg(env->psw.addr); - for (i = 0; i < 16; i++) { - r->regs[TARGET_REG_GPRS + i] = tswapreg(env->regs[i]); + r->pt.psw.mask = tswapal(env->psw.mask); + r->pt.psw.addr = tswapal(env->psw.addr); + for (int i = 0; i < 16; i++) { + r->pt.gprs[i] = tswapal(env->regs[i]); } - aregs = (uint32_t *)&(r->regs[TARGET_REG_ARS]); - for (i = 0; i < 16; i++) { - aregs[i] = tswap32(env->aregs[i]); + for (int i = 0; i < 16; i++) { + r->pt.acrs[i] = tswap32(env->aregs[i]); } - r->regs[TARGET_REG_ORIG_R2] = 0; + r->pt.orig_gpr2 = 0; } diff --git a/linux-user/s390x/target_elf.h b/linux-user/s390x/target_elf.h index b7d863ee66..670c7b3eed 100644 --- a/linux-user/s390x/target_elf.h +++ b/linux-user/s390x/target_elf.h @@ -8,15 +8,17 @@ #ifndef S390X_TARGET_ELF_H #define S390X_TARGET_ELF_H +#include "target_ptrace.h" + #define HAVE_ELF_HWCAP 1 #define HAVE_ELF_CORE_DUMP 1 -typedef abi_ulong target_elf_greg_t; - -/* See linux kernel: arch/s390/include/uapi/asm/ptrace.h (s390_regs). */ -#define ELF_NREG 27 +/* + * See linux kernel: arch/s390/include/asm/elf.h, where + * elf_gregset_t is typedef'd to struct s390_regs. + */ typedef struct target_elf_gregset_t { - target_elf_greg_t regs[ELF_NREG]; + struct target_s390_regs pt; } target_elf_gregset_t; #endif From ec12f80d1f2915bbd1c2734f7ce1dcbb3c0ff70b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Aug 2025 21:22:08 +1000 Subject: [PATCH 0193/2396] linux-user/sh4: Create target_ptrace.h Move target_pt_regs to target_ptrace.h. Convert to abi_foo types. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/sh4/target_ptrace.h | 18 ++++++++++++++++++ linux-user/sh4/target_syscall.h | 11 ----------- 2 files changed, 18 insertions(+), 11 deletions(-) create mode 100644 linux-user/sh4/target_ptrace.h diff --git a/linux-user/sh4/target_ptrace.h b/linux-user/sh4/target_ptrace.h new file mode 100644 index 0000000000..b80218526b --- /dev/null +++ b/linux-user/sh4/target_ptrace.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef SH4_TARGET_PTRACE_H +#define SH4_TARGET_PTRACE_H + +/* See arch/sh/include/uapi/asm/ptrace_32.h. */ +struct target_pt_regs { + abi_ulong regs[16]; + abi_ulong pc; + abi_ulong pr; + abi_ulong sr; + abi_ulong gbr; + abi_ulong mach; + abi_ulong macl; + abi_long tra; +}; + +#endif /* SH4_TARGET_PTRACE_H */ diff --git a/linux-user/sh4/target_syscall.h b/linux-user/sh4/target_syscall.h index 148398855d..2f3557742d 100644 --- a/linux-user/sh4/target_syscall.h +++ b/linux-user/sh4/target_syscall.h @@ -1,17 +1,6 @@ #ifndef SH4_TARGET_SYSCALL_H #define SH4_TARGET_SYSCALL_H -struct target_pt_regs { - unsigned long regs[16]; - unsigned long pc; - unsigned long pr; - unsigned long sr; - unsigned long gbr; - unsigned long mach; - unsigned long macl; - long tra; -}; - #define UNAME_MACHINE "sh4" #define UNAME_MINIMUM_RELEASE "2.6.32" From 92e1a92823eaa812451a9499754431b2002503ec Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Aug 2025 13:34:12 +1000 Subject: [PATCH 0194/2396] linux-user/sh4: Expand target_elf_gregset_t Make use of the fact that target_elf_gregset_t is a proper structure. Drop ELF_NREG, target_elf_greg_t, and tswapreg. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/sh4/elfload.c | 28 +++++++--------------------- linux-user/sh4/target_elf.h | 12 +++++++----- 2 files changed, 14 insertions(+), 26 deletions(-) diff --git a/linux-user/sh4/elfload.c b/linux-user/sh4/elfload.c index 71cae9703e..ddf2aaaed7 100644 --- a/linux-user/sh4/elfload.c +++ b/linux-user/sh4/elfload.c @@ -38,30 +38,16 @@ abi_ulong get_elf_hwcap(CPUState *cs) return hwcap; } -#define tswapreg(ptr) tswapal(ptr) - -/* See linux kernel: arch/sh/include/asm/ptrace.h. */ -enum { - TARGET_REG_PC = 16, - TARGET_REG_PR = 17, - TARGET_REG_SR = 18, - TARGET_REG_GBR = 19, - TARGET_REG_MACH = 20, - TARGET_REG_MACL = 21, - TARGET_REG_SYSCALL = 22 -}; - void elf_core_copy_regs(target_elf_gregset_t *r, const CPUSH4State *env) { for (int i = 0; i < 16; i++) { - r->regs[i] = tswapreg(env->gregs[i]); + r->pt.regs[i] = tswapal(env->gregs[i]); } - r->regs[TARGET_REG_PC] = tswapreg(env->pc); - r->regs[TARGET_REG_PR] = tswapreg(env->pr); - r->regs[TARGET_REG_SR] = tswapreg(env->sr); - r->regs[TARGET_REG_GBR] = tswapreg(env->gbr); - r->regs[TARGET_REG_MACH] = tswapreg(env->mach); - r->regs[TARGET_REG_MACL] = tswapreg(env->macl); - r->regs[TARGET_REG_SYSCALL] = 0; /* FIXME */ + r->pt.pc = tswapal(env->pc); + r->pt.pr = tswapal(env->pr); + r->pt.sr = tswapal(env->sr); + r->pt.gbr = tswapal(env->gbr); + r->pt.mach = tswapal(env->mach); + r->pt.macl = tswapal(env->macl); } diff --git a/linux-user/sh4/target_elf.h b/linux-user/sh4/target_elf.h index f7443ddbac..fd3ae68a01 100644 --- a/linux-user/sh4/target_elf.h +++ b/linux-user/sh4/target_elf.h @@ -8,15 +8,17 @@ #ifndef SH4_TARGET_ELF_H #define SH4_TARGET_ELF_H +#include "target_ptrace.h" + #define HAVE_ELF_HWCAP 1 #define HAVE_ELF_CORE_DUMP 1 -typedef abi_ulong target_elf_greg_t; - -/* See linux kernel: arch/sh/include/asm/elf.h. */ -#define ELF_NREG 23 +/* + * See linux kernel: arch/sh/include/asm/elf.h, where + * elf_gregset_t is mapped to struct pt_regs via sizeof. + */ typedef struct target_elf_gregset_t { - target_elf_greg_t regs[ELF_NREG]; + struct target_pt_regs pt; } target_elf_gregset_t; #endif From b90e36861636fc5f021fd2a0676087d076cd0cc9 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Aug 2025 21:35:33 +1000 Subject: [PATCH 0195/2396] linux-user/xtensa: Create target_ptrace.h Remove the target_pt_regs; add target_user_pt_regs to target_ptrace.h, which matches what is actually used. Remove xtensa_reg_t and xtregs_opt_t. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/xtensa/target_ptrace.h | 22 +++++++++++++++++++ linux-user/xtensa/target_syscall.h | 35 ------------------------------ 2 files changed, 22 insertions(+), 35 deletions(-) create mode 100644 linux-user/xtensa/target_ptrace.h diff --git a/linux-user/xtensa/target_ptrace.h b/linux-user/xtensa/target_ptrace.h new file mode 100644 index 0000000000..32443d0dee --- /dev/null +++ b/linux-user/xtensa/target_ptrace.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef XTENSA_TARGET_PTRACE_H +#define XTENSA_TARGET_PTRACE_H + +/* See arch/xtensa/include/uapi/asm/ptrace.h. */ +struct target_user_pt_regs { + uint32_t pc; + uint32_t ps; + uint32_t lbeg; + uint32_t lend; + uint32_t lcount; + uint32_t sar; + uint32_t windowstart; + uint32_t windowbase; + uint32_t threadptr; + uint32_t syscall; + uint32_t reserved[6 + 48]; + uint32_t a[64]; +}; + +#endif /* XTENSA_TARGET_PTRACE_H */ diff --git a/linux-user/xtensa/target_syscall.h b/linux-user/xtensa/target_syscall.h index afc86a153f..5d4352a4d1 100644 --- a/linux-user/xtensa/target_syscall.h +++ b/linux-user/xtensa/target_syscall.h @@ -8,41 +8,6 @@ #define MMAP_SHIFT TARGET_PAGE_BITS -typedef uint32_t xtensa_reg_t; -typedef struct { -} xtregs_opt_t; /* TODO */ - -struct target_pt_regs { - xtensa_reg_t pc; /* 4 */ - xtensa_reg_t ps; /* 8 */ - xtensa_reg_t depc; /* 12 */ - xtensa_reg_t exccause; /* 16 */ - xtensa_reg_t excvaddr; /* 20 */ - xtensa_reg_t debugcause; /* 24 */ - xtensa_reg_t wmask; /* 28 */ - xtensa_reg_t lbeg; /* 32 */ - xtensa_reg_t lend; /* 36 */ - xtensa_reg_t lcount; /* 40 */ - xtensa_reg_t sar; /* 44 */ - xtensa_reg_t windowbase; /* 48 */ - xtensa_reg_t windowstart; /* 52 */ - xtensa_reg_t syscall; /* 56 */ - xtensa_reg_t icountlevel; /* 60 */ - xtensa_reg_t scompare1; /* 64 */ - xtensa_reg_t threadptr; /* 68 */ - - /* Additional configurable registers that are used by the compiler. */ - xtregs_opt_t xtregs_opt; - - /* Make sure the areg field is 16 bytes aligned. */ - int align[0] __attribute__ ((aligned(16))); - - /* current register frame. - * Note: The ESF for kernel exceptions ends after 16 registers! - */ - xtensa_reg_t areg[16]; -}; - #define TARGET_MCL_CURRENT 1 #define TARGET_MCL_FUTURE 2 #define TARGET_MCL_ONFAULT 4 From abfa6c7c2a0ddc4eaf373de320d758435e7acf9b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Aug 2025 13:44:40 +1000 Subject: [PATCH 0196/2396] linux-user/xtensa: Expand target_elf_gregset_t Make use of the fact that target_elf_gregset_t is a proper structure. Drop ELF_NREG, target_elf_greg_t, and tswapreg. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/xtensa/elfload.c | 39 +++++++++++----------------------- linux-user/xtensa/target_elf.h | 12 ++++++----- 2 files changed, 19 insertions(+), 32 deletions(-) diff --git a/linux-user/xtensa/elfload.c b/linux-user/xtensa/elfload.c index 49e709a094..68aeed855f 100644 --- a/linux-user/xtensa/elfload.c +++ b/linux-user/xtensa/elfload.c @@ -11,36 +11,21 @@ const char *get_elf_cpu_model(uint32_t eflags) return XTENSA_DEFAULT_CPU_MODEL; } -#define tswapreg(ptr) tswapal(ptr) - -enum { - TARGET_REG_PC, - TARGET_REG_PS, - TARGET_REG_LBEG, - TARGET_REG_LEND, - TARGET_REG_LCOUNT, - TARGET_REG_SAR, - TARGET_REG_WINDOWSTART, - TARGET_REG_WINDOWBASE, - TARGET_REG_THREADPTR, - TARGET_REG_AR0 = 64, -}; - void elf_core_copy_regs(target_elf_gregset_t *r, const CPUXtensaState *env) { - unsigned i; + r->pt.pc = tswap32(env->pc); + r->pt.ps = tswap32(env->sregs[PS] & ~PS_EXCM); + r->pt.lbeg = tswap32(env->sregs[LBEG]); + r->pt.lend = tswap32(env->sregs[LEND]); + r->pt.lcount = tswap32(env->sregs[LCOUNT]); + r->pt.sar = tswap32(env->sregs[SAR]); + r->pt.windowstart = tswap32(env->sregs[WINDOW_START]); + r->pt.windowbase = tswap32(env->sregs[WINDOW_BASE]); + r->pt.threadptr = tswap32(env->uregs[THREADPTR]); - r->regs[TARGET_REG_PC] = tswapreg(env->pc); - r->regs[TARGET_REG_PS] = tswapreg(env->sregs[PS] & ~PS_EXCM); - r->regs[TARGET_REG_LBEG] = tswapreg(env->sregs[LBEG]); - r->regs[TARGET_REG_LEND] = tswapreg(env->sregs[LEND]); - r->regs[TARGET_REG_LCOUNT] = tswapreg(env->sregs[LCOUNT]); - r->regs[TARGET_REG_SAR] = tswapreg(env->sregs[SAR]); - r->regs[TARGET_REG_WINDOWSTART] = tswapreg(env->sregs[WINDOW_START]); - r->regs[TARGET_REG_WINDOWBASE] = tswapreg(env->sregs[WINDOW_BASE]); - r->regs[TARGET_REG_THREADPTR] = tswapreg(env->uregs[THREADPTR]); xtensa_sync_phys_from_window((CPUXtensaState *)env); - for (i = 0; i < env->config->nareg; ++i) { - r->regs[TARGET_REG_AR0 + i] = tswapreg(env->phys_regs[i]); + + for (unsigned i = 0; i < env->config->nareg; ++i) { + r->pt.a[i] = tswap32(env->phys_regs[i]); } } diff --git a/linux-user/xtensa/target_elf.h b/linux-user/xtensa/target_elf.h index 43e241aac1..850a7206a5 100644 --- a/linux-user/xtensa/target_elf.h +++ b/linux-user/xtensa/target_elf.h @@ -8,14 +8,16 @@ #ifndef XTENSA_TARGET_ELF_H #define XTENSA_TARGET_ELF_H +#include "target_ptrace.h" + #define HAVE_ELF_CORE_DUMP 1 -typedef abi_ulong target_elf_greg_t; - -/* See linux kernel: arch/xtensa/include/asm/elf.h. */ -#define ELF_NREG 128 +/* + * See linux kernel: arch/xtensa/include/asm/elf.h, where elf_gregset_t + * is mapped to struct user_pt_regs via typedef and sizeof. + */ typedef struct target_elf_gregset_t { - target_elf_greg_t regs[ELF_NREG]; + struct target_user_pt_regs pt; } target_elf_gregset_t; #endif From 8d4a6f8e4c95e11a3da2e38682da2819d4e2160c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 29 Jul 2025 09:06:15 -1000 Subject: [PATCH 0197/2396] linux-user: Move init_guest_commpage to x86_64/elfload.c Rename INIT_GUEST_COMMPAGE to HAVE_GUEST_COMMPAGE to match the other HAVE_* defines. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 23 +---------------------- linux-user/loader.h | 3 +++ linux-user/x86_64/elfload.c | 20 ++++++++++++++++++++ linux-user/x86_64/target_elf.h | 1 + 4 files changed, 25 insertions(+), 22 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 07d83c674d..0ba75a83b3 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -145,27 +145,6 @@ typedef abi_int target_pid_t; #define ELF_CLASS ELFCLASS64 #define ELF_ARCH EM_X86_64 -#if ULONG_MAX > UINT32_MAX -#define INIT_GUEST_COMMPAGE -static bool init_guest_commpage(void) -{ - /* - * The vsyscall page is at a high negative address aka kernel space, - * which means that we cannot actually allocate it with target_mmap. - * We still should be able to use page_set_flags, unless the user - * has specified -R reserved_va, which would trigger an assert(). - */ - if (reserved_va != 0 && - TARGET_VSYSCALL_PAGE + TARGET_PAGE_SIZE - 1 > reserved_va) { - error_report("Cannot allocate vsyscall page"); - exit(EXIT_FAILURE); - } - page_set_flags(TARGET_VSYSCALL_PAGE, - TARGET_VSYSCALL_PAGE | ~TARGET_PAGE_MASK, - PAGE_EXEC | PAGE_VALID); - return true; -} -#endif #else /* @@ -1215,7 +1194,7 @@ static abi_ulong create_elf_tables(abi_ulong p, int argc, int envc, #else #define HI_COMMPAGE 0 #define LO_COMMPAGE -1 -#ifndef INIT_GUEST_COMMPAGE +#ifndef HAVE_GUEST_COMMPAGE #define init_guest_commpage() true #endif #endif diff --git a/linux-user/loader.h b/linux-user/loader.h index 8f4a7f69ac..98015fba7d 100644 --- a/linux-user/loader.h +++ b/linux-user/loader.h @@ -105,6 +105,9 @@ const char *elf_hwcap_str(uint32_t bit); const char *elf_hwcap2_str(uint32_t bit); const char *get_elf_platform(CPUState *cs); const char *get_elf_base_platform(CPUState *cs); +#if defined(TARGET_X86_64) +bool init_guest_commpage(void); +#endif struct target_elf_gregset_t; void elf_core_copy_regs(struct target_elf_gregset_t *, const CPUArchState *); diff --git a/linux-user/x86_64/elfload.c b/linux-user/x86_64/elfload.c index 12de1c54c7..1e7000c6bc 100644 --- a/linux-user/x86_64/elfload.c +++ b/linux-user/x86_64/elfload.c @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ #include "qemu/osdep.h" +#include "qemu/error-report.h" #include "qemu.h" #include "loader.h" #include "target_elf.h" @@ -21,6 +22,25 @@ const char *get_elf_platform(CPUState *cs) return "x86_64"; } +bool init_guest_commpage(void) +{ + /* + * The vsyscall page is at a high negative address aka kernel space, + * which means that we cannot actually allocate it with target_mmap. + * We still should be able to use page_set_flags, unless the user + * has specified -R reserved_va, which would trigger an assert(). + */ + if (reserved_va != 0 && + TARGET_VSYSCALL_PAGE + TARGET_PAGE_SIZE - 1 > reserved_va) { + error_report("Cannot allocate vsyscall page"); + exit(EXIT_FAILURE); + } + page_set_flags(TARGET_VSYSCALL_PAGE, + TARGET_VSYSCALL_PAGE | ~TARGET_PAGE_MASK, + PAGE_EXEC | PAGE_VALID); + return true; +} + void elf_core_copy_regs(target_elf_gregset_t *r, const CPUX86State *env) { r->pt.r15 = tswapal(env->regs[15]); diff --git a/linux-user/x86_64/target_elf.h b/linux-user/x86_64/target_elf.h index 32a9eec431..f05b1d4dba 100644 --- a/linux-user/x86_64/target_elf.h +++ b/linux-user/x86_64/target_elf.h @@ -13,6 +13,7 @@ #define HAVE_ELF_HWCAP 1 #define HAVE_ELF_PLATFORM 1 #define HAVE_ELF_CORE_DUMP 1 +#define HAVE_GUEST_COMMPAGE 1 /* * See linux kernel: arch/x86/include/asm/elf.h, where From a56ef5e8454e42c0d263a97a1297ae67f4ce19cd Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 29 Jul 2025 09:11:12 -1000 Subject: [PATCH 0198/2396] linux-user: Move init_guest_commpage to arm/elfload.c Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/arm/elfload.c | 46 +++++++++++++++++++++++++++++++++++ linux-user/arm/target_elf.h | 2 ++ linux-user/elfload.c | 48 ------------------------------------- linux-user/loader.h | 2 +- 4 files changed, 49 insertions(+), 49 deletions(-) diff --git a/linux-user/arm/elfload.c b/linux-user/arm/elfload.c index f811c2f07a..1205687976 100644 --- a/linux-user/arm/elfload.c +++ b/linux-user/arm/elfload.c @@ -3,6 +3,8 @@ #include "qemu/osdep.h" #include "qemu.h" #include "loader.h" +#include "user-internals.h" +#include "target_elf.h" #include "target/arm/cpu-features.h" #include "target_elf.h" @@ -201,6 +203,50 @@ const char *get_elf_platform(CPUState *cs) #undef END } +bool init_guest_commpage(void) +{ + ARMCPU *cpu = ARM_CPU(thread_cpu); + int host_page_size = qemu_real_host_page_size(); + abi_ptr commpage; + void *want; + void *addr; + + /* + * M-profile allocates maximum of 2GB address space, so can never + * allocate the commpage. Skip it. + */ + if (arm_feature(&cpu->env, ARM_FEATURE_M)) { + return true; + } + + commpage = HI_COMMPAGE & -host_page_size; + want = g2h_untagged(commpage); + addr = mmap(want, host_page_size, PROT_READ | PROT_WRITE, + MAP_ANONYMOUS | MAP_PRIVATE | + (commpage < reserved_va ? MAP_FIXED : MAP_FIXED_NOREPLACE), + -1, 0); + + if (addr == MAP_FAILED) { + perror("Allocating guest commpage"); + exit(EXIT_FAILURE); + } + if (addr != want) { + return false; + } + + /* Set kernel helper versions; rest of page is 0. */ + __put_user(5, (uint32_t *)g2h_untagged(0xffff0ffcu)); + + if (mprotect(addr, host_page_size, PROT_READ)) { + perror("Protecting guest commpage"); + exit(EXIT_FAILURE); + } + + page_set_flags(commpage, commpage | (host_page_size - 1), + PAGE_READ | PAGE_EXEC | PAGE_VALID); + return true; +} + void elf_core_copy_regs(target_elf_gregset_t *r, const CPUARMState *env) { for (int i = 0; i < 16; ++i) { diff --git a/linux-user/arm/target_elf.h b/linux-user/arm/target_elf.h index fa8f8af2f3..5f81a43efb 100644 --- a/linux-user/arm/target_elf.h +++ b/linux-user/arm/target_elf.h @@ -15,6 +15,8 @@ #define HAVE_ELF_PLATFORM 1 #define HAVE_ELF_CORE_DUMP 1 +#define HI_COMMPAGE ((intptr_t)0xffff0f00u) + /* * See linux kernel: arch/arm/include/asm/elf.h, where * elf_gregset_t is mapped to struct pt_regs via sizeof. diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 0ba75a83b3..2281853c57 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -191,54 +191,6 @@ typedef abi_int target_pid_t; #define ELF_EXEC_PAGESIZE 4096 -/* The commpage only exists for 32 bit kernels */ - -#define HI_COMMPAGE (intptr_t)0xffff0f00u - -static bool init_guest_commpage(void) -{ - ARMCPU *cpu = ARM_CPU(thread_cpu); - int host_page_size = qemu_real_host_page_size(); - abi_ptr commpage; - void *want; - void *addr; - - /* - * M-profile allocates maximum of 2GB address space, so can never - * allocate the commpage. Skip it. - */ - if (arm_feature(&cpu->env, ARM_FEATURE_M)) { - return true; - } - - commpage = HI_COMMPAGE & -host_page_size; - want = g2h_untagged(commpage); - addr = mmap(want, host_page_size, PROT_READ | PROT_WRITE, - MAP_ANONYMOUS | MAP_PRIVATE | - (commpage < reserved_va ? MAP_FIXED : MAP_FIXED_NOREPLACE), - -1, 0); - - if (addr == MAP_FAILED) { - perror("Allocating guest commpage"); - exit(EXIT_FAILURE); - } - if (addr != want) { - return false; - } - - /* Set kernel helper versions; rest of page is 0. */ - __put_user(5, (uint32_t *)g2h_untagged(0xffff0ffcu)); - - if (mprotect(addr, host_page_size, PROT_READ)) { - perror("Protecting guest commpage"); - exit(EXIT_FAILURE); - } - - page_set_flags(commpage, commpage | (host_page_size - 1), - PAGE_READ | PAGE_EXEC | PAGE_VALID); - return true; -} - #if TARGET_BIG_ENDIAN #include "elf.h" #include "vdso-be8.c.inc" diff --git a/linux-user/loader.h b/linux-user/loader.h index 98015fba7d..0c2cc556c3 100644 --- a/linux-user/loader.h +++ b/linux-user/loader.h @@ -105,7 +105,7 @@ const char *elf_hwcap_str(uint32_t bit); const char *elf_hwcap2_str(uint32_t bit); const char *get_elf_platform(CPUState *cs); const char *get_elf_base_platform(CPUState *cs); -#if defined(TARGET_X86_64) +#if defined(TARGET_X86_64) || defined(TARGET_ARM) bool init_guest_commpage(void); #endif From 6a91f64ee12fe54d1a9573e9ecbbae037b113097 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 29 Jul 2025 09:13:54 -1000 Subject: [PATCH 0199/2396] linux-user: Move init_guest_commpage to hppa/elfload.c Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 32 -------------------------------- linux-user/hppa/elfload.c | 31 +++++++++++++++++++++++++++++++ linux-user/hppa/target_elf.h | 2 ++ linux-user/loader.h | 2 -- 4 files changed, 33 insertions(+), 34 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 2281853c57..25f29e60de 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -415,38 +415,6 @@ static const VdsoImageInfo *vdso_image_info(uint32_t elf_flags) #define VDSO_HEADER "vdso.c.inc" -#define LO_COMMPAGE 0 - -static bool init_guest_commpage(void) -{ - /* If reserved_va, then we have already mapped 0 page on the host. */ - if (!reserved_va) { - void *want, *addr; - - want = g2h_untagged(LO_COMMPAGE); - addr = mmap(want, TARGET_PAGE_SIZE, PROT_NONE, - MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED_NOREPLACE, -1, 0); - if (addr == MAP_FAILED) { - perror("Allocating guest commpage"); - exit(EXIT_FAILURE); - } - if (addr != want) { - return false; - } - } - - /* - * On Linux, page zero is normally marked execute only + gateway. - * Normal read or write is supposed to fail (thus PROT_NONE above), - * but specific offsets have kernel code mapped to raise permissions - * and implement syscalls. Here, simply mark the page executable. - * Special case the entry points during translation (see do_page_zero). - */ - page_set_flags(LO_COMMPAGE, LO_COMMPAGE | ~TARGET_PAGE_MASK, - PAGE_EXEC | PAGE_VALID); - return true; -} - #endif /* TARGET_HPPA */ #ifdef TARGET_XTENSA diff --git a/linux-user/hppa/elfload.c b/linux-user/hppa/elfload.c index 9dd3fe092a..018034f244 100644 --- a/linux-user/hppa/elfload.c +++ b/linux-user/hppa/elfload.c @@ -3,6 +3,7 @@ #include "qemu/osdep.h" #include "qemu.h" #include "loader.h" +#include "target_elf.h" const char *get_elf_cpu_model(uint32_t eflags) @@ -14,3 +15,33 @@ const char *get_elf_platform(CPUState *cs) { return "PARISC"; } + +bool init_guest_commpage(void) +{ + /* If reserved_va, then we have already mapped 0 page on the host. */ + if (!reserved_va) { + void *want, *addr; + + want = g2h_untagged(LO_COMMPAGE); + addr = mmap(want, TARGET_PAGE_SIZE, PROT_NONE, + MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED_NOREPLACE, -1, 0); + if (addr == MAP_FAILED) { + perror("Allocating guest commpage"); + exit(EXIT_FAILURE); + } + if (addr != want) { + return false; + } + } + + /* + * On Linux, page zero is normally marked execute only + gateway. + * Normal read or write is supposed to fail (thus PROT_NONE above), + * but specific offsets have kernel code mapped to raise permissions + * and implement syscalls. Here, simply mark the page executable. + * Special case the entry points during translation (see do_page_zero). + */ + page_set_flags(LO_COMMPAGE, LO_COMMPAGE | ~TARGET_PAGE_MASK, + PAGE_EXEC | PAGE_VALID); + return true; +} diff --git a/linux-user/hppa/target_elf.h b/linux-user/hppa/target_elf.h index 85be00584d..b654758afa 100644 --- a/linux-user/hppa/target_elf.h +++ b/linux-user/hppa/target_elf.h @@ -10,4 +10,6 @@ #define HAVE_ELF_PLATFORM 1 +#define LO_COMMPAGE 0 + #endif diff --git a/linux-user/loader.h b/linux-user/loader.h index 0c2cc556c3..c3b8f92e23 100644 --- a/linux-user/loader.h +++ b/linux-user/loader.h @@ -105,9 +105,7 @@ const char *elf_hwcap_str(uint32_t bit); const char *elf_hwcap2_str(uint32_t bit); const char *get_elf_platform(CPUState *cs); const char *get_elf_base_platform(CPUState *cs); -#if defined(TARGET_X86_64) || defined(TARGET_ARM) bool init_guest_commpage(void); -#endif struct target_elf_gregset_t; void elf_core_copy_regs(struct target_elf_gregset_t *, const CPUArchState *); From 6e1c4ec4582814537c9a2b4700ff32da44fb27af Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 29 Jul 2025 09:16:37 -1000 Subject: [PATCH 0200/2396] linux-user: Replace init_guest_commpage macro with function Turn the fallback macro into a function. This will produce a link error if the other macros are set up incorrectly. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 25f29e60de..81bf05f581 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -1115,7 +1115,7 @@ static abi_ulong create_elf_tables(abi_ulong p, int argc, int envc, #define HI_COMMPAGE 0 #define LO_COMMPAGE -1 #ifndef HAVE_GUEST_COMMPAGE -#define init_guest_commpage() true +bool init_guest_commpage(void) { return true; } #endif #endif From 71cc79a4a172d28638ad27a4e6327a4ce1bcdf2b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 29 Jul 2025 09:33:51 -1000 Subject: [PATCH 0201/2396] linux-user: Move get_vdso_image_info to arm/elfload.c Rename from vdso_image_info to avoid a symbol clash. Define HAVE_VDSO_IMAGE_INFO to signal the external definition exists. Provide fallback versions for other targets. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/arm/elfload.c | 20 ++++++++++++++++++ linux-user/arm/target_elf.h | 1 + linux-user/elfload.c | 41 ++++++++----------------------------- linux-user/loader.h | 12 +++++++++++ 4 files changed, 42 insertions(+), 32 deletions(-) diff --git a/linux-user/arm/elfload.c b/linux-user/arm/elfload.c index 1205687976..308ed23fcb 100644 --- a/linux-user/arm/elfload.c +++ b/linux-user/arm/elfload.c @@ -7,6 +7,7 @@ #include "target_elf.h" #include "target/arm/cpu-features.h" #include "target_elf.h" +#include "elf.h" const char *get_elf_cpu_model(uint32_t eflags) @@ -255,3 +256,22 @@ void elf_core_copy_regs(target_elf_gregset_t *r, const CPUARMState *env) r->pt.cpsr = tswapal(cpsr_read((CPUARMState *)env)); r->pt.orig_r0 = tswapal(env->regs[0]); /* FIXME */ } + +#if TARGET_BIG_ENDIAN +# include "vdso-be8.c.inc" +# include "vdso-be32.c.inc" +#else +# include "vdso-le.c.inc" +#endif + +const VdsoImageInfo *get_vdso_image_info(uint32_t elf_flags) +{ +#if TARGET_BIG_ENDIAN + return (EF_ARM_EABI_VERSION(elf_flags) >= EF_ARM_EABI_VER4 + && (elf_flags & EF_ARM_BE8) + ? &vdso_be8_image_info + : &vdso_be32_image_info); +#else + return &vdso_image_info; +#endif +} diff --git a/linux-user/arm/target_elf.h b/linux-user/arm/target_elf.h index 5f81a43efb..19fdfa2f2c 100644 --- a/linux-user/arm/target_elf.h +++ b/linux-user/arm/target_elf.h @@ -14,6 +14,7 @@ #define HAVE_ELF_HWCAP2 1 #define HAVE_ELF_PLATFORM 1 #define HAVE_ELF_CORE_DUMP 1 +#define HAVE_VDSO_IMAGE_INFO 1 #define HI_COMMPAGE ((intptr_t)0xffff0f00u) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 81bf05f581..aed390ebb3 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -40,15 +40,6 @@ #define TARGET_ARCH_HAS_SIGTRAMP_PAGE 0 #endif -typedef struct { - const uint8_t *image; - const uint32_t *relocs; - unsigned image_size; - unsigned reloc_count; - unsigned sigreturn_ofs; - unsigned rt_sigreturn_ofs; -} VdsoImageInfo; - #define ELF_OSABI ELFOSABI_SYSV /* from personality.h */ @@ -191,23 +182,6 @@ typedef abi_int target_pid_t; #define ELF_EXEC_PAGESIZE 4096 -#if TARGET_BIG_ENDIAN -#include "elf.h" -#include "vdso-be8.c.inc" -#include "vdso-be32.c.inc" - -static const VdsoImageInfo *vdso_image_info(uint32_t elf_flags) -{ - return (EF_ARM_EABI_VERSION(elf_flags) >= EF_ARM_EABI_VER4 - && (elf_flags & EF_ARM_BE8) - ? &vdso_be8_image_info - : &vdso_be32_image_info); -} -#define vdso_image_info vdso_image_info -#else -# define VDSO_HEADER "vdso-le.c.inc" -#endif - #else /* 64 bit ARM definitions */ @@ -1973,14 +1947,17 @@ static void load_elf_interp(const char *filename, struct image_info *info, load_elf_image(filename, &src, info, &ehdr, NULL); } -#ifndef vdso_image_info +#ifndef HAVE_VDSO_IMAGE_INFO +const VdsoImageInfo *get_vdso_image_info(uint32_t elf_flags) +{ #ifdef VDSO_HEADER #include VDSO_HEADER -#define vdso_image_info(flags) &vdso_image_info + return &vdso_image_info; #else -#define vdso_image_info(flags) NULL -#endif /* VDSO_HEADER */ -#endif /* vdso_image_info */ + return NULL; +#endif +} +#endif /* HAVE_VDSO_IMAGE_INFO */ static void load_elf_vdso(struct image_info *info, const VdsoImageInfo *vdso) { @@ -2311,7 +2288,7 @@ int load_elf_binary(struct linux_binprm *bprm, struct image_info *info) * Load a vdso if available, which will amongst other things contain the * signal trampolines. Otherwise, allocate a separate page for them. */ - const VdsoImageInfo *vdso = vdso_image_info(info->elf_flags); + const VdsoImageInfo *vdso = get_vdso_image_info(info->elf_flags); if (vdso) { load_elf_vdso(&vdso_info, vdso); info->vdso = vdso_info.load_bias; diff --git a/linux-user/loader.h b/linux-user/loader.h index c3b8f92e23..2175dd4e0a 100644 --- a/linux-user/loader.h +++ b/linux-user/loader.h @@ -110,4 +110,16 @@ bool init_guest_commpage(void); struct target_elf_gregset_t; void elf_core_copy_regs(struct target_elf_gregset_t *, const CPUArchState *); +typedef struct { + const uint8_t *image; + const uint32_t *relocs; + unsigned image_size; + unsigned reloc_count; + unsigned sigreturn_ofs; + unsigned rt_sigreturn_ofs; +} VdsoImageInfo; + +/* Note that both Elf32_Word and Elf64_Word are uint32_t. */ +const VdsoImageInfo *get_vdso_image_info(uint32_t elf_flags); + #endif /* LINUX_USER_LOADER_H */ From 9f24d077f1ea76ede5968d68312840c909751b73 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 29 Jul 2025 09:40:47 -1000 Subject: [PATCH 0202/2396] linux-user: Remove ELF_EXEC_PAGESIZE Use TARGET_PAGE_SIZE instead. If the target page size may vary, using a different fixed size is wrong. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 32 ++------------------------------ 1 file changed, 2 insertions(+), 30 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index aed390ebb3..59e6605e36 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -167,8 +167,6 @@ typedef abi_int target_pid_t; #define VDSO_HEADER "vdso.c.inc" -#define ELF_EXEC_PAGESIZE 4096 - #endif /* TARGET_I386 */ #ifdef TARGET_ARM @@ -180,16 +178,12 @@ typedef abi_int target_pid_t; #define ELF_CLASS ELFCLASS32 #define EXSTACK_DEFAULT true -#define ELF_EXEC_PAGESIZE 4096 - #else /* 64 bit ARM definitions */ #define ELF_ARCH EM_AARCH64 #define ELF_CLASS ELFCLASS64 -#define ELF_EXEC_PAGESIZE 4096 - #if TARGET_BIG_ENDIAN # define VDSO_HEADER "vdso-be.c.inc" #else @@ -258,8 +252,6 @@ typedef abi_int target_pid_t; NEW_AUX_ENT(AT_UCACHEBSIZE, 0); \ } while (0) -#define ELF_EXEC_PAGESIZE 4096 - #ifndef TARGET_PPC64 # define VDSO_HEADER "vdso-32.c.inc" #elif TARGET_BIG_ENDIAN @@ -280,8 +272,6 @@ typedef abi_int target_pid_t; #define VDSO_HEADER "vdso.c.inc" -#define ELF_EXEC_PAGESIZE 4096 - #endif /* TARGET_LOONGARCH64 */ #ifdef TARGET_MIPS @@ -300,8 +290,6 @@ typedef abi_int target_pid_t; #define elf_check_abi(x) (!((x) & EF_MIPS_ABI2)) #endif -#define ELF_EXEC_PAGESIZE 4096 - #endif /* TARGET_MIPS */ #ifdef TARGET_MICROBLAZE @@ -311,8 +299,6 @@ typedef abi_int target_pid_t; #define ELF_CLASS ELFCLASS32 #define ELF_ARCH EM_MICROBLAZE -#define ELF_EXEC_PAGESIZE 4096 - #endif /* TARGET_MICROBLAZE */ #ifdef TARGET_OPENRISC @@ -321,8 +307,6 @@ typedef abi_int target_pid_t; #define ELF_CLASS ELFCLASS32 #define ELF_DATA ELFDATA2MSB -#define ELF_EXEC_PAGESIZE 8192 - #endif /* TARGET_OPENRISC */ #ifdef TARGET_SH4 @@ -330,8 +314,6 @@ typedef abi_int target_pid_t; #define ELF_CLASS ELFCLASS32 #define ELF_ARCH EM_SH -#define ELF_EXEC_PAGESIZE 4096 - #endif #ifdef TARGET_M68K @@ -339,8 +321,6 @@ typedef abi_int target_pid_t; #define ELF_CLASS ELFCLASS32 #define ELF_ARCH EM_68K -#define ELF_EXEC_PAGESIZE 8192 - #endif #ifdef TARGET_ALPHA @@ -348,8 +328,6 @@ typedef abi_int target_pid_t; #define ELF_CLASS ELFCLASS64 #define ELF_ARCH EM_ALPHA -#define ELF_EXEC_PAGESIZE 8192 - #endif /* TARGET_ALPHA */ #ifdef TARGET_S390X @@ -358,8 +336,6 @@ typedef abi_int target_pid_t; #define ELF_DATA ELFDATA2MSB #define ELF_ARCH EM_S390 -#define ELF_EXEC_PAGESIZE 4096 - #define VDSO_HEADER "vdso.c.inc" #endif /* TARGET_S390X */ @@ -376,8 +352,6 @@ typedef abi_int target_pid_t; #define VDSO_HEADER "vdso-64.c.inc" #endif -#define ELF_EXEC_PAGESIZE 4096 - #endif /* TARGET_RISCV */ #ifdef TARGET_HPPA @@ -396,8 +370,6 @@ typedef abi_int target_pid_t; #define ELF_CLASS ELFCLASS32 #define ELF_ARCH EM_XTENSA -#define ELF_EXEC_PAGESIZE 4096 - #endif /* TARGET_XTENSA */ #ifdef TARGET_HEXAGON @@ -2697,7 +2669,7 @@ static int wmr_fill_region_phdr(void *opaque, vaddr start, phdr->p_flags = (flags & PAGE_READ ? PF_R : 0) | (flags & PAGE_WRITE_ORG ? PF_W : 0) | (flags & PAGE_EXEC ? PF_X : 0); - phdr->p_align = ELF_EXEC_PAGESIZE; + phdr->p_align = TARGET_PAGE_SIZE; bswap_phdr(phdr, 1); d->phdr = phdr + 1; @@ -2805,7 +2777,7 @@ static int elf_core_dump(int signr, const CPUArchState *env) offset += size_note("CORE", sizeof(struct target_elf_prpsinfo)); offset += size_note("CORE", sizeof(struct target_elf_prstatus)) * cpus; note_size = offset - note_offset; - data_offset = ROUND_UP(offset, ELF_EXEC_PAGESIZE); + data_offset = TARGET_PAGE_ALIGN(offset); /* Do not dump if the corefile size exceeds the limit. */ if (dumpsize.rlim_cur != RLIM_INFINITY From 793ca839186df6cc9dda25121932a25c7d0ff366 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 3 Aug 2025 07:21:46 +1000 Subject: [PATCH 0203/2396] linux-user: Remove redundant ELF_DATA definitons We already provide ELF_DATA based on TARGET_BIG_ENDIAN. Remove the extra definitions from openrisc and s390x. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 59e6605e36..8ff9f83bb8 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -305,7 +305,6 @@ typedef abi_int target_pid_t; #define ELF_ARCH EM_OPENRISC #define ELF_CLASS ELFCLASS32 -#define ELF_DATA ELFDATA2MSB #endif /* TARGET_OPENRISC */ @@ -333,7 +332,6 @@ typedef abi_int target_pid_t; #ifdef TARGET_S390X #define ELF_CLASS ELFCLASS64 -#define ELF_DATA ELFDATA2MSB #define ELF_ARCH EM_S390 #define VDSO_HEADER "vdso.c.inc" From 73addb3ffc2c2252a9ffc55e56d9ed88d1dccd84 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 29 Jul 2025 10:14:24 -1000 Subject: [PATCH 0204/2396] linux-user: Move elf parameters to {i386,x86_64}/target_elf.h Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 39 ---------------------------------- linux-user/i386/target_elf.h | 22 +++++++++++++++++++ linux-user/x86_64/target_elf.h | 4 ++++ 3 files changed, 26 insertions(+), 39 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 8ff9f83bb8..73ca6c681e 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -130,45 +130,6 @@ typedef abi_uint target_gid_t; #endif typedef abi_int target_pid_t; -#ifdef TARGET_I386 - -#ifdef TARGET_X86_64 -#define ELF_CLASS ELFCLASS64 -#define ELF_ARCH EM_X86_64 - -#else - -/* - * This is used to ensure we don't load something for the wrong architecture. - */ -#define elf_check_arch(x) ( ((x) == EM_386) || ((x) == EM_486) ) - -/* - * These are used to set parameters in the core dumps. - */ -#define ELF_CLASS ELFCLASS32 -#define ELF_ARCH EM_386 - -#define EXSTACK_DEFAULT true - -/* - * i386 is the only target which supplies AT_SYSINFO for the vdso. - * All others only supply AT_SYSINFO_EHDR. - */ -#define DLINFO_ARCH_ITEMS (vdso_info != NULL) -#define ARCH_DLINFO \ - do { \ - if (vdso_info) { \ - NEW_AUX_ENT(AT_SYSINFO, vdso_info->entry); \ - } \ - } while (0) - -#endif /* TARGET_X86_64 */ - -#define VDSO_HEADER "vdso.c.inc" - -#endif /* TARGET_I386 */ - #ifdef TARGET_ARM #ifndef TARGET_AARCH64 diff --git a/linux-user/i386/target_elf.h b/linux-user/i386/target_elf.h index f89ac0b611..dc58c0017a 100644 --- a/linux-user/i386/target_elf.h +++ b/linux-user/i386/target_elf.h @@ -10,6 +10,11 @@ #include "target_ptrace.h" +#define ELF_CLASS ELFCLASS32 +#define ELF_ARCH EM_386 +#define EXSTACK_DEFAULT true +#define VDSO_HEADER "vdso.c.inc" + #define HAVE_ELF_HWCAP 1 #define HAVE_ELF_PLATFORM 1 #define HAVE_ELF_CORE_DUMP 1 @@ -22,4 +27,21 @@ typedef struct target_elf_gregset_t { struct target_user_regs_struct pt; } target_elf_gregset_t; +/* + * This is used to ensure we don't load something for the wrong architecture. + */ +#define elf_check_arch(x) ((x) == EM_386 || (x) == EM_486) + +/* + * i386 is the only target which supplies AT_SYSINFO for the vdso. + * All others only supply AT_SYSINFO_EHDR. + */ +#define DLINFO_ARCH_ITEMS (vdso_info != NULL) +#define ARCH_DLINFO \ + do { \ + if (vdso_info) { \ + NEW_AUX_ENT(AT_SYSINFO, vdso_info->entry); \ + } \ + } while (0) + #endif diff --git a/linux-user/x86_64/target_elf.h b/linux-user/x86_64/target_elf.h index f05b1d4dba..f3c09bb8da 100644 --- a/linux-user/x86_64/target_elf.h +++ b/linux-user/x86_64/target_elf.h @@ -10,6 +10,10 @@ #include "target_ptrace.h" +#define ELF_CLASS ELFCLASS64 +#define ELF_ARCH EM_X86_64 +#define VDSO_HEADER "vdso.c.inc" + #define HAVE_ELF_HWCAP 1 #define HAVE_ELF_PLATFORM 1 #define HAVE_ELF_CORE_DUMP 1 From f8eff2334a3b77d30ddae672edd3c541020cd1ca Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 29 Jul 2025 10:17:19 -1000 Subject: [PATCH 0205/2396] linux-user: Move elf parameters to {arm,aarch64}/target_elf.h Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/aarch64/target_elf.h | 9 +++++++++ linux-user/arm/target_elf.h | 4 ++++ linux-user/elfload.c | 25 ------------------------- 3 files changed, 13 insertions(+), 25 deletions(-) diff --git a/linux-user/aarch64/target_elf.h b/linux-user/aarch64/target_elf.h index 9eb8bb547e..3c9fef9378 100644 --- a/linux-user/aarch64/target_elf.h +++ b/linux-user/aarch64/target_elf.h @@ -10,6 +10,9 @@ #include "target_ptrace.h" +#define ELF_ARCH EM_AARCH64 +#define ELF_CLASS ELFCLASS64 + #define HAVE_ELF_HWCAP 1 #define HAVE_ELF_HWCAP2 1 #define HAVE_ELF_PLATFORM 1 @@ -23,4 +26,10 @@ typedef struct target_elf_gregset_t { struct target_user_pt_regs pt; } target_elf_gregset_t; +#if TARGET_BIG_ENDIAN +# define VDSO_HEADER "vdso-be.c.inc" +#else +# define VDSO_HEADER "vdso-le.c.inc" +#endif + #endif diff --git a/linux-user/arm/target_elf.h b/linux-user/arm/target_elf.h index 19fdfa2f2c..d871d6d665 100644 --- a/linux-user/arm/target_elf.h +++ b/linux-user/arm/target_elf.h @@ -10,6 +10,10 @@ #include "target_ptrace.h" +#define ELF_ARCH EM_ARM +#define ELF_CLASS ELFCLASS32 +#define EXSTACK_DEFAULT true + #define HAVE_ELF_HWCAP 1 #define HAVE_ELF_HWCAP2 1 #define HAVE_ELF_PLATFORM 1 diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 73ca6c681e..838d7199a6 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -130,31 +130,6 @@ typedef abi_uint target_gid_t; #endif typedef abi_int target_pid_t; -#ifdef TARGET_ARM - -#ifndef TARGET_AARCH64 -/* 32 bit ARM definitions */ - -#define ELF_ARCH EM_ARM -#define ELF_CLASS ELFCLASS32 -#define EXSTACK_DEFAULT true - -#else -/* 64 bit ARM definitions */ - -#define ELF_ARCH EM_AARCH64 -#define ELF_CLASS ELFCLASS64 - -#if TARGET_BIG_ENDIAN -# define VDSO_HEADER "vdso-be.c.inc" -#else -# define VDSO_HEADER "vdso-le.c.inc" -#endif - -#endif /* not TARGET_AARCH64 */ - -#endif /* TARGET_ARM */ - #ifdef TARGET_SPARC #ifndef TARGET_SPARC64 From 7b75b5e611d157ec77e4be3ac7f5ff1301380440 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 29 Jul 2025 10:19:10 -1000 Subject: [PATCH 0206/2396] linux-user: Move elf parameters to sparc/target_elf.h Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 15 --------------- linux-user/sparc/target_elf.h | 11 +++++++++++ 2 files changed, 11 insertions(+), 15 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 838d7199a6..ccdd87aa12 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -130,21 +130,6 @@ typedef abi_uint target_gid_t; #endif typedef abi_int target_pid_t; -#ifdef TARGET_SPARC - -#ifndef TARGET_SPARC64 -# define ELF_CLASS ELFCLASS32 -# define ELF_ARCH EM_SPARC -#elif defined(TARGET_ABI32) -# define ELF_CLASS ELFCLASS32 -# define elf_check_arch(x) ((x) == EM_SPARC32PLUS || (x) == EM_SPARC) -#else -# define ELF_CLASS ELFCLASS64 -# define ELF_ARCH EM_SPARCV9 -#endif - -#endif /* TARGET_SPARC */ - #ifdef TARGET_PPC #define ELF_MACHINE PPC_ELF_MACHINE diff --git a/linux-user/sparc/target_elf.h b/linux-user/sparc/target_elf.h index b7544db0a1..f89c708c46 100644 --- a/linux-user/sparc/target_elf.h +++ b/linux-user/sparc/target_elf.h @@ -8,6 +8,17 @@ #ifndef SPARC_TARGET_ELF_H #define SPARC_TARGET_ELF_H +#ifndef TARGET_SPARC64 +# define ELF_CLASS ELFCLASS32 +# define ELF_ARCH EM_SPARC +#elif defined(TARGET_ABI32) +# define ELF_CLASS ELFCLASS32 +# define elf_check_arch(x) ((x) == EM_SPARC32PLUS || (x) == EM_SPARC) +#else +# define ELF_CLASS ELFCLASS64 +# define ELF_ARCH EM_SPARCV9 +#endif + #define HAVE_ELF_HWCAP 1 #endif From 5408b354552180e42d644dc42b8b565648fad1be Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 29 Jul 2025 10:24:56 -1000 Subject: [PATCH 0207/2396] linux-user: Move elf parameters to ppc/target_elf.h Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 53 ------------------------------------- linux-user/ppc/target_elf.h | 43 ++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 53 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index ccdd87aa12..526c90e2c1 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -130,59 +130,6 @@ typedef abi_uint target_gid_t; #endif typedef abi_int target_pid_t; -#ifdef TARGET_PPC - -#define ELF_MACHINE PPC_ELF_MACHINE - -#if defined(TARGET_PPC64) - -#define elf_check_arch(x) ( (x) == EM_PPC64 ) - -#define ELF_CLASS ELFCLASS64 - -#else - -#define ELF_CLASS ELFCLASS32 -#define EXSTACK_DEFAULT true - -#endif - -#define ELF_ARCH EM_PPC - -/* - * The requirements here are: - * - keep the final alignment of sp (sp & 0xf) - * - make sure the 32-bit value at the first 16 byte aligned position of - * AUXV is greater than 16 for glibc compatibility. - * AT_IGNOREPPC is used for that. - * - for compatibility with glibc ARCH_DLINFO must always be defined on PPC, - * even if DLINFO_ARCH_ITEMS goes to zero or is undefined. - */ -#define DLINFO_ARCH_ITEMS 5 -#define ARCH_DLINFO \ - do { \ - PowerPCCPU *cpu = POWERPC_CPU(thread_cpu); \ - /* \ - * Handle glibc compatibility: these magic entries must \ - * be at the lowest addresses in the final auxv. \ - */ \ - NEW_AUX_ENT(AT_IGNOREPPC, AT_IGNOREPPC); \ - NEW_AUX_ENT(AT_IGNOREPPC, AT_IGNOREPPC); \ - NEW_AUX_ENT(AT_DCACHEBSIZE, cpu->env.dcache_line_size); \ - NEW_AUX_ENT(AT_ICACHEBSIZE, cpu->env.icache_line_size); \ - NEW_AUX_ENT(AT_UCACHEBSIZE, 0); \ - } while (0) - -#ifndef TARGET_PPC64 -# define VDSO_HEADER "vdso-32.c.inc" -#elif TARGET_BIG_ENDIAN -# define VDSO_HEADER "vdso-64.c.inc" -#else -# define VDSO_HEADER "vdso-64le.c.inc" -#endif - -#endif - #ifdef TARGET_LOONGARCH64 #define ELF_CLASS ELFCLASS64 diff --git a/linux-user/ppc/target_elf.h b/linux-user/ppc/target_elf.h index 2a61cd2896..9a47f18fb8 100644 --- a/linux-user/ppc/target_elf.h +++ b/linux-user/ppc/target_elf.h @@ -10,6 +10,17 @@ #include "target_ptrace.h" +#define ELF_MACHINE PPC_ELF_MACHINE + +#ifdef TARGET_PPC64 +# define elf_check_arch(x) ((x) == EM_PPC64) +# define ELF_CLASS ELFCLASS64 +#else +# define ELF_CLASS ELFCLASS32 +# define EXSTACK_DEFAULT true +#endif +#define ELF_ARCH EM_PPC + #define HAVE_ELF_HWCAP 1 #define HAVE_ELF_HWCAP2 1 #define HAVE_ELF_CORE_DUMP 1 @@ -26,4 +37,36 @@ typedef struct target_elf_gregset_t { }; } target_elf_gregset_t; +#ifndef TARGET_PPC64 +# define VDSO_HEADER "vdso-32.c.inc" +#elif TARGET_BIG_ENDIAN +# define VDSO_HEADER "vdso-64.c.inc" +#else +# define VDSO_HEADER "vdso-64le.c.inc" +#endif + +/* + * The requirements here are: + * - keep the final alignment of sp (sp & 0xf) + * - make sure the 32-bit value at the first 16 byte aligned position of + * AUXV is greater than 16 for glibc compatibility. + * AT_IGNOREPPC is used for that. + * - for compatibility with glibc ARCH_DLINFO must always be defined on PPC, + * even if DLINFO_ARCH_ITEMS goes to zero or is undefined. + */ +#define DLINFO_ARCH_ITEMS 5 +#define ARCH_DLINFO \ + do { \ + PowerPCCPU *cpu = POWERPC_CPU(thread_cpu); \ + /* \ + * Handle glibc compatibility: these magic entries must \ + * be at the lowest addresses in the final auxv. \ + */ \ + NEW_AUX_ENT(AT_IGNOREPPC, AT_IGNOREPPC); \ + NEW_AUX_ENT(AT_IGNOREPPC, AT_IGNOREPPC); \ + NEW_AUX_ENT(AT_DCACHEBSIZE, cpu->env.dcache_line_size); \ + NEW_AUX_ENT(AT_ICACHEBSIZE, cpu->env.icache_line_size); \ + NEW_AUX_ENT(AT_UCACHEBSIZE, 0); \ + } while (0) + #endif From b38ec953747d8a56f1a6cf72cdecf9b243a55fae Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 29 Jul 2025 10:26:37 -1000 Subject: [PATCH 0208/2396] linux-user: Move elf parameters to loongarch64/target_elf.h Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 12 ------------ linux-user/loongarch64/target_elf.h | 6 ++++++ 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 526c90e2c1..a4005c44ef 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -130,18 +130,6 @@ typedef abi_uint target_gid_t; #endif typedef abi_int target_pid_t; -#ifdef TARGET_LOONGARCH64 - -#define ELF_CLASS ELFCLASS64 -#define ELF_ARCH EM_LOONGARCH -#define EXSTACK_DEFAULT true - -#define elf_check_arch(x) ((x) == EM_LOONGARCH) - -#define VDSO_HEADER "vdso.c.inc" - -#endif /* TARGET_LOONGARCH64 */ - #ifdef TARGET_MIPS #ifdef TARGET_MIPS64 diff --git a/linux-user/loongarch64/target_elf.h b/linux-user/loongarch64/target_elf.h index 1f40419af2..47bf51a41c 100644 --- a/linux-user/loongarch64/target_elf.h +++ b/linux-user/loongarch64/target_elf.h @@ -8,6 +8,12 @@ #include "target_ptrace.h" +#define ELF_CLASS ELFCLASS64 +#define ELF_ARCH EM_LOONGARCH +#define EXSTACK_DEFAULT true +#define elf_check_arch(x) ((x) == EM_LOONGARCH) +#define VDSO_HEADER "vdso.c.inc" + #define HAVE_ELF_HWCAP 1 #define HAVE_ELF_PLATFORM 1 #define HAVE_ELF_CORE_DUMP 1 From c33d6652970edcdc6d88e1e4316569f618086a9a Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 29 Jul 2025 10:30:44 -1000 Subject: [PATCH 0209/2396] linux-user: Move elf parameters to {mips,mips64}/target_elf.h Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 18 ------------------ linux-user/mips/target_elf.h | 4 ++++ linux-user/mips64/target_elf.h | 10 ++++++++++ 3 files changed, 14 insertions(+), 18 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index a4005c44ef..a67147d43b 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -130,24 +130,6 @@ typedef abi_uint target_gid_t; #endif typedef abi_int target_pid_t; -#ifdef TARGET_MIPS - -#ifdef TARGET_MIPS64 -#define ELF_CLASS ELFCLASS64 -#else -#define ELF_CLASS ELFCLASS32 -#endif -#define ELF_ARCH EM_MIPS -#define EXSTACK_DEFAULT true - -#ifdef TARGET_ABI_MIPSN32 -#define elf_check_abi(x) ((x) & EF_MIPS_ABI2) -#else -#define elf_check_abi(x) (!((x) & EF_MIPS_ABI2)) -#endif - -#endif /* TARGET_MIPS */ - #ifdef TARGET_MICROBLAZE #define elf_check_arch(x) ( (x) == EM_MICROBLAZE || (x) == EM_MICROBLAZE_OLD) diff --git a/linux-user/mips/target_elf.h b/linux-user/mips/target_elf.h index a4b7fadbd6..f400bc2fdb 100644 --- a/linux-user/mips/target_elf.h +++ b/linux-user/mips/target_elf.h @@ -10,6 +10,10 @@ #include "target_ptrace.h" +#define ELF_CLASS ELFCLASS32 +#define ELF_ARCH EM_MIPS +#define EXSTACK_DEFAULT true + #define HAVE_ELF_HWCAP 1 #define HAVE_ELF_BASE_PLATFORM 1 #define HAVE_ELF_CORE_DUMP 1 diff --git a/linux-user/mips64/target_elf.h b/linux-user/mips64/target_elf.h index 67bc963134..c455985a76 100644 --- a/linux-user/mips64/target_elf.h +++ b/linux-user/mips64/target_elf.h @@ -10,6 +10,16 @@ #include "target_ptrace.h" +#define ELF_CLASS ELFCLASS64 +#define ELF_ARCH EM_MIPS +#define EXSTACK_DEFAULT true + +#ifdef TARGET_ABI_MIPSN32 +#define elf_check_abi(x) ((x) & EF_MIPS_ABI2) +#else +#define elf_check_abi(x) (!((x) & EF_MIPS_ABI2)) +#endif + #define HAVE_ELF_HWCAP 1 #define HAVE_ELF_BASE_PLATFORM 1 #define HAVE_ELF_CORE_DUMP 1 From db325955c715c4e4237b370cb65c03ca3414e60d Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 29 Jul 2025 10:38:04 -1000 Subject: [PATCH 0210/2396] linux-user: Move elf parameters to microblaze/target_elf.h Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 9 --------- linux-user/microblaze/target_elf.h | 5 +++++ 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index a67147d43b..6c8771d804 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -130,15 +130,6 @@ typedef abi_uint target_gid_t; #endif typedef abi_int target_pid_t; -#ifdef TARGET_MICROBLAZE - -#define elf_check_arch(x) ( (x) == EM_MICROBLAZE || (x) == EM_MICROBLAZE_OLD) - -#define ELF_CLASS ELFCLASS32 -#define ELF_ARCH EM_MICROBLAZE - -#endif /* TARGET_MICROBLAZE */ - #ifdef TARGET_OPENRISC #define ELF_ARCH EM_OPENRISC diff --git a/linux-user/microblaze/target_elf.h b/linux-user/microblaze/target_elf.h index 56de77d4f3..a622cd8e43 100644 --- a/linux-user/microblaze/target_elf.h +++ b/linux-user/microblaze/target_elf.h @@ -10,6 +10,11 @@ #include "target_ptrace.h" +#define ELF_CLASS ELFCLASS32 +#define ELF_ARCH EM_MICROBLAZE + +#define elf_check_arch(x) ((x) == EM_MICROBLAZE || (x) == EM_MICROBLAZE_OLD) + #define HAVE_ELF_CORE_DUMP 1 /* From b333696601c72d1bffc8a608c25791113875571b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 29 Jul 2025 10:40:21 -1000 Subject: [PATCH 0211/2396] linux-user: Move elf parameters to openrisc/target_elf.h Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 7 ------- linux-user/openrisc/target_elf.h | 3 +++ 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 6c8771d804..d0993621c1 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -130,13 +130,6 @@ typedef abi_uint target_gid_t; #endif typedef abi_int target_pid_t; -#ifdef TARGET_OPENRISC - -#define ELF_ARCH EM_OPENRISC -#define ELF_CLASS ELFCLASS32 - -#endif /* TARGET_OPENRISC */ - #ifdef TARGET_SH4 #define ELF_CLASS ELFCLASS32 diff --git a/linux-user/openrisc/target_elf.h b/linux-user/openrisc/target_elf.h index ad80e4b41a..ed9739380f 100644 --- a/linux-user/openrisc/target_elf.h +++ b/linux-user/openrisc/target_elf.h @@ -10,6 +10,9 @@ #include "target_ptrace.h" +#define ELF_ARCH EM_OPENRISC +#define ELF_CLASS ELFCLASS32 + #define HAVE_ELF_CORE_DUMP 1 /* From ac2a6820bf1785c7ff28bb00a1b3e66765d82d3b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 29 Jul 2025 10:42:01 -1000 Subject: [PATCH 0212/2396] linux-user: Move elf parameters to sh4/target_elf.h Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 7 ------- linux-user/sh4/target_elf.h | 3 +++ 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index d0993621c1..1a6e81394c 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -130,13 +130,6 @@ typedef abi_uint target_gid_t; #endif typedef abi_int target_pid_t; -#ifdef TARGET_SH4 - -#define ELF_CLASS ELFCLASS32 -#define ELF_ARCH EM_SH - -#endif - #ifdef TARGET_M68K #define ELF_CLASS ELFCLASS32 diff --git a/linux-user/sh4/target_elf.h b/linux-user/sh4/target_elf.h index fd3ae68a01..61aea237c4 100644 --- a/linux-user/sh4/target_elf.h +++ b/linux-user/sh4/target_elf.h @@ -10,6 +10,9 @@ #include "target_ptrace.h" +#define ELF_CLASS ELFCLASS32 +#define ELF_ARCH EM_SH + #define HAVE_ELF_HWCAP 1 #define HAVE_ELF_CORE_DUMP 1 From 4cbf380111b7f013bcfe6bff9c4f73f00ffdefc6 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 29 Jul 2025 10:43:03 -1000 Subject: [PATCH 0213/2396] linux-user: Move elf parameters to m68k/target_elf.h Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 7 ------- linux-user/m68k/target_elf.h | 3 +++ 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 1a6e81394c..a3757c595e 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -130,13 +130,6 @@ typedef abi_uint target_gid_t; #endif typedef abi_int target_pid_t; -#ifdef TARGET_M68K - -#define ELF_CLASS ELFCLASS32 -#define ELF_ARCH EM_68K - -#endif - #ifdef TARGET_ALPHA #define ELF_CLASS ELFCLASS64 diff --git a/linux-user/m68k/target_elf.h b/linux-user/m68k/target_elf.h index 0737412cee..073c85becc 100644 --- a/linux-user/m68k/target_elf.h +++ b/linux-user/m68k/target_elf.h @@ -8,6 +8,9 @@ #ifndef M68K_TARGET_ELF_H #define M68K_TARGET_ELF_H +#define ELF_CLASS ELFCLASS32 +#define ELF_ARCH EM_68K + #define HAVE_ELF_CORE_DUMP 1 /* From 39955adeb2928fb41757421fb2b1b91b18bfc8de Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 29 Jul 2025 10:53:00 -1000 Subject: [PATCH 0214/2396] linux-user: Move elf parameters to alpha/target_elf.h Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/alpha/target_elf.h | 3 +++ linux-user/elfload.c | 7 ------- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/linux-user/alpha/target_elf.h b/linux-user/alpha/target_elf.h index 52b68680ad..f9d6372c9f 100644 --- a/linux-user/alpha/target_elf.h +++ b/linux-user/alpha/target_elf.h @@ -8,4 +8,7 @@ #ifndef ALPHA_TARGET_ELF_H #define ALPHA_TARGET_ELF_H +#define ELF_CLASS ELFCLASS64 +#define ELF_ARCH EM_ALPHA + #endif diff --git a/linux-user/elfload.c b/linux-user/elfload.c index a3757c595e..aff800baff 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -130,13 +130,6 @@ typedef abi_uint target_gid_t; #endif typedef abi_int target_pid_t; -#ifdef TARGET_ALPHA - -#define ELF_CLASS ELFCLASS64 -#define ELF_ARCH EM_ALPHA - -#endif /* TARGET_ALPHA */ - #ifdef TARGET_S390X #define ELF_CLASS ELFCLASS64 From 2fed144511457a2bc83f8731b8af4dc4b476bba4 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 29 Jul 2025 10:55:36 -1000 Subject: [PATCH 0215/2396] linux-user: Move elf parameters to s390x/target_elf.h Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 9 --------- linux-user/s390x/target_elf.h | 4 ++++ 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index aff800baff..705d726922 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -130,15 +130,6 @@ typedef abi_uint target_gid_t; #endif typedef abi_int target_pid_t; -#ifdef TARGET_S390X - -#define ELF_CLASS ELFCLASS64 -#define ELF_ARCH EM_S390 - -#define VDSO_HEADER "vdso.c.inc" - -#endif /* TARGET_S390X */ - #ifdef TARGET_RISCV #define ELF_ARCH EM_RISCV diff --git a/linux-user/s390x/target_elf.h b/linux-user/s390x/target_elf.h index 670c7b3eed..b23e46ab46 100644 --- a/linux-user/s390x/target_elf.h +++ b/linux-user/s390x/target_elf.h @@ -10,6 +10,10 @@ #include "target_ptrace.h" +#define ELF_CLASS ELFCLASS64 +#define ELF_ARCH EM_S390 +#define VDSO_HEADER "vdso.c.inc" + #define HAVE_ELF_HWCAP 1 #define HAVE_ELF_CORE_DUMP 1 From 4a56d73ae20aa893ce4f3f7d691310f9d503e7b9 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 29 Jul 2025 11:03:31 -1000 Subject: [PATCH 0216/2396] linux-user: Move elf parameters to riscv/target_elf.h Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 14 -------------- linux-user/riscv/target_elf.h | 10 ++++++++++ 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 705d726922..6e476d5308 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -130,20 +130,6 @@ typedef abi_uint target_gid_t; #endif typedef abi_int target_pid_t; -#ifdef TARGET_RISCV - -#define ELF_ARCH EM_RISCV - -#ifdef TARGET_RISCV32 -#define ELF_CLASS ELFCLASS32 -#define VDSO_HEADER "vdso-32.c.inc" -#else -#define ELF_CLASS ELFCLASS64 -#define VDSO_HEADER "vdso-64.c.inc" -#endif - -#endif /* TARGET_RISCV */ - #ifdef TARGET_HPPA #define ELF_CLASS ELFCLASS32 diff --git a/linux-user/riscv/target_elf.h b/linux-user/riscv/target_elf.h index 48d9af557b..51b8def1d1 100644 --- a/linux-user/riscv/target_elf.h +++ b/linux-user/riscv/target_elf.h @@ -8,6 +8,16 @@ #ifndef RISCV_TARGET_ELF_H #define RISCV_TARGET_ELF_H +#define ELF_ARCH EM_RISCV + +#ifdef TARGET_RISCV32 +#define ELF_CLASS ELFCLASS32 +#define VDSO_HEADER "vdso-32.c.inc" +#else +#define ELF_CLASS ELFCLASS64 +#define VDSO_HEADER "vdso-64.c.inc" +#endif + #define HAVE_ELF_HWCAP 1 #endif From 964bc6c1b251e46b6d635ccbd69990227f8891df Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 29 Jul 2025 11:11:36 -1000 Subject: [PATCH 0217/2396] linux-user: Move elf parameters to hppa/target_elf.h Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 11 ----------- linux-user/hppa/target_elf.h | 6 ++++++ 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 6e476d5308..6732011332 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -130,17 +130,6 @@ typedef abi_uint target_gid_t; #endif typedef abi_int target_pid_t; -#ifdef TARGET_HPPA - -#define ELF_CLASS ELFCLASS32 -#define ELF_ARCH EM_PARISC -#define STACK_GROWS_DOWN 0 -#define STACK_ALIGNMENT 64 - -#define VDSO_HEADER "vdso.c.inc" - -#endif /* TARGET_HPPA */ - #ifdef TARGET_XTENSA #define ELF_CLASS ELFCLASS32 diff --git a/linux-user/hppa/target_elf.h b/linux-user/hppa/target_elf.h index b654758afa..9b6363a0a7 100644 --- a/linux-user/hppa/target_elf.h +++ b/linux-user/hppa/target_elf.h @@ -8,8 +8,14 @@ #ifndef HPPA_TARGET_ELF_H #define HPPA_TARGET_ELF_H +#define ELF_CLASS ELFCLASS32 +#define ELF_ARCH EM_PARISC + #define HAVE_ELF_PLATFORM 1 #define LO_COMMPAGE 0 +#define STACK_GROWS_DOWN 0 +#define STACK_ALIGNMENT 64 +#define VDSO_HEADER "vdso.c.inc" #endif From 60502ab782496c160223a7892197abaad1670187 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 29 Jul 2025 11:13:03 -1000 Subject: [PATCH 0218/2396] linux-user: Move elf parameters to xtensa/target_elf.h Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 7 ------- linux-user/xtensa/target_elf.h | 3 +++ 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 6732011332..804a819471 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -130,13 +130,6 @@ typedef abi_uint target_gid_t; #endif typedef abi_int target_pid_t; -#ifdef TARGET_XTENSA - -#define ELF_CLASS ELFCLASS32 -#define ELF_ARCH EM_XTENSA - -#endif /* TARGET_XTENSA */ - #ifdef TARGET_HEXAGON #define ELF_CLASS ELFCLASS32 diff --git a/linux-user/xtensa/target_elf.h b/linux-user/xtensa/target_elf.h index 850a7206a5..0689e79be5 100644 --- a/linux-user/xtensa/target_elf.h +++ b/linux-user/xtensa/target_elf.h @@ -10,6 +10,9 @@ #include "target_ptrace.h" +#define ELF_CLASS ELFCLASS32 +#define ELF_ARCH EM_XTENSA + #define HAVE_ELF_CORE_DUMP 1 /* From 14095cb5f84c0a18707e86ed7f064e2fed3bf87a Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 29 Jul 2025 11:14:14 -1000 Subject: [PATCH 0219/2396] linux-user: Move elf parameters to hexagon/target_elf.h Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 7 ------- linux-user/hexagon/target_elf.h | 3 +++ 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 804a819471..33c4214c95 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -130,13 +130,6 @@ typedef abi_uint target_gid_t; #endif typedef abi_int target_pid_t; -#ifdef TARGET_HEXAGON - -#define ELF_CLASS ELFCLASS32 -#define ELF_ARCH EM_HEXAGON - -#endif /* TARGET_HEXAGON */ - #ifndef ELF_MACHINE #define ELF_MACHINE ELF_ARCH #endif diff --git a/linux-user/hexagon/target_elf.h b/linux-user/hexagon/target_elf.h index eccf207f6b..a9f6d77fc6 100644 --- a/linux-user/hexagon/target_elf.h +++ b/linux-user/hexagon/target_elf.h @@ -18,4 +18,7 @@ #ifndef HEXAGON_TARGET_ELF_H #define HEXAGON_TARGET_ELF_H +#define ELF_CLASS ELFCLASS32 +#define ELF_ARCH EM_HEXAGON + #endif From e6b7635c9a33918c671e1f2ef4b17b5c2f279d66 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 29 Jul 2025 11:23:31 -1000 Subject: [PATCH 0220/2396] linux-user: Standardize on ELF_MACHINE not ELF_ARCH PowerPC was the one outlier that defined both ELF_ARCH and ELF_MACHINE; ELF_ARCH was defined incorrectly, necessitating the definition of elf_check_arch. However, the elf file header field in question is called e_machine, so ELF_MACHINE is in fact the better name. Mechanically change most target/target_elf.h files, then adjust ppc/target_elf.h manually. Do not provide a default for ELF_MACHINE. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/aarch64/target_elf.h | 2 +- linux-user/alpha/target_elf.h | 2 +- linux-user/arm/target_elf.h | 2 +- linux-user/elfload.c | 6 +----- linux-user/hexagon/target_elf.h | 2 +- linux-user/hppa/target_elf.h | 2 +- linux-user/i386/target_elf.h | 2 +- linux-user/loongarch64/target_elf.h | 2 +- linux-user/m68k/target_elf.h | 2 +- linux-user/microblaze/target_elf.h | 2 +- linux-user/mips/target_elf.h | 2 +- linux-user/mips64/target_elf.h | 2 +- linux-user/openrisc/target_elf.h | 2 +- linux-user/ppc/target_elf.h | 2 -- linux-user/riscv/target_elf.h | 2 +- linux-user/s390x/target_elf.h | 2 +- linux-user/sh4/target_elf.h | 2 +- linux-user/sparc/target_elf.h | 4 ++-- linux-user/x86_64/target_elf.h | 2 +- linux-user/xtensa/target_elf.h | 2 +- 20 files changed, 20 insertions(+), 26 deletions(-) diff --git a/linux-user/aarch64/target_elf.h b/linux-user/aarch64/target_elf.h index 3c9fef9378..9ec51f6237 100644 --- a/linux-user/aarch64/target_elf.h +++ b/linux-user/aarch64/target_elf.h @@ -10,7 +10,7 @@ #include "target_ptrace.h" -#define ELF_ARCH EM_AARCH64 +#define ELF_MACHINE EM_AARCH64 #define ELF_CLASS ELFCLASS64 #define HAVE_ELF_HWCAP 1 diff --git a/linux-user/alpha/target_elf.h b/linux-user/alpha/target_elf.h index f9d6372c9f..864dc6e2e6 100644 --- a/linux-user/alpha/target_elf.h +++ b/linux-user/alpha/target_elf.h @@ -9,6 +9,6 @@ #define ALPHA_TARGET_ELF_H #define ELF_CLASS ELFCLASS64 -#define ELF_ARCH EM_ALPHA +#define ELF_MACHINE EM_ALPHA #endif diff --git a/linux-user/arm/target_elf.h b/linux-user/arm/target_elf.h index d871d6d665..12cdc8e5a7 100644 --- a/linux-user/arm/target_elf.h +++ b/linux-user/arm/target_elf.h @@ -10,7 +10,7 @@ #include "target_ptrace.h" -#define ELF_ARCH EM_ARM +#define ELF_MACHINE EM_ARM #define ELF_CLASS ELFCLASS32 #define EXSTACK_DEFAULT true diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 33c4214c95..c481759710 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -130,12 +130,8 @@ typedef abi_uint target_gid_t; #endif typedef abi_int target_pid_t; -#ifndef ELF_MACHINE -#define ELF_MACHINE ELF_ARCH -#endif - #ifndef elf_check_arch -#define elf_check_arch(x) ((x) == ELF_ARCH) +#define elf_check_arch(x) ((x) == ELF_MACHINE) #endif #ifndef elf_check_abi diff --git a/linux-user/hexagon/target_elf.h b/linux-user/hexagon/target_elf.h index a9f6d77fc6..f81ae3895a 100644 --- a/linux-user/hexagon/target_elf.h +++ b/linux-user/hexagon/target_elf.h @@ -19,6 +19,6 @@ #define HEXAGON_TARGET_ELF_H #define ELF_CLASS ELFCLASS32 -#define ELF_ARCH EM_HEXAGON +#define ELF_MACHINE EM_HEXAGON #endif diff --git a/linux-user/hppa/target_elf.h b/linux-user/hppa/target_elf.h index 9b6363a0a7..76930c9369 100644 --- a/linux-user/hppa/target_elf.h +++ b/linux-user/hppa/target_elf.h @@ -9,7 +9,7 @@ #define HPPA_TARGET_ELF_H #define ELF_CLASS ELFCLASS32 -#define ELF_ARCH EM_PARISC +#define ELF_MACHINE EM_PARISC #define HAVE_ELF_PLATFORM 1 diff --git a/linux-user/i386/target_elf.h b/linux-user/i386/target_elf.h index dc58c0017a..c3caad68b9 100644 --- a/linux-user/i386/target_elf.h +++ b/linux-user/i386/target_elf.h @@ -11,7 +11,7 @@ #include "target_ptrace.h" #define ELF_CLASS ELFCLASS32 -#define ELF_ARCH EM_386 +#define ELF_MACHINE EM_386 #define EXSTACK_DEFAULT true #define VDSO_HEADER "vdso.c.inc" diff --git a/linux-user/loongarch64/target_elf.h b/linux-user/loongarch64/target_elf.h index 47bf51a41c..b988592993 100644 --- a/linux-user/loongarch64/target_elf.h +++ b/linux-user/loongarch64/target_elf.h @@ -9,7 +9,7 @@ #include "target_ptrace.h" #define ELF_CLASS ELFCLASS64 -#define ELF_ARCH EM_LOONGARCH +#define ELF_MACHINE EM_LOONGARCH #define EXSTACK_DEFAULT true #define elf_check_arch(x) ((x) == EM_LOONGARCH) #define VDSO_HEADER "vdso.c.inc" diff --git a/linux-user/m68k/target_elf.h b/linux-user/m68k/target_elf.h index 073c85becc..b997fa0b6d 100644 --- a/linux-user/m68k/target_elf.h +++ b/linux-user/m68k/target_elf.h @@ -9,7 +9,7 @@ #define M68K_TARGET_ELF_H #define ELF_CLASS ELFCLASS32 -#define ELF_ARCH EM_68K +#define ELF_MACHINE EM_68K #define HAVE_ELF_CORE_DUMP 1 diff --git a/linux-user/microblaze/target_elf.h b/linux-user/microblaze/target_elf.h index a622cd8e43..1ec91ea5a9 100644 --- a/linux-user/microblaze/target_elf.h +++ b/linux-user/microblaze/target_elf.h @@ -11,7 +11,7 @@ #include "target_ptrace.h" #define ELF_CLASS ELFCLASS32 -#define ELF_ARCH EM_MICROBLAZE +#define ELF_MACHINE EM_MICROBLAZE #define elf_check_arch(x) ((x) == EM_MICROBLAZE || (x) == EM_MICROBLAZE_OLD) diff --git a/linux-user/mips/target_elf.h b/linux-user/mips/target_elf.h index f400bc2fdb..157306f7a0 100644 --- a/linux-user/mips/target_elf.h +++ b/linux-user/mips/target_elf.h @@ -11,7 +11,7 @@ #include "target_ptrace.h" #define ELF_CLASS ELFCLASS32 -#define ELF_ARCH EM_MIPS +#define ELF_MACHINE EM_MIPS #define EXSTACK_DEFAULT true #define HAVE_ELF_HWCAP 1 diff --git a/linux-user/mips64/target_elf.h b/linux-user/mips64/target_elf.h index c455985a76..061471a0f1 100644 --- a/linux-user/mips64/target_elf.h +++ b/linux-user/mips64/target_elf.h @@ -11,7 +11,7 @@ #include "target_ptrace.h" #define ELF_CLASS ELFCLASS64 -#define ELF_ARCH EM_MIPS +#define ELF_MACHINE EM_MIPS #define EXSTACK_DEFAULT true #ifdef TARGET_ABI_MIPSN32 diff --git a/linux-user/openrisc/target_elf.h b/linux-user/openrisc/target_elf.h index ed9739380f..e8554f5339 100644 --- a/linux-user/openrisc/target_elf.h +++ b/linux-user/openrisc/target_elf.h @@ -10,7 +10,7 @@ #include "target_ptrace.h" -#define ELF_ARCH EM_OPENRISC +#define ELF_MACHINE EM_OPENRISC #define ELF_CLASS ELFCLASS32 #define HAVE_ELF_CORE_DUMP 1 diff --git a/linux-user/ppc/target_elf.h b/linux-user/ppc/target_elf.h index 9a47f18fb8..22854cf52f 100644 --- a/linux-user/ppc/target_elf.h +++ b/linux-user/ppc/target_elf.h @@ -13,13 +13,11 @@ #define ELF_MACHINE PPC_ELF_MACHINE #ifdef TARGET_PPC64 -# define elf_check_arch(x) ((x) == EM_PPC64) # define ELF_CLASS ELFCLASS64 #else # define ELF_CLASS ELFCLASS32 # define EXSTACK_DEFAULT true #endif -#define ELF_ARCH EM_PPC #define HAVE_ELF_HWCAP 1 #define HAVE_ELF_HWCAP2 1 diff --git a/linux-user/riscv/target_elf.h b/linux-user/riscv/target_elf.h index 51b8def1d1..dbbfdf54d3 100644 --- a/linux-user/riscv/target_elf.h +++ b/linux-user/riscv/target_elf.h @@ -8,7 +8,7 @@ #ifndef RISCV_TARGET_ELF_H #define RISCV_TARGET_ELF_H -#define ELF_ARCH EM_RISCV +#define ELF_MACHINE EM_RISCV #ifdef TARGET_RISCV32 #define ELF_CLASS ELFCLASS32 diff --git a/linux-user/s390x/target_elf.h b/linux-user/s390x/target_elf.h index b23e46ab46..ef5edbd860 100644 --- a/linux-user/s390x/target_elf.h +++ b/linux-user/s390x/target_elf.h @@ -11,7 +11,7 @@ #include "target_ptrace.h" #define ELF_CLASS ELFCLASS64 -#define ELF_ARCH EM_S390 +#define ELF_MACHINE EM_S390 #define VDSO_HEADER "vdso.c.inc" #define HAVE_ELF_HWCAP 1 diff --git a/linux-user/sh4/target_elf.h b/linux-user/sh4/target_elf.h index 61aea237c4..d9e253d425 100644 --- a/linux-user/sh4/target_elf.h +++ b/linux-user/sh4/target_elf.h @@ -11,7 +11,7 @@ #include "target_ptrace.h" #define ELF_CLASS ELFCLASS32 -#define ELF_ARCH EM_SH +#define ELF_MACHINE EM_SH #define HAVE_ELF_HWCAP 1 #define HAVE_ELF_CORE_DUMP 1 diff --git a/linux-user/sparc/target_elf.h b/linux-user/sparc/target_elf.h index f89c708c46..6b0cac3caf 100644 --- a/linux-user/sparc/target_elf.h +++ b/linux-user/sparc/target_elf.h @@ -10,13 +10,13 @@ #ifndef TARGET_SPARC64 # define ELF_CLASS ELFCLASS32 -# define ELF_ARCH EM_SPARC +# define ELF_MACHINE EM_SPARC #elif defined(TARGET_ABI32) # define ELF_CLASS ELFCLASS32 # define elf_check_arch(x) ((x) == EM_SPARC32PLUS || (x) == EM_SPARC) #else # define ELF_CLASS ELFCLASS64 -# define ELF_ARCH EM_SPARCV9 +# define ELF_MACHINE EM_SPARCV9 #endif #define HAVE_ELF_HWCAP 1 diff --git a/linux-user/x86_64/target_elf.h b/linux-user/x86_64/target_elf.h index f3c09bb8da..840bddf5ec 100644 --- a/linux-user/x86_64/target_elf.h +++ b/linux-user/x86_64/target_elf.h @@ -11,7 +11,7 @@ #include "target_ptrace.h" #define ELF_CLASS ELFCLASS64 -#define ELF_ARCH EM_X86_64 +#define ELF_MACHINE EM_X86_64 #define VDSO_HEADER "vdso.c.inc" #define HAVE_ELF_HWCAP 1 diff --git a/linux-user/xtensa/target_elf.h b/linux-user/xtensa/target_elf.h index 0689e79be5..1bf8f2a14a 100644 --- a/linux-user/xtensa/target_elf.h +++ b/linux-user/xtensa/target_elf.h @@ -11,7 +11,7 @@ #include "target_ptrace.h" #define ELF_CLASS ELFCLASS32 -#define ELF_ARCH EM_XTENSA +#define ELF_MACHINE EM_XTENSA #define HAVE_ELF_CORE_DUMP 1 From eb727cc45a352ff259c4c2d85456f0412e738d43 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 29 Jul 2025 11:26:43 -1000 Subject: [PATCH 0221/2396] linux-user: Rename elf_check_arch Rename to elf_check_machine to match ELF_MACHINE. Remove the unnecessary definition for loongarch64. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 6 +++--- linux-user/i386/target_elf.h | 2 +- linux-user/loongarch64/target_elf.h | 1 - linux-user/microblaze/target_elf.h | 2 +- linux-user/sparc/target_elf.h | 2 +- 5 files changed, 6 insertions(+), 7 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index c481759710..aa0eed6dea 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -130,8 +130,8 @@ typedef abi_uint target_gid_t; #endif typedef abi_int target_pid_t; -#ifndef elf_check_arch -#define elf_check_arch(x) ((x) == ELF_MACHINE) +#ifndef elf_check_machine +#define elf_check_machine(x) ((x) == ELF_MACHINE) #endif #ifndef elf_check_abi @@ -346,7 +346,7 @@ static bool elf_check_ident(struct elfhdr *ehdr) This has to wait until after bswapping the header. */ static bool elf_check_ehdr(struct elfhdr *ehdr) { - return (elf_check_arch(ehdr->e_machine) + return (elf_check_machine(ehdr->e_machine) && elf_check_abi(ehdr->e_flags) && ehdr->e_ehsize == sizeof(struct elfhdr) && ehdr->e_phentsize == sizeof(struct elf_phdr) diff --git a/linux-user/i386/target_elf.h b/linux-user/i386/target_elf.h index c3caad68b9..eafac8f382 100644 --- a/linux-user/i386/target_elf.h +++ b/linux-user/i386/target_elf.h @@ -30,7 +30,7 @@ typedef struct target_elf_gregset_t { /* * This is used to ensure we don't load something for the wrong architecture. */ -#define elf_check_arch(x) ((x) == EM_386 || (x) == EM_486) +#define elf_check_machine(x) ((x) == EM_386 || (x) == EM_486) /* * i386 is the only target which supplies AT_SYSINFO for the vdso. diff --git a/linux-user/loongarch64/target_elf.h b/linux-user/loongarch64/target_elf.h index b988592993..3aa8c83958 100644 --- a/linux-user/loongarch64/target_elf.h +++ b/linux-user/loongarch64/target_elf.h @@ -11,7 +11,6 @@ #define ELF_CLASS ELFCLASS64 #define ELF_MACHINE EM_LOONGARCH #define EXSTACK_DEFAULT true -#define elf_check_arch(x) ((x) == EM_LOONGARCH) #define VDSO_HEADER "vdso.c.inc" #define HAVE_ELF_HWCAP 1 diff --git a/linux-user/microblaze/target_elf.h b/linux-user/microblaze/target_elf.h index 1ec91ea5a9..7b3ef70d23 100644 --- a/linux-user/microblaze/target_elf.h +++ b/linux-user/microblaze/target_elf.h @@ -13,7 +13,7 @@ #define ELF_CLASS ELFCLASS32 #define ELF_MACHINE EM_MICROBLAZE -#define elf_check_arch(x) ((x) == EM_MICROBLAZE || (x) == EM_MICROBLAZE_OLD) +#define elf_check_machine(x) ((x) == EM_MICROBLAZE || (x) == EM_MICROBLAZE_OLD) #define HAVE_ELF_CORE_DUMP 1 diff --git a/linux-user/sparc/target_elf.h b/linux-user/sparc/target_elf.h index 6b0cac3caf..7827767bcb 100644 --- a/linux-user/sparc/target_elf.h +++ b/linux-user/sparc/target_elf.h @@ -13,7 +13,7 @@ # define ELF_MACHINE EM_SPARC #elif defined(TARGET_ABI32) # define ELF_CLASS ELFCLASS32 -# define elf_check_arch(x) ((x) == EM_SPARC32PLUS || (x) == EM_SPARC) +# define elf_check_machine(x) ((x) == EM_SPARC32PLUS || (x) == EM_SPARC) #else # define ELF_CLASS ELFCLASS64 # define ELF_MACHINE EM_SPARCV9 From 5dcc0b62059e2330f2746fa691ac6791e9a14cc3 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 29 Jul 2025 11:30:26 -1000 Subject: [PATCH 0222/2396] linux-user: Remove ELIBBAD from elfload.c The last use of this fallback was removed in 8e62a71738bc. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index aa0eed6dea..c0326928d4 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -110,11 +110,6 @@ int info_is_fdpic(struct image_info *info) #define MAP_DENYWRITE 0 #endif -/* should probably go in elf.h */ -#ifndef ELIBBAD -#define ELIBBAD 80 -#endif - #if TARGET_BIG_ENDIAN #define ELF_DATA ELFDATA2MSB #else From dd1d0239a79c4e7947c901fcb1f232ecc0428a96 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 29 Jul 2025 11:33:58 -1000 Subject: [PATCH 0223/2396] linux-user: Remove MAP_DENYWRITE from elfload.c The last use of this fallback was removed in bf858897b769. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index c0326928d4..8b92fba0f0 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -105,11 +105,6 @@ int info_is_fdpic(struct image_info *info) return info->personality == PER_LINUX_FDPIC; } -/* this flag is uneffective under linux too, should be deleted */ -#ifndef MAP_DENYWRITE -#define MAP_DENYWRITE 0 -#endif - #if TARGET_BIG_ENDIAN #define ELF_DATA ELFDATA2MSB #else From 8218eb6f52abd08c9e89baafdddec6f0d9234768 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 29 Jul 2025 11:43:27 -1000 Subject: [PATCH 0224/2396] linux-user: Move arch_parse_elf_property to aarch64/elfload.c Rename the controlling macro to HAVE_ELF_GNU_PROPERTY to match the other HAVE_* macros. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/aarch64/elfload.c | 18 +++++++++++++++ linux-user/aarch64/target_elf.h | 1 + linux-user/elfload.c | 39 +++++++-------------------------- linux-user/loader.h | 5 +++++ 4 files changed, 32 insertions(+), 31 deletions(-) diff --git a/linux-user/aarch64/elfload.c b/linux-user/aarch64/elfload.c index 07a0c3f844..8076968251 100644 --- a/linux-user/aarch64/elfload.c +++ b/linux-user/aarch64/elfload.c @@ -1,10 +1,12 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ #include "qemu/osdep.h" +#include "qapi/error.h" #include "qemu.h" #include "loader.h" #include "target/arm/cpu-features.h" #include "target_elf.h" +#include "elf.h" const char *get_elf_cpu_model(uint32_t eflags) @@ -349,6 +351,22 @@ const char *get_elf_platform(CPUState *cs) return TARGET_BIG_ENDIAN ? "aarch64_be" : "aarch64"; } +bool arch_parse_elf_property(uint32_t pr_type, uint32_t pr_datasz, + const uint32_t *data, + struct image_info *info, + Error **errp) +{ + if (pr_type == GNU_PROPERTY_AARCH64_FEATURE_1_AND) { + if (pr_datasz != sizeof(uint32_t)) { + error_setg(errp, "Ill-formed GNU_PROPERTY_AARCH64_FEATURE_1_AND"); + return false; + } + /* We will extract GNU_PROPERTY_AARCH64_FEATURE_1_BTI later. */ + info->note_flags = *data; + } + return true; +} + void elf_core_copy_regs(target_elf_gregset_t *r, const CPUARMState *env) { for (int i = 0; i < 31; i++) { diff --git a/linux-user/aarch64/target_elf.h b/linux-user/aarch64/target_elf.h index 9ec51f6237..4cdeb64b0d 100644 --- a/linux-user/aarch64/target_elf.h +++ b/linux-user/aarch64/target_elf.h @@ -17,6 +17,7 @@ #define HAVE_ELF_HWCAP2 1 #define HAVE_ELF_PLATFORM 1 #define HAVE_ELF_CORE_DUMP 1 +#define HAVE_ELF_GNU_PROPERTY 1 /* * See linux kernel: arch/arm64/include/asm/elf.h, where diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 8b92fba0f0..12d4873212 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -166,41 +166,18 @@ const char *get_elf_platform(CPUState *cs) { return NULL; } const char *get_elf_base_platform(CPUState *cs) { return NULL; } #endif -#include "elf.h" - -/* We must delay the following stanzas until after "elf.h". */ -#if defined(TARGET_AARCH64) - -static bool arch_parse_elf_property(uint32_t pr_type, uint32_t pr_datasz, - const uint32_t *data, - struct image_info *info, - Error **errp) -{ - if (pr_type == GNU_PROPERTY_AARCH64_FEATURE_1_AND) { - if (pr_datasz != sizeof(uint32_t)) { - error_setg(errp, "Ill-formed GNU_PROPERTY_AARCH64_FEATURE_1_AND"); - return false; - } - /* We will extract GNU_PROPERTY_AARCH64_FEATURE_1_BTI later. */ - info->note_flags = *data; - } - return true; -} -#define ARCH_USE_GNU_PROPERTY 1 - -#else - -static bool arch_parse_elf_property(uint32_t pr_type, uint32_t pr_datasz, - const uint32_t *data, - struct image_info *info, - Error **errp) +#ifndef HAVE_ELF_GNU_PROPERTY +bool arch_parse_elf_property(uint32_t pr_type, uint32_t pr_datasz, + const uint32_t *data, struct image_info *info, + Error **errp) { g_assert_not_reached(); } -#define ARCH_USE_GNU_PROPERTY 0 - +#define HAVE_ELF_GNU_PROPERTY 0 #endif +#include "elf.h" + struct exec { unsigned int a_info; /* Use macros N_MAGIC, etc for access */ @@ -1233,7 +1210,7 @@ static bool parse_elf_properties(const ImageSource *src, uint32_t prev_type; /* Unless the arch requires properties, ignore them. */ - if (!ARCH_USE_GNU_PROPERTY) { + if (!HAVE_ELF_GNU_PROPERTY) { return true; } diff --git a/linux-user/loader.h b/linux-user/loader.h index 2175dd4e0a..e42b8fa1e3 100644 --- a/linux-user/loader.h +++ b/linux-user/loader.h @@ -122,4 +122,9 @@ typedef struct { /* Note that both Elf32_Word and Elf64_Word are uint32_t. */ const VdsoImageInfo *get_vdso_image_info(uint32_t elf_flags); +bool arch_parse_elf_property(uint32_t pr_type, uint32_t pr_datasz, + const uint32_t *data, + struct image_info *info, + Error **errp); + #endif /* LINUX_USER_LOADER_H */ From ab2d261ab3e73f622d75c5c6cc06cc0b7db2d79a Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 29 Jul 2025 11:45:13 -1000 Subject: [PATCH 0225/2396] linux-user: Remove a.out declarations from elfload.c These should have been removed with the rest of the stub a.out support in b9329d4b5321, though they were not in use even then. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 12d4873212..26c090c95d 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -178,25 +178,6 @@ bool arch_parse_elf_property(uint32_t pr_type, uint32_t pr_datasz, #include "elf.h" -struct exec -{ - unsigned int a_info; /* Use macros N_MAGIC, etc for access */ - unsigned int a_text; /* length of text, in bytes */ - unsigned int a_data; /* length of data, in bytes */ - unsigned int a_bss; /* length of uninitialized data area, in bytes */ - unsigned int a_syms; /* length of symbol table data in file, in bytes */ - unsigned int a_entry; /* start address */ - unsigned int a_trsize; /* length of relocation info for text, in bytes */ - unsigned int a_drsize; /* length of relocation info for data, in bytes */ -}; - - -#define N_MAGIC(exec) ((exec).a_info & 0xffff) -#define OMAGIC 0407 -#define NMAGIC 0410 -#define ZMAGIC 0413 -#define QMAGIC 0314 - #define DLINFO_ITEMS 16 static inline void memcpy_fromfs(void * to, const void * from, unsigned long n) From 8bb7873f28e82944ab6fb2c633c09877fd4eeda8 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Aug 2025 21:51:46 +1000 Subject: [PATCH 0226/2396] linux-user/sparc: Create target_ptrace.h Move target_pt_regs to target_ptrace.h. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/sparc/signal.c | 2 ++ linux-user/sparc/target_ptrace.h | 24 ++++++++++++++++++++++++ linux-user/sparc/target_syscall.h | 19 ------------------- 3 files changed, 26 insertions(+), 19 deletions(-) create mode 100644 linux-user/sparc/target_ptrace.h diff --git a/linux-user/sparc/signal.c b/linux-user/sparc/signal.c index 8181b8b92c..d339f89928 100644 --- a/linux-user/sparc/signal.c +++ b/linux-user/sparc/signal.c @@ -21,6 +21,8 @@ #include "user-internals.h" #include "signal-common.h" #include "linux-user/trace.h" +#include "target_ptrace.h" + /* A Sparc register window */ struct target_reg_window { diff --git a/linux-user/sparc/target_ptrace.h b/linux-user/sparc/target_ptrace.h new file mode 100644 index 0000000000..a4d5416c1f --- /dev/null +++ b/linux-user/sparc/target_ptrace.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef SPARC_TARGET_PTRACE_H +#define SPARC_TARGET_PTRACE_H + +/* See arch/sparc/include/uapi/asm/ptrace.h. */ +struct target_pt_regs { +#if defined(TARGET_SPARC64) && !defined(TARGET_ABI32) + abi_ulong u_regs[16]; + abi_ulong tstate; + abi_ulong pc; + abi_ulong npc; + uint32_t y; + uint32_t magic; +#else + abi_ulong psr; + abi_ulong pc; + abi_ulong npc; + abi_ulong y; + abi_ulong u_regs[16]; +#endif +}; + +#endif /* SPARC_TARGET_PTRACE_H */ diff --git a/linux-user/sparc/target_syscall.h b/linux-user/sparc/target_syscall.h index e421165357..a90ed2983a 100644 --- a/linux-user/sparc/target_syscall.h +++ b/linux-user/sparc/target_syscall.h @@ -1,25 +1,6 @@ #ifndef SPARC_TARGET_SYSCALL_H #define SPARC_TARGET_SYSCALL_H -#if defined(TARGET_SPARC64) && !defined(TARGET_ABI32) -struct target_pt_regs { - abi_ulong u_regs[16]; - abi_ulong tstate; - abi_ulong pc; - abi_ulong npc; - uint32_t y; - uint32_t magic; -}; -#else -struct target_pt_regs { - abi_ulong psr; - abi_ulong pc; - abi_ulong npc; - abi_ulong y; - abi_ulong u_regs[16]; -}; -#endif - #ifdef TARGET_SPARC64 # define UNAME_MACHINE "sparc64" #else From 3f1b9dbdf5452a2baab00d46bd149f6f8192fe44 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 28 Aug 2025 21:55:01 +1000 Subject: [PATCH 0227/2396] linux-user: Remove target_pt_regs from target_syscall.h All target_pt_regs which have not been broken out to target_ptrace.h by this point are unused. Remove them. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/alpha/target_syscall.h | 40 ----------------------------- linux-user/hexagon/target_syscall.h | 5 ---- linux-user/hppa/target_syscall.h | 18 ------------- linux-user/m68k/target_syscall.h | 16 ------------ linux-user/riscv/target_syscall.h | 35 ------------------------- 5 files changed, 114 deletions(-) diff --git a/linux-user/alpha/target_syscall.h b/linux-user/alpha/target_syscall.h index fda3a49f29..53706b749f 100644 --- a/linux-user/alpha/target_syscall.h +++ b/linux-user/alpha/target_syscall.h @@ -1,46 +1,6 @@ #ifndef ALPHA_TARGET_SYSCALL_H #define ALPHA_TARGET_SYSCALL_H -/* default linux values for the selectors */ -#define __USER_DS (1) - -struct target_pt_regs { - abi_ulong r0; - abi_ulong r1; - abi_ulong r2; - abi_ulong r3; - abi_ulong r4; - abi_ulong r5; - abi_ulong r6; - abi_ulong r7; - abi_ulong r8; - abi_ulong r19; - abi_ulong r20; - abi_ulong r21; - abi_ulong r22; - abi_ulong r23; - abi_ulong r24; - abi_ulong r25; - abi_ulong r26; - abi_ulong r27; - abi_ulong r28; - abi_ulong hae; -/* JRP - These are the values provided to a0-a2 by PALcode */ - abi_ulong trap_a0; - abi_ulong trap_a1; - abi_ulong trap_a2; -/* These are saved by PAL-code: */ - abi_ulong ps; - abi_ulong pc; - abi_ulong gp; - abi_ulong r16; - abi_ulong r17; - abi_ulong r18; -/* Those is needed by qemu to temporary store the user stack pointer */ - abi_ulong usp; - abi_ulong unique; -}; - #define UNAME_MACHINE "alpha" #define UNAME_MINIMUM_RELEASE "2.6.32" diff --git a/linux-user/hexagon/target_syscall.h b/linux-user/hexagon/target_syscall.h index 7f91a4abc7..d9c94737a5 100644 --- a/linux-user/hexagon/target_syscall.h +++ b/linux-user/hexagon/target_syscall.h @@ -18,11 +18,6 @@ #ifndef HEXAGON_TARGET_SYSCALL_H #define HEXAGON_TARGET_SYSCALL_H -struct target_pt_regs { - abi_long sepc; - abi_long sp; -}; - #define UNAME_MACHINE "hexagon" #define UNAME_MINIMUM_RELEASE "4.15.0" diff --git a/linux-user/hppa/target_syscall.h b/linux-user/hppa/target_syscall.h index 9a8f8ca628..4b21e85371 100644 --- a/linux-user/hppa/target_syscall.h +++ b/linux-user/hppa/target_syscall.h @@ -1,24 +1,6 @@ #ifndef HPPA_TARGET_SYSCALL_H #define HPPA_TARGET_SYSCALL_H -struct target_pt_regs { - target_ulong gr[32]; - uint64_t fr[32]; - target_ulong sr[8]; - target_ulong iasq[2]; - target_ulong iaoq[2]; - target_ulong cr27; - target_ulong __pad0; - target_ulong orig_r28; - target_ulong ksp; - target_ulong kpc; - target_ulong sar; - target_ulong iir; - target_ulong isr; - target_ulong ior; - target_ulong ipsw; -}; - #define UNAME_MACHINE "parisc" #define UNAME_MINIMUM_RELEASE "2.6.32" #define TARGET_CLONE_BACKWARDS diff --git a/linux-user/m68k/target_syscall.h b/linux-user/m68k/target_syscall.h index 8d4ddbd76c..3ca0231c70 100644 --- a/linux-user/m68k/target_syscall.h +++ b/linux-user/m68k/target_syscall.h @@ -1,22 +1,6 @@ #ifndef M68K_TARGET_SYSCALL_H #define M68K_TARGET_SYSCALL_H -/* this struct defines the way the registers are stored on the - stack during a system call. */ - -struct target_pt_regs { - abi_long d1, d2, d3, d4, d5, d6, d7; - abi_long a0, a1, a2, a3, a4, a5, a6; - abi_ulong d0; - abi_ulong usp; - abi_ulong orig_d0; - int16_t stkadj; - uint16_t sr; - abi_ulong pc; - uint16_t fntvex; - uint16_t __fill; -}; - #define UNAME_MACHINE "m68k" #define UNAME_MINIMUM_RELEASE "2.6.32" diff --git a/linux-user/riscv/target_syscall.h b/linux-user/riscv/target_syscall.h index 7601f10c28..69a7b753eb 100644 --- a/linux-user/riscv/target_syscall.h +++ b/linux-user/riscv/target_syscall.h @@ -8,41 +8,6 @@ #ifndef LINUX_USER_RISCV_TARGET_SYSCALL_H #define LINUX_USER_RISCV_TARGET_SYSCALL_H -struct target_pt_regs { - abi_long sepc; - abi_long ra; - abi_long sp; - abi_long gp; - abi_long tp; - abi_long t0; - abi_long t1; - abi_long t2; - abi_long s0; - abi_long s1; - abi_long a0; - abi_long a1; - abi_long a2; - abi_long a3; - abi_long a4; - abi_long a5; - abi_long a6; - abi_long a7; - abi_long s2; - abi_long s3; - abi_long s4; - abi_long s5; - abi_long s6; - abi_long s7; - abi_long s8; - abi_long s9; - abi_long s10; - abi_long s11; - abi_long t3; - abi_long t4; - abi_long t5; - abi_long t6; -}; - #ifdef TARGET_RISCV32 #define UNAME_MACHINE "riscv32" #define UNAME_MINIMUM_RELEASE "5.4.0" From a362960b29749750168601f3969ec9364082ee59 Mon Sep 17 00:00:00 2001 From: Gustavo Romero Date: Tue, 26 Aug 2025 11:21:27 +0100 Subject: [PATCH 0228/2396] target/arm: Clean up of register field definitions Clean up the definitions of NSW and NSA fields in the VTCR register. These two fields are already defined properly using FIELD() so they are actually duplications. Also, define the NSW and NSA fields in the VSTCR register using FIELD() and remove their definitions based on VTCR fields. Signed-off-by: Gustavo Romero Message-id: 20250725014755.2122579-1-gustavo.romero@linaro.org Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- target/arm/internals.h | 8 +++----- target/arm/ptw.c | 8 ++++---- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/target/arm/internals.h b/target/arm/internals.h index 1b3d0244fd..3f86b07044 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -113,11 +113,6 @@ FIELD(DBGWCR, WT, 20, 1) FIELD(DBGWCR, MASK, 24, 5) FIELD(DBGWCR, SSCE, 29, 1) -#define VTCR_NSW (1u << 29) -#define VTCR_NSA (1u << 30) -#define VSTCR_SW VTCR_NSW -#define VSTCR_SA VTCR_NSA - /* Bit definitions for CPACR (AArch32 only) */ FIELD(CPACR, CP10, 20, 2) FIELD(CPACR, CP11, 22, 2) @@ -220,6 +215,9 @@ FIELD(VTCR, NSA, 30, 1) FIELD(VTCR, DS, 32, 1) FIELD(VTCR, SL2, 33, 1) +FIELD(VSTCR, SW, 29, 1) +FIELD(VSTCR, SA, 30, 1) + #define HCRX_ENAS0 (1ULL << 0) #define HCRX_ENALS (1ULL << 1) #define HCRX_ENASR (1ULL << 2) diff --git a/target/arm/ptw.c b/target/arm/ptw.c index 561bf2678e..ed5c728eab 100644 --- a/target/arm/ptw.c +++ b/target/arm/ptw.c @@ -193,9 +193,9 @@ static ARMMMUIdx ptw_idx_for_stage_2(CPUARMState *env, ARMMMUIdx stage2idx) return ARMMMUIdx_Phys_Realm; case ARMSS_Secure: if (stage2idx == ARMMMUIdx_Stage2_S) { - s2walk_secure = !(env->cp15.vstcr_el2 & VSTCR_SW); + s2walk_secure = !(env->cp15.vstcr_el2 & R_VSTCR_SW_MASK); } else { - s2walk_secure = !(env->cp15.vtcr_el2 & VTCR_NSW); + s2walk_secure = !(env->cp15.vtcr_el2 & R_VTCR_NSW_MASK); } return s2walk_secure ? ARMMMUIdx_Phys_S : ARMMMUIdx_Phys_NS; default: @@ -3372,9 +3372,9 @@ static bool get_phys_addr_twostage(CPUARMState *env, S1Translate *ptw, */ if (in_space == ARMSS_Secure) { result->f.attrs.secure = - !(env->cp15.vstcr_el2 & (VSTCR_SA | VSTCR_SW)) + !(env->cp15.vstcr_el2 & (R_VSTCR_SA_MASK | R_VSTCR_SW_MASK)) && (ipa_secure - || !(env->cp15.vtcr_el2 & (VTCR_NSA | VTCR_NSW))); + || !(env->cp15.vtcr_el2 & (R_VTCR_NSA_MASK | R_VTCR_NSW_MASK))); result->f.attrs.space = arm_secure_to_space(result->f.attrs.secure); } From 2561626c25e05bc6f673b29f7dd46a20fa6719c3 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Tue, 26 Aug 2025 11:21:27 +0100 Subject: [PATCH 0229/2396] tests/functional/test_aarch64_device_passthrough: update image TF-A needs to be patched to enable support for FEAT_TCR2 and FEAT_SCTLR2. This new image contains updated firmware. Signed-off-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-id: 20250727074202.83141-2-richard.henderson@linaro.org Message-ID: <20250719035838.2284029-2-pierrick.bouvier@linaro.org> Signed-off-by: Peter Maydell --- .../aarch64/test_device_passthrough.py | 27 ++++++++++--------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/tests/functional/aarch64/test_device_passthrough.py b/tests/functional/aarch64/test_device_passthrough.py index 1f3f158a9f..17437784bb 100755 --- a/tests/functional/aarch64/test_device_passthrough.py +++ b/tests/functional/aarch64/test_device_passthrough.py @@ -9,7 +9,7 @@ # # SPDX-License-Identifier: GPL-2.0-or-later -import os +from os.path import join from qemu_test import QemuSystemTest, Asset from qemu_test import exec_command, wait_for_console_pattern @@ -77,15 +77,16 @@ echo device_passthrough_test_ok class Aarch64DevicePassthrough(QemuSystemTest): - # https://github.com/pbo-linaro/qemu-linux-stack + # https://github.com/pbo-linaro/qemu-linux-stack/tree/device_passthrough + # $ ./build.sh && ./archive_artifacts.sh out.tar.xz # # 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') + ('https://github.com/pbo-linaro/qemu-linux-stack/' + 'releases/download/build/device_passthrough-c3fb84a.tar.xz'), + '15ac2b02bed0c0ea8e3e007de0bcfdaf6fd51c1ba98213f841dc7d01d6f72f04') # This tests the device passthrough implementation, by booting a VM # supporting it with two nvme disks attached, and launching a nested VM @@ -96,16 +97,16 @@ class Aarch64DevicePassthrough(QemuSystemTest): self.vm.set_console() - stack_path_tar_gz = self.ASSET_DEVICE_PASSTHROUGH_STACK.fetch() - self.archive_extract(stack_path_tar_gz, format="tar") + stack_path_tar = self.ASSET_DEVICE_PASSTHROUGH_STACK.fetch() + self.archive_extract(stack_path_tar, 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') + kernel = join(stack, 'Image.gz') + rootfs_host = join(stack, 'host.ext4') + disk_vfio = join(stack, 'disk_vfio') + disk_iommufd = join(stack, 'disk_iommufd') + guest_cmd = join(stack, 'guest.sh') + nested_guest_cmd = 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)) From 84249d026bc3878c196ecd3e8558609ba9260eb6 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Tue, 26 Aug 2025 11:21:27 +0100 Subject: [PATCH 0230/2396] tests/functional/test_aarch64_rme: update image MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit TF-A needs to be patched to enable support for FEAT_TCR2 and FEAT_SCTLR2. This new image contains updated firmware. Signed-off-by: Pierrick Bouvier Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Message-id: 20250727074202.83141-3-richard.henderson@linaro.org Message-ID: <20250719035838.2284029-3-pierrick.bouvier@linaro.org> [PMM: switch to os.makedirs(..., exist_ok=True) to improve robustness when re-run after test was cancelled midway] Signed-off-by: Peter Maydell --- tests/functional/aarch64/test_rme_sbsaref.py | 64 ++++++++------- tests/functional/aarch64/test_rme_virt.py | 85 +++++++------------- 2 files changed, 66 insertions(+), 83 deletions(-) diff --git a/tests/functional/aarch64/test_rme_sbsaref.py b/tests/functional/aarch64/test_rme_sbsaref.py index 100f1c7738..ca892e0a8c 100755 --- a/tests/functional/aarch64/test_rme_sbsaref.py +++ b/tests/functional/aarch64/test_rme_sbsaref.py @@ -10,21 +10,23 @@ # SPDX-License-Identifier: GPL-2.0-or-later import os +from os.path import join +import shutil from qemu_test import QemuSystemTest, Asset, wait_for_console_pattern from qemu_test import exec_command_and_wait_for_pattern -from test_rme_virt import test_realms_guest class Aarch64RMESbsaRefMachine(QemuSystemTest): - # Stack is built with OP-TEE build environment from those instructions: + # Stack is inspired from: # https://linaro.atlassian.net/wiki/spaces/QEMU/pages/29051027459/ - # https://github.com/pbo-linaro/qemu-rme-stack + # https://github.com/pbo-linaro/qemu-linux-stack/tree/rme_sbsa_release + # ./build.sh && ./archive_artifacts.sh out.tar.xz 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') + ('https://github.com/pbo-linaro/qemu-linux-stack/' + 'releases/download/build/rme_sbsa_release-a7f02cf.tar.xz'), + '27d8400b11befb828d6db0cab97e7ae102d0992c928d3dfbf38b24b6cf6c324c') # This tests the FEAT_RME cpu implementation, by booting a VM supporting it, # and launching a nested VM using it. @@ -35,35 +37,41 @@ class Aarch64RMESbsaRefMachine(QemuSystemTest): self.vm.set_console() - stack_path_tar_gz = self.ASSET_RME_STACK_SBSA.fetch() - self.archive_extract(stack_path_tar_gz, format="tar") + stack_path_tar = self.ASSET_RME_STACK_SBSA.fetch() + self.archive_extract(stack_path_tar, 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') + rme_stack = self.scratch_file('.') + pflash0 = join(rme_stack, 'out', 'SBSA_FLASH0.fd') + pflash1 = join(rme_stack, 'out', 'SBSA_FLASH1.fd') + rootfs = join(rme_stack, 'out', 'host.ext4') - self.vm.add_args('-cpu', 'max,x-rme=on,pauth-impdef=on') + efi = join(rme_stack, 'out', 'EFI') + os.makedirs(efi, exist_ok=True) + shutil.copyfile(join(rme_stack, 'out', 'Image'), join(efi, 'Image')) + with open(join(efi, 'startup.nsh'), 'w') as startup: + startup.write('fs0:Image nokaslr root=/dev/vda rw init=/init --' + ' /host/out/lkvm run --realm' + ' -m 256m' + ' --restricted_mem' + ' --kernel /host/out/Image' + ' --disk /host/out/guest.ext4' + ' --params "root=/dev/vda rw init=/init"') + + self.vm.add_args('-cpu', 'max,x-rme=on') + self.vm.add_args('-smp', '2') 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.add_args('-drive', f'file=fat:rw:{efi},format=raw') + self.vm.add_args('-drive', f'format=raw,file={rootfs},if=virtio') + self.vm.add_args('-virtfs', + f'local,path={rme_stack}/,mount_tag=host,' + 'security_model=mapped,readonly=off') 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) + # Wait for host and guest VM boot to complete. + wait_for_console_pattern(self, 'root@guest', + failure_message='Kernel panic') if __name__ == '__main__': QemuSystemTest.main() diff --git a/tests/functional/aarch64/test_rme_virt.py b/tests/functional/aarch64/test_rme_virt.py index 8452d27928..bb603aaa26 100755 --- a/tests/functional/aarch64/test_rme_virt.py +++ b/tests/functional/aarch64/test_rme_virt.py @@ -9,50 +9,22 @@ # # SPDX-License-Identifier: GPL-2.0-or-later -import os +from os.path import join 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: + # Stack is inspired from: # https://linaro.atlassian.net/wiki/spaces/QEMU/pages/29051027459/ - # https://github.com/pbo-linaro/qemu-rme-stack + # https://github.com/pbo-linaro/qemu-linux-stack/tree/rme_release + # ./build.sh && ./archive_artifacts.sh out.tar.xz 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') + ('https://github.com/pbo-linaro/qemu-linux-stack/' + 'releases/download/build/rme_release-86101e5.tar.xz'), + 'e42fef8439badb52a071ac446fc33cff4cb7d61314c7a28fdbe61a11e1faad3a') # This tests the FEAT_RME cpu implementation, by booting a VM supporting it, # and launching a nested VM using it. @@ -63,15 +35,16 @@ class Aarch64RMEVirtMachine(QemuSystemTest): self.vm.set_console() - stack_path_tar_gz = self.ASSET_RME_STACK_VIRT.fetch() - self.archive_extract(stack_path_tar_gz, format="tar") + stack_path_tar = self.ASSET_RME_STACK_VIRT.fetch() + self.archive_extract(stack_path_tar, 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') + rme_stack = self.scratch_file('.') + kernel = join(rme_stack, 'out', 'Image') + bios = join(rme_stack, 'out', 'flash.bin') + rootfs = join(rme_stack, 'out', 'host.ext4') - self.vm.add_args('-cpu', 'max,x-rme=on,pauth-impdef=on') + self.vm.add_args('-cpu', 'max,x-rme=on') + self.vm.add_args('-smp', '2') self.vm.add_args('-m', '2G') self.vm.add_args('-M', 'virt,acpi=off,' 'virtualization=on,' @@ -79,23 +52,25 @@ class Aarch64RMEVirtMachine(QemuSystemTest): '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') + self.vm.add_args('-drive', f'format=raw,file={rootfs},if=virtio') + self.vm.add_args('-virtfs', + f'local,path={rme_stack}/,mount_tag=host,' + 'security_model=mapped,readonly=off') # 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.add_args('-append', + 'nokaslr root=/dev/vda rw init=/init --' + ' /host/out/lkvm run --realm' + ' -m 256m' + ' --restricted_mem' + ' --kernel /host/out/Image' + ' --disk /host/out/guest.ext4' + ' --params "root=/dev/vda rw init=/init"') 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) + # Wait for host and guest VM boot to complete. + wait_for_console_pattern(self, 'root@guest', + failure_message='Kernel panic') if __name__ == '__main__': QemuSystemTest.main() From 8a60ffe9a8f46ed514656eb4a40d1386c439daf8 Mon Sep 17 00:00:00 2001 From: Gustavo Romero Date: Tue, 26 Aug 2025 11:21:28 +0100 Subject: [PATCH 0231/2396] target/arm: Implement FEAT_SCTLR2 and enable with -cpu max Add FEAT_SCTLR2, which introduces the SCTLR2_EL1, SCTLR2_EL2, and SCTLR2_EL3 registers. These registers are extensions of the SCTLR_ELx ones. Signed-off-by: Gustavo Romero Reviewed-by: Richard Henderson Signed-off-by: Richard Henderson Message-id: 20250727074202.83141-4-richard.henderson@linaro.org Message-ID: <20250711140828.1714666-4-gustavo.romero@linaro.org> [rth: Remove FEAT_MEC code; handle SCR and HCRX enable bits.] Signed-off-by: Richard Henderson Signed-off-by: Peter Maydell --- docs/system/arm/emulation.rst | 1 + target/arm/cpu-features.h | 5 ++ target/arm/cpu.c | 3 ++ target/arm/cpu.h | 15 ++++++ target/arm/helper.c | 97 ++++++++++++++++++++++++++++++++--- target/arm/internals.h | 1 + target/arm/tcg/cpu64.c | 5 +- 7 files changed, 119 insertions(+), 8 deletions(-) diff --git a/docs/system/arm/emulation.rst b/docs/system/arm/emulation.rst index 890dc6fee2..66043b0747 100644 --- a/docs/system/arm/emulation.rst +++ b/docs/system/arm/emulation.rst @@ -121,6 +121,7 @@ the following architecture extensions: - FEAT_RPRES (Increased precision of FRECPE and FRSQRTE) - FEAT_S2FWB (Stage 2 forced Write-Back) - FEAT_SB (Speculation Barrier) +- FEAT_SCTLR2 (Extension to SCTLR_ELx) - FEAT_SEL2 (Secure EL2) - FEAT_SHA1 (SHA1 instructions) - FEAT_SHA256 (SHA256 instructions) diff --git a/target/arm/cpu-features.h b/target/arm/cpu-features.h index 5876162428..e372543bf3 100644 --- a/target/arm/cpu-features.h +++ b/target/arm/cpu-features.h @@ -904,6 +904,11 @@ static inline bool isar_feature_aa64_nv2(const ARMISARegisters *id) return FIELD_EX64_IDREG(id, ID_AA64MMFR2, NV) >= 2; } +static inline bool isar_feature_aa64_sctlr2(const ARMISARegisters *id) +{ + return FIELD_EX64_IDREG(id, ID_AA64MMFR3, SCTLRX) != 0; +} + static inline bool isar_feature_aa64_pmuv3p1(const ARMISARegisters *id) { return FIELD_EX64_IDREG(id, ID_AA64DFR0, PMUVER) >= 4 && diff --git a/target/arm/cpu.c b/target/arm/cpu.c index e2b2337399..2ab04cb5f7 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -644,6 +644,9 @@ void arm_emulate_firmware_reset(CPUState *cpustate, int target_el) if (cpu_isar_feature(aa64_fgt, cpu)) { env->cp15.scr_el3 |= SCR_FGTEN; } + if (cpu_isar_feature(aa64_sctlr2, cpu)) { + env->cp15.scr_el3 |= SCR_SCTLR2EN; + } } if (target_el == 2) { diff --git a/target/arm/cpu.h b/target/arm/cpu.h index dc9b6dce4c..08a29802e1 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -337,6 +337,7 @@ typedef struct CPUArchState { }; uint64_t sctlr_el[4]; }; + uint64_t sctlr2_el[4]; /* Extension to System control register. */ uint64_t vsctlr; /* Virtualization System control register. */ uint64_t cpacr_el1; /* Architectural feature access control register */ uint64_t cptr_el[4]; /* ARMv8 feature trap registers */ @@ -1420,6 +1421,19 @@ void pmu_init(ARMCPU *cpu); #define SCTLR_SPINTMASK (1ULL << 62) /* FEAT_NMI */ #define SCTLR_TIDCP (1ULL << 63) /* FEAT_TIDCP1 */ +#define SCTLR2_EMEC (1ULL << 1) /* FEAT_MEC */ +#define SCTLR2_NMEA (1ULL << 2) /* FEAT_DoubleFault2 */ +#define SCTLR2_ENADERR (1ULL << 3) /* FEAT_ADERR */ +#define SCTLR2_ENANERR (1ULL << 4) /* FEAT_ANERR */ +#define SCTLR2_EASE (1ULL << 5) /* FEAT_DoubleFault2 */ +#define SCTLR2_ENIDCP128 (1ULL << 6) /* FEAT_SYSREG128 */ +#define SCTLR2_ENPACM (1ULL << 7) /* FEAT_PAuth_LR */ +#define SCTLR2_ENPACM0 (1ULL << 8) /* FEAT_PAuth_LR */ +#define SCTLR2_CPTA (1ULL << 9) /* FEAT_CPA2 */ +#define SCTLR2_CPTA0 (1ULL << 10) /* FEAT_CPA2 */ +#define SCTLR2_CPTM (1ULL << 11) /* FEAT_CPA2 */ +#define SCTLR2_CPTM0 (1ULL << 12) /* FEAT_CAP2 */ + #define CPSR_M (0x1fU) #define CPSR_T (1U << 5) #define CPSR_F (1U << 6) @@ -1712,6 +1726,7 @@ static inline void xpsr_write(CPUARMState *env, uint32_t val, uint32_t mask) #define SCR_HXEN (1ULL << 38) #define SCR_TRNDR (1ULL << 40) #define SCR_ENTP2 (1ULL << 41) +#define SCR_SCTLR2EN (1ULL << 44) #define SCR_GPF (1ULL << 48) #define SCR_NSE (1ULL << 62) diff --git a/target/arm/helper.c b/target/arm/helper.c index 0c1299ff84..11ddeabb13 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -741,6 +741,9 @@ static void scr_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) if (cpu_isar_feature(aa64_ecv, cpu)) { valid_mask |= SCR_ECVEN; } + if (cpu_isar_feature(aa64_sctlr2, cpu)) { + valid_mask |= SCR_SCTLR2EN; + } } else { valid_mask &= ~(SCR_RW | SCR_ST); if (cpu_isar_feature(aa32_ras, cpu)) { @@ -3907,23 +3910,21 @@ static void hcrx_write(CPUARMState *env, const ARMCPRegInfo *ri, ARMCPU *cpu = env_archcpu(env); uint64_t valid_mask = 0; - /* FEAT_MOPS adds MSCEn and MCE2 */ if (cpu_isar_feature(aa64_mops, cpu)) { valid_mask |= HCRX_MSCEN | HCRX_MCE2; } - - /* FEAT_NMI adds TALLINT, VINMI and VFNMI */ if (cpu_isar_feature(aa64_nmi, cpu)) { valid_mask |= HCRX_TALLINT | HCRX_VINMI | HCRX_VFNMI; } - /* FEAT_CMOW adds CMOW */ if (cpu_isar_feature(aa64_cmow, cpu)) { valid_mask |= HCRX_CMOW; } - /* FEAT_XS adds FGTnXS, FnXS */ if (cpu_isar_feature(aa64_xs, cpu)) { valid_mask |= HCRX_FGTNXS | HCRX_FNXS; } + if (cpu_isar_feature(aa64_sctlr2, cpu)) { + valid_mask |= HCRX_SCTLR2EN; + } /* Clear RES0 bits. */ env->cp15.hcrx_el2 = value & valid_mask; @@ -3981,11 +3982,16 @@ uint64_t arm_hcrx_el2_eff(CPUARMState *env) * This may need to be revisited for future bits. */ if (!arm_is_el2_enabled(env)) { + ARMCPU *cpu = env_archcpu(env); uint64_t hcrx = 0; - if (cpu_isar_feature(aa64_mops, env_archcpu(env))) { - /* MSCEn behaves as 1 if EL2 is not enabled */ + + /* Bits which whose effective value is 1 if el2 not enabled. */ + if (cpu_isar_feature(aa64_mops, cpu)) { hcrx |= HCRX_MSCEN; } + if (cpu_isar_feature(aa64_sctlr2, cpu)) { + hcrx |= HCRX_SCTLR2EN; + } return hcrx; } if (arm_feature(env, ARM_FEATURE_EL3) && !(env->cp15.scr_el3 & SCR_HXEN)) { @@ -4513,6 +4519,8 @@ static void define_arm_vh_e2h_redirects_aliases(ARMCPU *cpu) static const struct E2HAlias aliases[] = { { K(3, 0, 1, 0, 0), K(3, 4, 1, 0, 0), K(3, 5, 1, 0, 0), "SCTLR", "SCTLR_EL2", "SCTLR_EL12" }, + { K(3, 0, 1, 0, 3), K(3, 4, 1, 0, 3), K(3, 5, 1, 0, 3), + "SCTLR2_EL1", "SCTLR2_EL2", "SCTLR2_EL12", isar_feature_aa64_sctlr2 }, { K(3, 0, 1, 0, 2), K(3, 4, 1, 1, 2), K(3, 5, 1, 0, 2), "CPACR", "CPTR_EL2", "CPACR_EL12" }, { K(3, 0, 2, 0, 0), K(3, 4, 2, 0, 0), K(3, 5, 2, 0, 0), @@ -5994,6 +6002,77 @@ static const ARMCPRegInfo actlr2_hactlr2_reginfo[] = { .resetvalue = 0 }, }; +static CPAccessResult sctlr2_el2_access(CPUARMState *env, + const ARMCPRegInfo *ri, + bool isread) +{ + if (arm_current_el(env) < 3 + && arm_feature(env, ARM_FEATURE_EL3) + && !(env->cp15.scr_el3 & SCR_SCTLR2EN)) { + return CP_ACCESS_TRAP_EL3; + } + return CP_ACCESS_OK; +} + +static CPAccessResult sctlr2_el1_access(CPUARMState *env, + const ARMCPRegInfo *ri, + bool isread) +{ + CPAccessResult ret = access_tvm_trvm(env, ri, isread); + if (ret != CP_ACCESS_OK) { + return ret; + } + if (arm_current_el(env) < 2 && !(arm_hcrx_el2_eff(env) & HCRX_SCTLR2EN)) { + return CP_ACCESS_TRAP_EL2; + } + return sctlr2_el2_access(env, ri, isread); +} + +static void sctlr2_el1_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + uint64_t valid_mask = 0; + + value &= valid_mask; + raw_write(env, ri, value); +} + +static void sctlr2_el2_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + uint64_t valid_mask = 0; + + value &= valid_mask; + raw_write(env, ri, value); +} + +static void sctlr2_el3_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + uint64_t valid_mask = 0; + + value &= valid_mask; + raw_write(env, ri, value); +} + +static const ARMCPRegInfo sctlr2_reginfo[] = { + { .name = "SCTLR2_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .opc2 = 3, .crn = 1, .crm = 0, + .access = PL1_RW, .accessfn = sctlr2_el1_access, + .writefn = sctlr2_el1_write, .fgt = FGT_SCTLR_EL1, + .nv2_redirect_offset = 0x278 | NV2_REDIR_NV1, + .fieldoffset = offsetof(CPUARMState, cp15.sctlr2_el[1]) }, + { .name = "SCTLR2_EL2", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 4, .opc2 = 3, .crn = 1, .crm = 0, + .access = PL2_RW, .accessfn = sctlr2_el2_access, + .writefn = sctlr2_el2_write, + .fieldoffset = offsetof(CPUARMState, cp15.sctlr2_el[2]) }, + { .name = "SCTLR2_EL3", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 6, .opc2 = 3, .crn = 1, .crm = 0, + .access = PL3_RW, .writefn = sctlr2_el3_write, + .fieldoffset = offsetof(CPUARMState, cp15.sctlr2_el[3]) }, +}; + void register_cp_regs_for_features(ARMCPU *cpu) { /* Register all the coprocessor registers based on feature bits */ @@ -7223,6 +7302,10 @@ void register_cp_regs_for_features(ARMCPU *cpu) define_arm_cp_regs(cpu, nmi_reginfo); } + if (cpu_isar_feature(aa64_sctlr2, cpu)) { + define_arm_cp_regs(cpu, sctlr2_reginfo); + } + if (cpu_isar_feature(any_predinv, cpu)) { define_arm_cp_regs(cpu, predinv_reginfo); } diff --git a/target/arm/internals.h b/target/arm/internals.h index 3f86b07044..fb72236255 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -230,6 +230,7 @@ FIELD(VSTCR, SA, 30, 1) #define HCRX_CMOW (1ULL << 9) #define HCRX_MCE2 (1ULL << 10) #define HCRX_MSCEN (1ULL << 11) +#define HCRX_SCTLR2EN (1ULL << 15) #define HPFAR_NS (1ULL << 63) diff --git a/target/arm/tcg/cpu64.c b/target/arm/tcg/cpu64.c index 35cddbafa4..f4efff03a5 100644 --- a/target/arm/tcg/cpu64.c +++ b/target/arm/tcg/cpu64.c @@ -1247,7 +1247,10 @@ void aarch64_max_tcg_initfn(Object *obj) t = FIELD_DP64(t, ID_AA64MMFR2, E0PD, 1); /* FEAT_E0PD */ SET_IDREG(isar, ID_AA64MMFR2, t); - FIELD_DP64_IDREG(isar, ID_AA64MMFR3, SPEC_FPACC, 1); /* FEAT_FPACC_SPEC */ + t = GET_IDREG(isar, ID_AA64MMFR3); + t = FIELD_DP64(t, ID_AA64MMFR3, SCTLRX, 1); /* FEAT_SCTLR2 */ + t = FIELD_DP64(t, ID_AA64MMFR3, SPEC_FPACC, 1); /* FEAT_FPACC_SPEC */ + SET_IDREG(isar, ID_AA64MMFR3, t); t = GET_IDREG(isar, ID_AA64ZFR0); t = FIELD_DP64(t, ID_AA64ZFR0, SVEVER, 2); /* FEAT_SVE2p1 */ From 6e6d5fb4b928f09b46d0fa99830d75cddb22f73b Mon Sep 17 00:00:00 2001 From: Gustavo Romero Date: Tue, 26 Aug 2025 11:21:28 +0100 Subject: [PATCH 0232/2396] target/arm: Implement FEAT_TCR2 and enable with -cpu max Add FEAT_TCR2, which introduces the TCR2_EL1 and TCR2_EL2 registers. These registers are extensions of the TCR_ELx registers and provide top-level control of the EL10 and EL20 translation regimes. Signed-off-by: Gustavo Romero Signed-off-by: Richard Henderson Reviewed-by: Richard Henderson Message-id: 20250727074202.83141-5-richard.henderson@linaro.org Message-ID: <20250711140828.1714666-5-gustavo.romero@linaro.org> Reviewed-by: Richard Henderson [rth: Remove FEAT_MEC code; handle SCR and HCRX enable bits.] Signed-off-by: Richard Henderson Signed-off-by: Peter Maydell --- docs/system/arm/emulation.rst | 1 + target/arm/cpu-features.h | 5 +++ target/arm/cpu.c | 3 ++ target/arm/cpu.h | 2 + target/arm/helper.c | 71 +++++++++++++++++++++++++++++++++++ target/arm/internals.h | 19 ++++++++++ target/arm/tcg/cpu64.c | 1 + 7 files changed, 102 insertions(+) diff --git a/docs/system/arm/emulation.rst b/docs/system/arm/emulation.rst index 66043b0747..1c597d8673 100644 --- a/docs/system/arm/emulation.rst +++ b/docs/system/arm/emulation.rst @@ -149,6 +149,7 @@ the following architecture extensions: - FEAT_SPECRES (Speculation restriction instructions) - FEAT_SSBS (Speculative Store Bypass Safe) - FEAT_SSBS2 (MRS and MSR instructions for SSBS version 2) +- FEAT_TCR2 (Support for TCR2_ELx) - FEAT_TGran16K (Support for 16KB memory translation granule size at stage 1) - FEAT_TGran4K (Support for 4KB memory translation granule size at stage 1) - FEAT_TGran64K (Support for 64KB memory translation granule size at stage 1) diff --git a/target/arm/cpu-features.h b/target/arm/cpu-features.h index e372543bf3..8ec8c3feb3 100644 --- a/target/arm/cpu-features.h +++ b/target/arm/cpu-features.h @@ -904,6 +904,11 @@ static inline bool isar_feature_aa64_nv2(const ARMISARegisters *id) return FIELD_EX64_IDREG(id, ID_AA64MMFR2, NV) >= 2; } +static inline bool isar_feature_aa64_tcr2(const ARMISARegisters *id) +{ + return FIELD_EX64_IDREG(id, ID_AA64MMFR3, TCRX) != 0; +} + static inline bool isar_feature_aa64_sctlr2(const ARMISARegisters *id) { return FIELD_EX64_IDREG(id, ID_AA64MMFR3, SCTLRX) != 0; diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 2ab04cb5f7..27a4610da5 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -644,6 +644,9 @@ void arm_emulate_firmware_reset(CPUState *cpustate, int target_el) if (cpu_isar_feature(aa64_fgt, cpu)) { env->cp15.scr_el3 |= SCR_FGTEN; } + if (cpu_isar_feature(aa64_tcr2, cpu)) { + env->cp15.scr_el3 |= SCR_TCR2EN; + } if (cpu_isar_feature(aa64_sctlr2, cpu)) { env->cp15.scr_el3 |= SCR_SCTLR2EN; } diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 08a29802e1..c15d79a106 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -366,6 +366,7 @@ typedef struct CPUArchState { uint64_t vsttbr_el2; /* Secure Virtualization Translation Table. */ /* MMU translation table base control. */ uint64_t tcr_el[4]; + uint64_t tcr2_el[3]; uint64_t vtcr_el2; /* Virtualization Translation Control. */ uint64_t vstcr_el2; /* Secure Virtualization Translation Control. */ uint32_t c2_data; /* MPU data cacheable bits. */ @@ -1726,6 +1727,7 @@ static inline void xpsr_write(CPUARMState *env, uint32_t val, uint32_t mask) #define SCR_HXEN (1ULL << 38) #define SCR_TRNDR (1ULL << 40) #define SCR_ENTP2 (1ULL << 41) +#define SCR_TCR2EN (1ULL << 43) #define SCR_SCTLR2EN (1ULL << 44) #define SCR_GPF (1ULL << 48) #define SCR_NSE (1ULL << 62) diff --git a/target/arm/helper.c b/target/arm/helper.c index 11ddeabb13..5a219703ae 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -741,6 +741,9 @@ static void scr_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) if (cpu_isar_feature(aa64_ecv, cpu)) { valid_mask |= SCR_ECVEN; } + if (cpu_isar_feature(aa64_tcr2, cpu)) { + valid_mask |= SCR_TCR2EN; + } if (cpu_isar_feature(aa64_sctlr2, cpu)) { valid_mask |= SCR_SCTLR2EN; } @@ -3922,6 +3925,9 @@ static void hcrx_write(CPUARMState *env, const ARMCPRegInfo *ri, if (cpu_isar_feature(aa64_xs, cpu)) { valid_mask |= HCRX_FGTNXS | HCRX_FNXS; } + if (cpu_isar_feature(aa64_tcr2, cpu)) { + valid_mask |= HCRX_TCR2EN; + } if (cpu_isar_feature(aa64_sctlr2, cpu)) { valid_mask |= HCRX_SCTLR2EN; } @@ -3989,6 +3995,9 @@ uint64_t arm_hcrx_el2_eff(CPUARMState *env) if (cpu_isar_feature(aa64_mops, cpu)) { hcrx |= HCRX_MSCEN; } + if (cpu_isar_feature(aa64_tcr2, cpu)) { + hcrx |= HCRX_TCR2EN; + } if (cpu_isar_feature(aa64_sctlr2, cpu)) { hcrx |= HCRX_SCTLR2EN; } @@ -4529,6 +4538,8 @@ static void define_arm_vh_e2h_redirects_aliases(ARMCPU *cpu) "TTBR1_EL1", "TTBR1_EL2", "TTBR1_EL12" }, { K(3, 0, 2, 0, 2), K(3, 4, 2, 0, 2), K(3, 5, 2, 0, 2), "TCR_EL1", "TCR_EL2", "TCR_EL12" }, + { K(3, 0, 2, 0, 3), K(3, 4, 2, 0, 3), K(3, 5, 2, 0, 3), + "TCR2_EL1", "TCR2_EL2", "TCR2_EL12", isar_feature_aa64_tcr2 }, { K(3, 0, 4, 0, 0), K(3, 4, 4, 0, 0), K(3, 5, 4, 0, 0), "SPSR_EL1", "SPSR_EL2", "SPSR_EL12" }, { K(3, 0, 4, 0, 1), K(3, 4, 4, 0, 1), K(3, 5, 4, 0, 1), @@ -6073,6 +6084,62 @@ static const ARMCPRegInfo sctlr2_reginfo[] = { .fieldoffset = offsetof(CPUARMState, cp15.sctlr2_el[3]) }, }; +static CPAccessResult tcr2_el2_access(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread) +{ + if (arm_current_el(env) < 3 + && arm_feature(env, ARM_FEATURE_EL3) + && !(env->cp15.scr_el3 & SCR_TCR2EN)) { + return CP_ACCESS_TRAP_EL3; + } + return CP_ACCESS_OK; +} + +static CPAccessResult tcr2_el1_access(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread) +{ + CPAccessResult ret = access_tvm_trvm(env, ri, isread); + if (ret != CP_ACCESS_OK) { + return ret; + } + if (arm_current_el(env) < 2 && !(arm_hcrx_el2_eff(env) & HCRX_TCR2EN)) { + return CP_ACCESS_TRAP_EL2; + } + return tcr2_el2_access(env, ri, isread); +} + +static void tcr2_el1_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + uint64_t valid_mask = 0; + + value &= valid_mask; + raw_write(env, ri, value); +} + +static void tcr2_el2_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + uint64_t valid_mask = 0; + + value &= valid_mask; + raw_write(env, ri, value); +} + +static const ARMCPRegInfo tcr2_reginfo[] = { + { .name = "TCR2_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .opc2 = 3, .crn = 2, .crm = 0, + .access = PL1_RW, .accessfn = tcr2_el1_access, + .writefn = tcr2_el1_write, .fgt = FGT_TCR_EL1, + .nv2_redirect_offset = 0x270 | NV2_REDIR_NV1, + .fieldoffset = offsetof(CPUARMState, cp15.tcr2_el[1]) }, + { .name = "TCR2_EL2", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 4, .opc2 = 3, .crn = 2, .crm = 0, + .access = PL2_RW, .accessfn = tcr2_el2_access, + .writefn = tcr2_el2_write, + .fieldoffset = offsetof(CPUARMState, cp15.tcr2_el[2]) }, +}; + void register_cp_regs_for_features(ARMCPU *cpu) { /* Register all the coprocessor registers based on feature bits */ @@ -7306,6 +7373,10 @@ void register_cp_regs_for_features(ARMCPU *cpu) define_arm_cp_regs(cpu, sctlr2_reginfo); } + if (cpu_isar_feature(aa64_tcr2, cpu)) { + define_arm_cp_regs(cpu, tcr2_reginfo); + } + if (cpu_isar_feature(any_predinv, cpu)) { define_arm_cp_regs(cpu, predinv_reginfo); } diff --git a/target/arm/internals.h b/target/arm/internals.h index fb72236255..f5a1e75db3 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -196,6 +196,24 @@ FIELD(CPTR_EL3, TCPAC, 31, 1) #define TTBCR_SH1 (1U << 28) #define TTBCR_EAE (1U << 31) +#define TCR2_PNCH (1ULL << 0) +#define TCR2_PIE (1ULL << 1) +#define TCR2_E0POE (1ULL << 2) +#define TCR2_POE (1ULL << 3) +#define TCR2_AIE (1ULL << 4) +#define TCR2_D128 (1ULL << 5) +#define TCR2_PTTWI (1ULL << 10) +#define TCR2_HAFT (1ULL << 11) +#define TCR2_AMEC0 (1ULL << 12) +#define TCR2_AMEC1 (1ULL << 13) +#define TCR2_DISCH0 (1ULL << 14) +#define TCR2_DISCH1 (1ULL << 15) +#define TCR2_A2 (1ULL << 16) +#define TCR2_FNG0 (1ULL << 17) +#define TCR2_FNG1 (1ULL << 18) +#define TCR2_FNGNA0 (1ULL << 20) +#define TCR2_FNGNA1 (1ULL << 21) + FIELD(VTCR, T0SZ, 0, 6) FIELD(VTCR, SL0, 6, 2) FIELD(VTCR, IRGN0, 8, 2) @@ -230,6 +248,7 @@ FIELD(VSTCR, SA, 30, 1) #define HCRX_CMOW (1ULL << 9) #define HCRX_MCE2 (1ULL << 10) #define HCRX_MSCEN (1ULL << 11) +#define HCRX_TCR2EN (1ULL << 14) #define HCRX_SCTLR2EN (1ULL << 15) #define HPFAR_NS (1ULL << 63) diff --git a/target/arm/tcg/cpu64.c b/target/arm/tcg/cpu64.c index f4efff03a5..4eb51420ef 100644 --- a/target/arm/tcg/cpu64.c +++ b/target/arm/tcg/cpu64.c @@ -1248,6 +1248,7 @@ void aarch64_max_tcg_initfn(Object *obj) SET_IDREG(isar, ID_AA64MMFR2, t); t = GET_IDREG(isar, ID_AA64MMFR3); + t = FIELD_DP64(t, ID_AA64MMFR3, TCRX, 1); /* FEAT_TCR2 */ t = FIELD_DP64(t, ID_AA64MMFR3, SCTLRX, 1); /* FEAT_SCTLR2 */ t = FIELD_DP64(t, ID_AA64MMFR3, SPEC_FPACC, 1); /* FEAT_FPACC_SPEC */ SET_IDREG(isar, ID_AA64MMFR3, t); From 376cdd7e9c94f1e03b2c58e068e8ebfe78b49514 Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Tue, 26 Aug 2025 11:21:28 +0100 Subject: [PATCH 0233/2396] hw/intc/arm_gicv3_kvm: preserve pending interrupts during cpr Close a race condition that causes cpr-transfer to lose VFIO interrupts on ARM. CPR stops VCPUs but does not disable VFIO interrupts, which may continue to arrive throughout the transition to new QEMU. CPR calls kvm_irqchip_remove_irqfd_notifier_gsi in old QEMU to force future interrupts to the producer eventfd, where they are preserved. Old QEMU then destroys the old KVM instance. However, interrupts may already be pending in KVM state. To preserve them, call ioctl KVM_DEV_ARM_VGIC_SAVE_PENDING_TABLES to flush them to guest RAM, where they will be picked up when the new KVM+VCPU instance is created. Cc: qemu-stable@nongnu.org Signed-off-by: Steve Sistare Reviewed-by: Fabiano Rosas Message-id: 1754936384-278328-1-git-send-email-steven.sistare@oracle.com Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/intc/arm_gicv3_kvm.c | 15 +++++++++++++++ include/hw/intc/arm_gicv3_common.h | 3 +++ 2 files changed, 18 insertions(+) diff --git a/hw/intc/arm_gicv3_kvm.c b/hw/intc/arm_gicv3_kvm.c index 6166283cd1..0cd14d78a7 100644 --- a/hw/intc/arm_gicv3_kvm.c +++ b/hw/intc/arm_gicv3_kvm.c @@ -31,6 +31,7 @@ #include "gicv3_internal.h" #include "vgic_common.h" #include "migration/blocker.h" +#include "migration/misc.h" #include "qom/object.h" #include "target/arm/cpregs.h" @@ -776,6 +777,17 @@ static void vm_change_state_handler(void *opaque, bool running, } } +static int kvm_arm_gicv3_notifier(NotifierWithReturn *notifier, + MigrationEvent *e, Error **errp) +{ + if (e->type == MIG_EVENT_PRECOPY_DONE) { + GICv3State *s = container_of(notifier, GICv3State, cpr_notifier); + return kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_CTRL, + KVM_DEV_ARM_VGIC_SAVE_PENDING_TABLES, + NULL, true, errp); + } + return 0; +} static void kvm_arm_gicv3_realize(DeviceState *dev, Error **errp) { @@ -917,6 +929,9 @@ static void kvm_arm_gicv3_realize(DeviceState *dev, Error **errp) if (kvm_device_check_attr(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_CTRL, KVM_DEV_ARM_VGIC_SAVE_PENDING_TABLES)) { qemu_add_vm_change_state_handler(vm_change_state_handler, s); + migration_add_notifier_mode(&s->cpr_notifier, + kvm_arm_gicv3_notifier, + MIG_MODE_CPR_TRANSFER); } } diff --git a/include/hw/intc/arm_gicv3_common.h b/include/hw/intc/arm_gicv3_common.h index c18503869f..572d971d22 100644 --- a/include/hw/intc/arm_gicv3_common.h +++ b/include/hw/intc/arm_gicv3_common.h @@ -27,6 +27,7 @@ #include "hw/sysbus.h" #include "hw/intc/arm_gic_common.h" #include "qom/object.h" +#include "qemu/notify.h" /* * Maximum number of possible interrupts, determined by the GIC architecture. @@ -271,6 +272,8 @@ struct GICv3State { GICv3CPUState *cpu; /* List of all ITSes connected to this GIC */ GPtrArray *itslist; + + NotifierWithReturn cpr_notifier; }; #define GICV3_BITMAP_ACCESSORS(BMP) \ From 186db6a73bc5c01026bb9f4f4a59e442c0156841 Mon Sep 17 00:00:00 2001 From: Smail AIDER Date: Tue, 26 Aug 2025 11:21:28 +0100 Subject: [PATCH 0234/2396] target/arm: Trap PMCR when MDCR_EL2.TPMCR is set Trap PMCR_EL0 or PMCR accesses to EL2 when MDCR_EL2.TPMCR is set. Similar to MDCR_EL2.TPM, MDCR_EL2.TPMCR allows trapping EL0 and EL1 accesses to the PMCR register to EL2. Cc: qemu-stable@nongnu.org Signed-off-by: Smail AIDER Reviewed-by: Richard Henderson Message-id: 20250811112143.1577055-2-smail.aider@huawei.com Message-Id: <20250722131925.2119169-1-smail.aider@huawei.com> Signed-off-by: Peter Maydell --- target/arm/cpregs-pmu.c | 34 ++++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/target/arm/cpregs-pmu.c b/target/arm/cpregs-pmu.c index 9c4431c18b..31c01eddc8 100644 --- a/target/arm/cpregs-pmu.c +++ b/target/arm/cpregs-pmu.c @@ -228,22 +228,27 @@ static bool event_supported(uint16_t number) return supported_event_map[number] != UNSUPPORTED_EVENT; } -static CPAccessResult pmreg_access(CPUARMState *env, const ARMCPRegInfo *ri, - bool isread) +static CPAccessResult do_pmreg_access(CPUARMState *env, bool is_pmcr) { /* * Performance monitor registers user accessibility is controlled - * by PMUSERENR. MDCR_EL2.TPM and MDCR_EL3.TPM allow configurable + * by PMUSERENR. MDCR_EL2.TPM/TPMCR and MDCR_EL3.TPM allow configurable * trapping to EL2 or EL3 for other accesses. */ int el = arm_current_el(env); - uint64_t mdcr_el2 = arm_mdcr_el2_eff(env); if (el == 0 && !(env->cp15.c9_pmuserenr & 1)) { return CP_ACCESS_TRAP_EL1; } - if (el < 2 && (mdcr_el2 & MDCR_TPM)) { - return CP_ACCESS_TRAP_EL2; + if (el < 2) { + uint64_t mdcr_el2 = arm_mdcr_el2_eff(env); + + if (mdcr_el2 & MDCR_TPM) { + return CP_ACCESS_TRAP_EL2; + } + if (is_pmcr && (mdcr_el2 & MDCR_TPMCR)) { + return CP_ACCESS_TRAP_EL2; + } } if (el < 3 && (env->cp15.mdcr_el3 & MDCR_TPM)) { return CP_ACCESS_TRAP_EL3; @@ -252,6 +257,19 @@ static CPAccessResult pmreg_access(CPUARMState *env, const ARMCPRegInfo *ri, return CP_ACCESS_OK; } +static CPAccessResult pmreg_access(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread) +{ + return do_pmreg_access(env, false); +} + +static CPAccessResult pmreg_access_pmcr(CPUARMState *env, + const ARMCPRegInfo *ri, + bool isread) +{ + return do_pmreg_access(env, true); +} + static CPAccessResult pmreg_access_xevcntr(CPUARMState *env, const ARMCPRegInfo *ri, bool isread) @@ -1187,14 +1205,14 @@ void define_pm_cpregs(ARMCPU *cpu) .fgt = FGT_PMCR_EL0, .type = ARM_CP_IO | ARM_CP_ALIAS, .fieldoffset = offsetoflow32(CPUARMState, cp15.c9_pmcr), - .accessfn = pmreg_access, + .accessfn = pmreg_access_pmcr, .readfn = pmcr_read, .raw_readfn = raw_read, .writefn = pmcr_write, .raw_writefn = raw_write, }; const ARMCPRegInfo pmcr64 = { .name = "PMCR_EL0", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 12, .opc2 = 0, - .access = PL0_RW, .accessfn = pmreg_access, + .access = PL0_RW, .accessfn = pmreg_access_pmcr, .fgt = FGT_PMCR_EL0, .type = ARM_CP_IO, .fieldoffset = offsetof(CPUARMState, cp15.c9_pmcr), From 3b53af353b0b2e00fced6f00de87c03346542665 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 26 Aug 2025 11:21:29 +0100 Subject: [PATCH 0235/2396] target/arm: Add feature predicate for FEAT_CSSC Signed-off-by: Richard Henderson Reviewed-by: Peter Maydell Message-id: 20250803014019.416797-2-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/cpu-features.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/target/arm/cpu-features.h b/target/arm/cpu-features.h index 8ec8c3feb3..41511d0835 100644 --- a/target/arm/cpu-features.h +++ b/target/arm/cpu-features.h @@ -604,6 +604,11 @@ static inline bool isar_feature_aa64_rpres(const ARMISARegisters *id) return FIELD_EX64_IDREG(id, ID_AA64ISAR2, RPRES); } +static inline bool isar_feature_aa64_cssc(const ARMISARegisters *id) +{ + return FIELD_EX64_IDREG(id, ID_AA64ISAR2, CSSC) != 0; +} + static inline bool isar_feature_aa64_lut(const ARMISARegisters *id) { return FIELD_EX64_IDREG(id, ID_AA64ISAR2, LUT); From d0e4b9d4d77ab3685fc22b71de0f4fd220afa17a Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 26 Aug 2025 11:21:29 +0100 Subject: [PATCH 0236/2396] target/arm: Implement MIN/MAX (immediate) Signed-off-by: Richard Henderson Reviewed-by: Peter Maydell Message-id: 20250803014019.416797-3-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/a64.decode | 10 ++++++++ target/arm/tcg/translate-a64.c | 44 ++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index 8c798cde2b..c1811b0274 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -156,6 +156,16 @@ MOVZ . 10 100101 .. ................ ..... @movw_32 MOVK . 11 100101 .. ................ ..... @movw_64 MOVK . 11 100101 .. ................ ..... @movw_32 +# Min/Max (immediate) + +@minmaxi_s sf:1 .. ........... imm:s8 rn:5 rd:5 &rri_sf +@minmaxi_u sf:1 .. ........... imm:8 rn:5 rd:5 &rri_sf + +SMAX_i . 00 1000111 0000 ........ ..... ..... @minmaxi_s +SMIN_i . 00 1000111 0010 ........ ..... ..... @minmaxi_s +UMAX_i . 00 1000111 0001 ........ ..... ..... @minmaxi_u +UMIN_i . 00 1000111 0011 ........ ..... ..... @minmaxi_u + # Bitfield &bitfield rd rn sf immr imms diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index dbf47595db..b70ae5befd 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -4552,6 +4552,50 @@ TRANS(SUB_i, gen_rri, a, 1, 1, tcg_gen_sub_i64) TRANS(ADDS_i, gen_rri, a, 0, 1, a->sf ? gen_add64_CC : gen_add32_CC) TRANS(SUBS_i, gen_rri, a, 0, 1, a->sf ? gen_sub64_CC : gen_sub32_CC) +/* + * Min/Max (immediate) + */ + +static void gen_wrap3_i32(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m, NeonGenTwoOpFn fn) +{ + TCGv_i32 t1 = tcg_temp_new_i32(); + TCGv_i32 t2 = tcg_temp_new_i32(); + + tcg_gen_extrl_i64_i32(t1, n); + tcg_gen_extrl_i64_i32(t2, m); + fn(t1, t1, t2); + tcg_gen_extu_i32_i64(d, t1); +} + +static void gen_smax32_i64(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m) +{ + gen_wrap3_i32(d, n, m, tcg_gen_smax_i32); +} + +static void gen_smin32_i64(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m) +{ + gen_wrap3_i32(d, n, m, tcg_gen_smin_i32); +} + +static void gen_umax32_i64(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m) +{ + gen_wrap3_i32(d, n, m, tcg_gen_umax_i32); +} + +static void gen_umin32_i64(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m) +{ + gen_wrap3_i32(d, n, m, tcg_gen_umin_i32); +} + +TRANS_FEAT(SMAX_i, aa64_cssc, gen_rri, a, 0, 0, + a->sf ? tcg_gen_smax_i64 : gen_smax32_i64) +TRANS_FEAT(SMIN_i, aa64_cssc, gen_rri, a, 0, 0, + a->sf ? tcg_gen_smin_i64 : gen_smin32_i64) +TRANS_FEAT(UMAX_i, aa64_cssc, gen_rri, a, 0, 0, + a->sf ? tcg_gen_umax_i64 : gen_umax32_i64) +TRANS_FEAT(UMIN_i, aa64_cssc, gen_rri, a, 0, 0, + a->sf ? tcg_gen_umin_i64 : gen_umin32_i64) + /* * Add/subtract (immediate, with tags) */ From 506538208ddabc9b15b02a7e865aa1b64f9f18e5 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 26 Aug 2025 11:21:29 +0100 Subject: [PATCH 0237/2396] target/arm: Implement MIN/MAX (register) Signed-off-by: Richard Henderson Reviewed-by: Peter Maydell Message-id: 20250803014019.416797-4-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/a64.decode | 5 +++++ target/arm/tcg/translate-a64.c | 22 ++++++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index c1811b0274..a886b3ba4c 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -708,6 +708,11 @@ GMI 1 00 11010110 ..... 000101 ..... ..... @rrr PACGA 1 00 11010110 ..... 001100 ..... ..... @rrr +SMAX . 00 11010110 ..... 011000 ..... ..... @rrr_sf +SMIN . 00 11010110 ..... 011010 ..... ..... @rrr_sf +UMAX . 00 11010110 ..... 011001 ..... ..... @rrr_sf +UMIN . 00 11010110 ..... 011011 ..... ..... @rrr_sf + # Data Processing (1-source) @rr . .......... ..... ...... rn:5 rd:5 &rr diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index b70ae5befd..bb92bdc296 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -8201,6 +8201,28 @@ static bool trans_PACGA(DisasContext *s, arg_rrr *a) return false; } +static bool gen_rrr(DisasContext *s, arg_rrr_sf *a, ArithTwoOp fn) +{ + TCGv_i64 tcg_rm = cpu_reg(s, a->rm); + TCGv_i64 tcg_rn = cpu_reg(s, a->rn); + TCGv_i64 tcg_rd = cpu_reg(s, a->rd); + + fn(tcg_rd, tcg_rn, tcg_rm); + if (!a->sf) { + tcg_gen_ext32u_i64(tcg_rd, tcg_rd); + } + return true; +} + +TRANS_FEAT(SMAX, aa64_cssc, gen_rrr, a, + a->sf ? tcg_gen_smax_i64 : gen_smax32_i64) +TRANS_FEAT(SMIN, aa64_cssc, gen_rrr, a, + a->sf ? tcg_gen_smin_i64 : gen_smin32_i64) +TRANS_FEAT(UMAX, aa64_cssc, gen_rrr, a, + a->sf ? tcg_gen_umax_i64 : gen_umax32_i64) +TRANS_FEAT(UMIN, aa64_cssc, gen_rrr, a, + a->sf ? tcg_gen_umin_i64 : gen_umin32_i64) + typedef void ArithOneOp(TCGv_i64, TCGv_i64); static bool gen_rr(DisasContext *s, int rd, int rn, ArithOneOp fn) From 8a4bb8b975f80022cdea27757cd8d14f13bf65a9 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 26 Aug 2025 11:21:29 +0100 Subject: [PATCH 0238/2396] target/arm: Split out gen_wrap2_i32 helper Wrapper to extract the low 32 bits, perform an operation, and zero-extend back to 64 bits. Signed-off-by: Richard Henderson Reviewed-by: Peter Maydell Message-id: 20250803014019.416797-5-richard.henderson@linaro.org [PMM: fixed wrong output-reg argument in callsites; add comment] Signed-off-by: Peter Maydell --- target/arm/tcg/translate-a64.c | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index bb92bdc296..c0fa9a44e7 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -8231,13 +8231,22 @@ static bool gen_rr(DisasContext *s, int rd, int rn, ArithOneOp fn) return true; } +/* + * Perform 32-bit operation fn on the low half of n; + * the high half of the output is zeroed. + */ +static void gen_wrap2_i32(TCGv_i64 d, TCGv_i64 n, NeonGenOneOpFn fn) +{ + TCGv_i32 t = tcg_temp_new_i32(); + + tcg_gen_extrl_i64_i32(t, n); + fn(t, t); + tcg_gen_extu_i32_i64(d, t); +} + static void gen_rbit32(TCGv_i64 tcg_rd, TCGv_i64 tcg_rn) { - TCGv_i32 t32 = tcg_temp_new_i32(); - - tcg_gen_extrl_i64_i32(t32, tcg_rn); - gen_helper_rbit(t32, t32); - tcg_gen_extu_i32_i64(tcg_rd, t32); + gen_wrap2_i32(tcg_rd, tcg_rn, gen_helper_rbit); } static void gen_rev16_xx(TCGv_i64 tcg_rd, TCGv_i64 tcg_rn, TCGv_i64 mask) @@ -8293,11 +8302,7 @@ static void gen_clz64(TCGv_i64 tcg_rd, TCGv_i64 tcg_rn) static void gen_cls32(TCGv_i64 tcg_rd, TCGv_i64 tcg_rn) { - TCGv_i32 t32 = tcg_temp_new_i32(); - - tcg_gen_extrl_i64_i32(t32, tcg_rn); - tcg_gen_clrsb_i32(t32, t32); - tcg_gen_extu_i32_i64(tcg_rd, t32); + gen_wrap2_i32(tcg_rd, tcg_rn, tcg_gen_clrsb_i32); } TRANS(CLZ, gen_rr, a->rd, a->rn, a->sf ? gen_clz64 : gen_clz32) From 994a260feac452ca478af5bd4ba4bff45b889b6e Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 26 Aug 2025 17:11:12 +0100 Subject: [PATCH 0239/2396] target/arm: Implement CTZ, CNT, ABS Signed-off-by: Richard Henderson Reviewed-by: Peter Maydell Message-id: 20250803014019.416797-6-richard.henderson@linaro.org [PMM: fix tcg_rd/tcg_rn mixup] Signed-off-by: Peter Maydell --- target/arm/tcg/a64.decode | 4 ++++ target/arm/tcg/translate-a64.c | 31 +++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index a886b3ba4c..766c610c01 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -726,6 +726,10 @@ REV64 1 10 11010110 00000 000011 ..... ..... @rr CLZ . 10 11010110 00000 000100 ..... ..... @rr_sf CLS . 10 11010110 00000 000101 ..... ..... @rr_sf +CTZ . 10 11010110 00000 000110 ..... ..... @rr_sf +CNT . 10 11010110 00000 000111 ..... ..... @rr_sf +ABS . 10 11010110 00000 001000 ..... ..... @rr_sf + &pacaut rd rn z @pacaut . .. ........ ..... .. z:1 ... rn:5 rd:5 &pacaut diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index c0fa9a44e7..259aa70a36 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -8308,6 +8308,37 @@ static void gen_cls32(TCGv_i64 tcg_rd, TCGv_i64 tcg_rn) TRANS(CLZ, gen_rr, a->rd, a->rn, a->sf ? gen_clz64 : gen_clz32) TRANS(CLS, gen_rr, a->rd, a->rn, a->sf ? tcg_gen_clrsb_i64 : gen_cls32) +static void gen_ctz32(TCGv_i64 tcg_rd, TCGv_i64 tcg_rn) +{ + TCGv_i32 t32 = tcg_temp_new_i32(); + + tcg_gen_extrl_i64_i32(t32, tcg_rn); + tcg_gen_ctzi_i32(t32, t32, 32); + tcg_gen_extu_i32_i64(tcg_rd, t32); +} + +static void gen_ctz64(TCGv_i64 tcg_rd, TCGv_i64 tcg_rn) +{ + tcg_gen_ctzi_i64(tcg_rd, tcg_rn, 64); +} + +static void gen_cnt32(TCGv_i64 tcg_rd, TCGv_i64 tcg_rn) +{ + gen_wrap2_i32(tcg_rd, tcg_rn, tcg_gen_ctpop_i32); +} + +static void gen_abs32(TCGv_i64 tcg_rd, TCGv_i64 tcg_rn) +{ + gen_wrap2_i32(tcg_rd, tcg_rn, tcg_gen_abs_i32); +} + +TRANS_FEAT(CTZ, aa64_cssc, gen_rr, a->rd, a->rn, + a->sf ? gen_ctz64 : gen_ctz32) +TRANS_FEAT(CNT, aa64_cssc, gen_rr, a->rd, a->rn, + a->sf ? tcg_gen_ctpop_i64 : gen_cnt32) +TRANS_FEAT(ABS, aa64_cssc, gen_rr, a->rd, a->rn, + a->sf ? tcg_gen_abs_i64 : gen_abs32) + static bool gen_pacaut(DisasContext *s, arg_pacaut *a, NeonGenTwo64OpEnvFn fn) { TCGv_i64 tcg_rd, tcg_rn; From 7494f8bbfbb030c5c40a42e4d71430115e4f7a63 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 26 Aug 2025 11:21:30 +0100 Subject: [PATCH 0240/2396] target/arm: Enable FEAT_CSSC for -cpu max Signed-off-by: Richard Henderson Reviewed-by: Peter Maydell Message-id: 20250803014019.416797-7-richard.henderson@linaro.org [PMM: rebased to handle linux-user elfload.c refactor] Signed-off-by: Peter Maydell --- docs/system/arm/emulation.rst | 1 + linux-user/aarch64/elfload.c | 1 + target/arm/tcg/cpu64.c | 1 + 3 files changed, 3 insertions(+) diff --git a/docs/system/arm/emulation.rst b/docs/system/arm/emulation.rst index 1c597d8673..b12f013b4f 100644 --- a/docs/system/arm/emulation.rst +++ b/docs/system/arm/emulation.rst @@ -30,6 +30,7 @@ the following architecture extensions: - FEAT_CMOW (Control for cache maintenance permission) - FEAT_CRC32 (CRC32 instructions) - FEAT_Crypto (Cryptographic Extension) +- FEAT_CSSC (Common Short Sequence Compression instructions) - FEAT_CSV2 (Cache speculation variant 2) - FEAT_CSV2_1p1 (Cache speculation variant 2, version 1.1) - FEAT_CSV2_1p2 (Cache speculation variant 2, version 1.2) diff --git a/linux-user/aarch64/elfload.c b/linux-user/aarch64/elfload.c index 8076968251..dd5f34398a 100644 --- a/linux-user/aarch64/elfload.c +++ b/linux-user/aarch64/elfload.c @@ -215,6 +215,7 @@ abi_ulong get_elf_hwcap2(CPUState *cs) GET_FEATURE_ID(aa64_sme_b16b16, ARM_HWCAP2_A64_SME_B16B16); GET_FEATURE_ID(aa64_sme_f16f16, ARM_HWCAP2_A64_SME_F16F16); GET_FEATURE_ID(aa64_sve_b16b16, ARM_HWCAP2_A64_SVE_B16B16); + GET_FEATURE_ID(aa64_cssc, ARM_HWCAP2_A64_CSSC); return hwcaps; } diff --git a/target/arm/tcg/cpu64.c b/target/arm/tcg/cpu64.c index 4eb51420ef..eaf8846a6a 100644 --- a/target/arm/tcg/cpu64.c +++ b/target/arm/tcg/cpu64.c @@ -1178,6 +1178,7 @@ void aarch64_max_tcg_initfn(Object *obj) t = FIELD_DP64(t, ID_AA64ISAR2, MOPS, 1); /* FEAT_MOPS */ t = FIELD_DP64(t, ID_AA64ISAR2, BC, 1); /* FEAT_HBC */ t = FIELD_DP64(t, ID_AA64ISAR2, WFXT, 2); /* FEAT_WFxT */ + t = FIELD_DP64(t, ID_AA64ISAR2, CSSC, 1); /* FEAT_CSSC */ SET_IDREG(isar, ID_AA64ISAR2, t); t = GET_IDREG(isar, ID_AA64PFR0); From 36bc78aca83cfd3c8f73cbcb428bc7f0ce7a4a61 Mon Sep 17 00:00:00 2001 From: Manos Pitsidianakis Date: Tue, 26 Aug 2025 11:21:30 +0100 Subject: [PATCH 0241/2396] hw/arm: add static NVDIMMs in device tree NVDIMM is used for fast rootfs with EROFS, for example by kata containers. To allow booting with static NVDIMM memory, add them to the device tree in arm virt machine. This allows users to boot directly with nvdimm memory devices without having to rely on ACPI and hotplug. Verified to work with command invocation: ./qemu-system-aarch64 \ -M virt,nvdimm=on \ -cpu cortex-a57 \ -m 4G,slots=2,maxmem=8G \ -object memory-backend-file,id=mem1,share=on,mem-path=/tmp/nvdimm,size=4G,readonly=off \ -device nvdimm,id=nvdimm1,memdev=mem1,unarmed=off \ -drive file=./debian-12-nocloud-arm64-commited.qcow2,format=qcow2 \ -kernel ./vmlinuz-6.1.0-13-arm64 \ -append "root=/dev/vda1 console=ttyAMA0,115200 acpi=off" -initrd ./initrd.img-6.1.0-13-arm64 \ -nographic \ -serial mon:stdio Signed-off-by: Manos Pitsidianakis Message-id: 20250807-nvdimm_arm64_virt-v2-1-b8054578bea8@linaro.org Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/arm/boot.c | 42 ++++++++++++++++++++++++++++++++++++++++++ hw/arm/virt.c | 8 +++++--- 2 files changed, 47 insertions(+), 3 deletions(-) diff --git a/hw/arm/boot.c b/hw/arm/boot.c index d391cd01bb..1e57c4ab9e 100644 --- a/hw/arm/boot.c +++ b/hw/arm/boot.c @@ -25,6 +25,7 @@ #include "hw/boards.h" #include "system/reset.h" #include "hw/loader.h" +#include "hw/mem/memory-device.h" #include "elf.h" #include "system/device_tree.h" #include "qemu/config-file.h" @@ -515,6 +516,29 @@ static void fdt_add_psci_node(void *fdt, ARMCPU *armcpu) qemu_fdt_setprop_cell(fdt, "/psci", "migrate", migrate_fn); } +static int fdt_add_pmem_node(void *fdt, uint32_t acells, uint32_t scells, + int64_t mem_base, int64_t size, int64_t node) +{ + int ret; + + g_autofree char *nodename = g_strdup_printf("/pmem@%" PRIx64, mem_base); + + qemu_fdt_add_subnode(fdt, nodename); + qemu_fdt_setprop_string(fdt, nodename, "compatible", "pmem-region"); + ret = qemu_fdt_setprop_sized_cells(fdt, nodename, "reg", acells, + mem_base, scells, size); + if (ret) { + return ret; + } + + if (node >= 0) { + return qemu_fdt_setprop_cell(fdt, nodename, "numa-node-id", + node); + } + + return 0; +} + int arm_load_dtb(hwaddr addr, const struct arm_boot_info *binfo, hwaddr addr_limit, AddressSpace *as, MachineState *ms, ARMCPU *cpu) @@ -525,6 +549,7 @@ int arm_load_dtb(hwaddr addr, const struct arm_boot_info *binfo, unsigned int i; hwaddr mem_base, mem_len; char **node_path; + g_autofree MemoryDeviceInfoList *md_list = NULL; Error *err = NULL; if (binfo->dtb_filename) { @@ -628,6 +653,23 @@ int arm_load_dtb(hwaddr addr, const struct arm_boot_info *binfo, } } + md_list = qmp_memory_device_list(); + for (MemoryDeviceInfoList *m = md_list; m != NULL; m = m->next) { + MemoryDeviceInfo *mi = m->value; + + if (mi->type == MEMORY_DEVICE_INFO_KIND_NVDIMM) { + PCDIMMDeviceInfo *di = mi->u.nvdimm.data; + + rc = fdt_add_pmem_node(fdt, acells, scells, + di->addr, di->size, di->node); + if (rc < 0) { + fprintf(stderr, "couldn't add NVDIMM /pmem@%"PRIx64" node\n", + di->addr); + goto fail; + } + } + } + rc = fdt_path_offset(fdt, "/chosen"); if (rc < 0) { qemu_fdt_add_subnode(fdt, "/chosen"); diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 9326cfc895..1e63f40fbe 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -2917,7 +2917,7 @@ static void virt_memory_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev, const MachineState *ms = MACHINE(hotplug_dev); const bool is_nvdimm = object_dynamic_cast(OBJECT(dev), TYPE_NVDIMM); - if (!vms->acpi_dev) { + if (!vms->acpi_dev && !(is_nvdimm && !dev->hotplugged)) { error_setg(errp, "memory hotplug is not enabled: missing acpi-ged device"); return; @@ -2949,8 +2949,10 @@ static void virt_memory_plug(HotplugHandler *hotplug_dev, nvdimm_plug(ms->nvdimms_state); } - hotplug_handler_plug(HOTPLUG_HANDLER(vms->acpi_dev), - dev, &error_abort); + if (vms->acpi_dev) { + hotplug_handler_plug(HOTPLUG_HANDLER(vms->acpi_dev), + dev, &error_abort); + } } static void virt_machine_device_pre_plug_cb(HotplugHandler *hotplug_dev, From 5ffd387e9e0f787744fadaad35e1bf92224b0642 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 19 Aug 2025 12:56:48 +0100 Subject: [PATCH 0242/2396] scripts/kernel-doc: Avoid new Perl precedence warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Newer versions of Perl (5.41.x and up) emit a warning for code in kernel-doc: Possible precedence problem between ! and pattern match (m//) at /scripts/kernel-doc line 1597. This is because the code does: if (!$param =~ /\w\.\.\.$/) { In Perl, the ! operator has higher precedence than the =~ pattern-match binding, so the effect of this condition is to first logically-negate the string $param into a true-or-false value and then try to pattern match it against the regex, which in this case will always fail. This is almost certainly not what the author intended. In the new Python version of kernel-doc in the Linux kernel, the equivalent code is written: if KernRe(r'\w\.\.\.$').search(param): # For named variable parameters of the form `x...`, # remove the dots param = param[:-3] else: # Handles unnamed variable parameters param = "..." which is a more sensible way of writing the behaviour you would get if you put in brackets to make the regex match first and then negate the result. Take this as the intended behaviour, and update the Perl to match. For QEMU, this produces no change in output, presumably because we never used the "unnamed variable parameters" syntax. Cc: qemu-stable@nongnu.org Signed-off-by: Peter Maydell Reviewed-by: Daniel P. Berrangé Reviewed-by: Mauro Carvalho Chehab Message-id: 20250819115648.2125709-1-peter.maydell@linaro.org --- scripts/kernel-doc | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/scripts/kernel-doc b/scripts/kernel-doc index fec83f53ed..117ec8fcd1 100755 --- a/scripts/kernel-doc +++ b/scripts/kernel-doc @@ -1594,13 +1594,12 @@ sub push_parameter($$$$$) { if ($type eq "" && $param =~ /\.\.\.$/) { - if (!$param =~ /\w\.\.\.$/) { - # handles unnamed variable parameters - $param = "..."; - } - elsif ($param =~ /\w\.\.\.$/) { + if ($param =~ /\w\.\.\.$/) { # for named variable parameters of the form `x...`, remove the dots $param =~ s/\.\.\.$//; + } else { + # handles unnamed variable parameters + $param = "..."; } if (!defined $parameterdescs{$param} || $parameterdescs{$param} eq "") { $parameterdescs{$param} = "variable arguments"; From 3f34478007a2423399dbdfae5e3deeca3898da9c Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 14 Aug 2025 18:13:16 +0100 Subject: [PATCH 0243/2396] docs/sphinx/kerneldoc.py: Handle new LINENO syntax The new upstream kernel-doc that we plan to update to uses a different syntax for the LINENO directives that the Sphinx extension parses: instead of #define LINENO 86 it has .. LINENO 86 Update the kerneldoc.py extension to handle both syntaxes, so that it will work with both the old and the new kernel-doc. Signed-off-by: Peter Maydell Reviewed-by: Paolo Bonzini Reviewed-by: Mauro Carvalho Chehab Message-id: 20250814171324.1614516-2-peter.maydell@linaro.org --- docs/sphinx/kerneldoc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/sphinx/kerneldoc.py b/docs/sphinx/kerneldoc.py index 3aa972f2e8..30bb343198 100644 --- a/docs/sphinx/kerneldoc.py +++ b/docs/sphinx/kerneldoc.py @@ -127,7 +127,7 @@ class KernelDocDirective(Directive): result = ViewList() lineoffset = 0; - line_regex = re.compile("^#define LINENO ([0-9]+)$") + line_regex = re.compile(r"^(?:\.\.|#define) LINENO ([0-9]+)$") for line in lines: match = line_regex.search(line) if match: From 4a1bc66d3f61cb803b16bb1691e46b5ec59d25f5 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 14 Aug 2025 18:13:17 +0100 Subject: [PATCH 0244/2396] tests/qtest/libqtest.h: Remove stray space from doc comment The doc comment for qtest_cb_for_every_machine has a stray space at the start of its description, which makes kernel-doc think that this line is part of the documentation of the skip_old_versioned argument. The result is that the HTML doesn't have a "Description" section and the text is instead put in the wrong place. Remove the stray space. Signed-off-by: Peter Maydell Reviewed-by: Paolo Bonzini Reviewed-by: Mauro Carvalho Chehab Message-id: 20250814171324.1614516-3-peter.maydell@linaro.org --- tests/qtest/libqtest.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/qtest/libqtest.h b/tests/qtest/libqtest.h index b3f2e7fbef..fd27521a9c 100644 --- a/tests/qtest/libqtest.h +++ b/tests/qtest/libqtest.h @@ -977,7 +977,7 @@ void qtest_qmp_fds_assert_success(QTestState *qts, int *fds, size_t nfds, * @cb: Pointer to the callback function * @skip_old_versioned: true if versioned old machine types should be skipped * - * Call a callback function for every name of all available machines. + * Call a callback function for every name of all available machines. */ void qtest_cb_for_every_machine(void (*cb)(const char *machine), bool skip_old_versioned); From 2b2765ac4045642563cc92ad98c2244a0aa0c7fc Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 14 Aug 2025 18:13:18 +0100 Subject: [PATCH 0245/2396] scripts: Import Python kerneldoc from Linux kernel We last synced our copy of kerneldoc with Linux back in 2020. In the interim, upstream has entirely rewritten the script in Python, and the new Python version is split into a main script plus some libraries in the kernel's scripts/lib/kdoc. Import all these files. These are the versions as of kernel commit 0cc53520e68be, with no local changes. We use the same lib/kdoc/ directory as the kernel does here, so we can avoid having to edit the top-level script just to adjust a pathname, even though it is probably not the naming we would have picked if this was a purely QEMU script. The Sphinx conf.py still points at the Perl version of the script, so this Python code will not be invoked to build the docs yet. NB: checkpatch complains about many things in this commit, including the use of "GPL-2.0" rather than "GPL-2.0-only" in the SPDX tags, but since this is a third party import we can ignore this. Signed-off-by: Peter Maydell Reviewed-by: Paolo Bonzini Reviewed-by: Mauro Carvalho Chehab Message-id: 20250814171324.1614516-4-peter.maydell@linaro.org --- scripts/kernel-doc.py | 325 ++++++ scripts/lib/kdoc/kdoc_files.py | 291 ++++++ scripts/lib/kdoc/kdoc_item.py | 42 + scripts/lib/kdoc/kdoc_output.py | 749 ++++++++++++++ scripts/lib/kdoc/kdoc_parser.py | 1669 +++++++++++++++++++++++++++++++ scripts/lib/kdoc/kdoc_re.py | 270 +++++ 6 files changed, 3346 insertions(+) create mode 100755 scripts/kernel-doc.py create mode 100644 scripts/lib/kdoc/kdoc_files.py create mode 100644 scripts/lib/kdoc/kdoc_item.py create mode 100644 scripts/lib/kdoc/kdoc_output.py create mode 100644 scripts/lib/kdoc/kdoc_parser.py create mode 100644 scripts/lib/kdoc/kdoc_re.py diff --git a/scripts/kernel-doc.py b/scripts/kernel-doc.py new file mode 100755 index 0000000000..fc3d46ef51 --- /dev/null +++ b/scripts/kernel-doc.py @@ -0,0 +1,325 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0 +# Copyright(c) 2025: Mauro Carvalho Chehab . +# +# pylint: disable=C0103,R0915 +# +# Converted from the kernel-doc script originally written in Perl +# under GPLv2, copyrighted since 1998 by the following authors: +# +# Aditya Srivastava +# Akira Yokosawa +# Alexander A. Klimov +# Alexander Lobakin +# André Almeida +# Andy Shevchenko +# Anna-Maria Behnsen +# Armin Kuster +# Bart Van Assche +# Ben Hutchings +# Borislav Petkov +# Chen-Yu Tsai +# Coco Li +# Conchúr Navid +# Daniel Santos +# Danilo Cesar Lemes de Paula +# Dan Luedtke +# Donald Hunter +# Gabriel Krisman Bertazi +# Greg Kroah-Hartman +# Harvey Harrison +# Horia Geanta +# Ilya Dryomov +# Jakub Kicinski +# Jani Nikula +# Jason Baron +# Jason Gunthorpe +# Jérémy Bobbio +# Johannes Berg +# Johannes Weiner +# Jonathan Cameron +# Jonathan Corbet +# Jonathan Neuschäfer +# Kamil Rytarowski +# Kees Cook +# Laurent Pinchart +# Levin, Alexander (Sasha Levin) +# Linus Torvalds +# Lucas De Marchi +# Mark Rutland +# Markus Heiser +# Martin Waitz +# Masahiro Yamada +# Matthew Wilcox +# Mauro Carvalho Chehab +# Michal Wajdeczko +# Michael Zucchi +# Mike Rapoport +# Niklas Söderlund +# Nishanth Menon +# Paolo Bonzini +# Pavan Kumar Linga +# Pavel Pisa +# Peter Maydell +# Pierre-Louis Bossart +# Randy Dunlap +# Richard Kennedy +# Rich Walker +# Rolf Eike Beer +# Sakari Ailus +# Silvio Fricke +# Simon Huggins +# Tim Waugh +# Tomasz Warniełło +# Utkarsh Tripathi +# valdis.kletnieks@vt.edu +# Vegard Nossum +# Will Deacon +# Yacine Belkadi +# Yujie Liu + +""" +kernel_doc +========== + +Print formatted kernel documentation to stdout + +Read C language source or header FILEs, extract embedded +documentation comments, and print formatted documentation +to standard output. + +The documentation comments are identified by the "/**" +opening comment mark. + +See Documentation/doc-guide/kernel-doc.rst for the +documentation comment syntax. +""" + +import argparse +import logging +import os +import sys + +# Import Python modules + +LIB_DIR = "lib/kdoc" +SRC_DIR = os.path.dirname(os.path.realpath(__file__)) + +sys.path.insert(0, os.path.join(SRC_DIR, LIB_DIR)) + +from kdoc_files import KernelFiles # pylint: disable=C0413 +from kdoc_output import RestFormat, ManFormat # pylint: disable=C0413 + +DESC = """ +Read C language source or header FILEs, extract embedded documentation comments, +and print formatted documentation to standard output. + +The documentation comments are identified by the "/**" opening comment mark. + +See Documentation/doc-guide/kernel-doc.rst for the documentation comment syntax. +""" + +EXPORT_FILE_DESC = """ +Specify an additional FILE in which to look for EXPORT_SYMBOL information. + +May be used multiple times. +""" + +EXPORT_DESC = """ +Only output documentation for the symbols that have been +exported using EXPORT_SYMBOL() and related macros in any input +FILE or -export-file FILE. +""" + +INTERNAL_DESC = """ +Only output documentation for the symbols that have NOT been +exported using EXPORT_SYMBOL() and related macros in any input +FILE or -export-file FILE. +""" + +FUNCTION_DESC = """ +Only output documentation for the given function or DOC: section +title. All other functions and DOC: sections are ignored. + +May be used multiple times. +""" + +NOSYMBOL_DESC = """ +Exclude the specified symbol from the output documentation. + +May be used multiple times. +""" + +FILES_DESC = """ +Header and C source files to be parsed. +""" + +WARN_CONTENTS_BEFORE_SECTIONS_DESC = """ +Warns if there are contents before sections (deprecated). + +This option is kept just for backward-compatibility, but it does nothing, +neither here nor at the original Perl script. +""" + + +class MsgFormatter(logging.Formatter): + """Helper class to format warnings on a similar way to kernel-doc.pl""" + + def format(self, record): + record.levelname = record.levelname.capitalize() + return logging.Formatter.format(self, record) + +def main(): + """Main program""" + + parser = argparse.ArgumentParser(formatter_class=argparse.RawTextHelpFormatter, + description=DESC) + + # Normal arguments + + parser.add_argument("-v", "-verbose", "--verbose", action="store_true", + help="Verbose output, more warnings and other information.") + + parser.add_argument("-d", "-debug", "--debug", action="store_true", + help="Enable debug messages") + + parser.add_argument("-M", "-modulename", "--modulename", + default="Kernel API", + help="Allow setting a module name at the output.") + + parser.add_argument("-l", "-enable-lineno", "--enable_lineno", + action="store_true", + help="Enable line number output (only in ReST mode)") + + # Arguments to control the warning behavior + + parser.add_argument("-Wreturn", "--wreturn", action="store_true", + help="Warns about the lack of a return markup on functions.") + + parser.add_argument("-Wshort-desc", "-Wshort-description", "--wshort-desc", + action="store_true", + help="Warns if initial short description is missing") + + parser.add_argument("-Wcontents-before-sections", + "--wcontents-before-sections", action="store_true", + help=WARN_CONTENTS_BEFORE_SECTIONS_DESC) + + parser.add_argument("-Wall", "--wall", action="store_true", + help="Enable all types of warnings") + + parser.add_argument("-Werror", "--werror", action="store_true", + help="Treat warnings as errors.") + + parser.add_argument("-export-file", "--export-file", action='append', + help=EXPORT_FILE_DESC) + + # Output format mutually-exclusive group + + out_group = parser.add_argument_group("Output format selection (mutually exclusive)") + + out_fmt = out_group.add_mutually_exclusive_group() + + out_fmt.add_argument("-m", "-man", "--man", action="store_true", + help="Output troff manual page format.") + out_fmt.add_argument("-r", "-rst", "--rst", action="store_true", + help="Output reStructuredText format (default).") + out_fmt.add_argument("-N", "-none", "--none", action="store_true", + help="Do not output documentation, only warnings.") + + # Output selection mutually-exclusive group + + sel_group = parser.add_argument_group("Output selection (mutually exclusive)") + sel_mut = sel_group.add_mutually_exclusive_group() + + sel_mut.add_argument("-e", "-export", "--export", action='store_true', + help=EXPORT_DESC) + + sel_mut.add_argument("-i", "-internal", "--internal", action='store_true', + help=INTERNAL_DESC) + + sel_mut.add_argument("-s", "-function", "--symbol", action='append', + help=FUNCTION_DESC) + + # Those are valid for all 3 types of filter + parser.add_argument("-n", "-nosymbol", "--nosymbol", action='append', + help=NOSYMBOL_DESC) + + parser.add_argument("-D", "-no-doc-sections", "--no-doc-sections", + action='store_true', help="Don't outputt DOC sections") + + parser.add_argument("files", metavar="FILE", + nargs="+", help=FILES_DESC) + + args = parser.parse_args() + + if args.wall: + args.wreturn = True + args.wshort_desc = True + args.wcontents_before_sections = True + + logger = logging.getLogger() + + if not args.debug: + logger.setLevel(logging.INFO) + else: + logger.setLevel(logging.DEBUG) + + formatter = MsgFormatter('%(levelname)s: %(message)s') + + handler = logging.StreamHandler() + handler.setFormatter(formatter) + + logger.addHandler(handler) + + python_ver = sys.version_info[:2] + if python_ver < (3,6): + logger.warning("Python 3.6 or later is required by kernel-doc") + + # Return 0 here to avoid breaking compilation + sys.exit(0) + + if python_ver < (3,7): + logger.warning("Python 3.7 or later is required for correct results") + + if args.man: + out_style = ManFormat(modulename=args.modulename) + elif args.none: + out_style = None + else: + out_style = RestFormat() + + kfiles = KernelFiles(verbose=args.verbose, + out_style=out_style, werror=args.werror, + wreturn=args.wreturn, wshort_desc=args.wshort_desc, + wcontents_before_sections=args.wcontents_before_sections) + + kfiles.parse(args.files, export_file=args.export_file) + + for t in kfiles.msg(enable_lineno=args.enable_lineno, export=args.export, + internal=args.internal, symbol=args.symbol, + nosymbol=args.nosymbol, export_file=args.export_file, + no_doc_sections=args.no_doc_sections): + msg = t[1] + if msg: + print(msg) + + error_count = kfiles.errors + if not error_count: + sys.exit(0) + + if args.werror: + print(f"{error_count} warnings as errors") + sys.exit(error_count) + + if args.verbose: + print(f"{error_count} errors") + + if args.none: + sys.exit(0) + + sys.exit(error_count) + + +# Call main method +if __name__ == "__main__": + main() diff --git a/scripts/lib/kdoc/kdoc_files.py b/scripts/lib/kdoc/kdoc_files.py new file mode 100644 index 0000000000..9e09b45b02 --- /dev/null +++ b/scripts/lib/kdoc/kdoc_files.py @@ -0,0 +1,291 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0 +# Copyright(c) 2025: Mauro Carvalho Chehab . +# +# pylint: disable=R0903,R0913,R0914,R0917 + +""" +Parse lernel-doc tags on multiple kernel source files. +""" + +import argparse +import logging +import os +import re + +from kdoc_parser import KernelDoc +from kdoc_output import OutputFormat + + +class GlobSourceFiles: + """ + Parse C source code file names and directories via an Interactor. + """ + + def __init__(self, srctree=None, valid_extensions=None): + """ + Initialize valid extensions with a tuple. + + If not defined, assume default C extensions (.c and .h) + + It would be possible to use python's glob function, but it is + very slow, and it is not interactive. So, it would wait to read all + directories before actually do something. + + So, let's use our own implementation. + """ + + if not valid_extensions: + self.extensions = (".c", ".h") + else: + self.extensions = valid_extensions + + self.srctree = srctree + + def _parse_dir(self, dirname): + """Internal function to parse files recursively""" + + with os.scandir(dirname) as obj: + for entry in obj: + name = os.path.join(dirname, entry.name) + + if entry.is_dir(): + yield from self._parse_dir(name) + + if not entry.is_file(): + continue + + basename = os.path.basename(name) + + if not basename.endswith(self.extensions): + continue + + yield name + + def parse_files(self, file_list, file_not_found_cb): + """ + Define an interator to parse all source files from file_list, + handling directories if any + """ + + if not file_list: + return + + for fname in file_list: + if self.srctree: + f = os.path.join(self.srctree, fname) + else: + f = fname + + if os.path.isdir(f): + yield from self._parse_dir(f) + elif os.path.isfile(f): + yield f + elif file_not_found_cb: + file_not_found_cb(fname) + + +class KernelFiles(): + """ + Parse kernel-doc tags on multiple kernel source files. + + There are two type of parsers defined here: + - self.parse_file(): parses both kernel-doc markups and + EXPORT_SYMBOL* macros; + - self.process_export_file(): parses only EXPORT_SYMBOL* macros. + """ + + def warning(self, msg): + """Ancillary routine to output a warning and increment error count""" + + self.config.log.warning(msg) + self.errors += 1 + + def error(self, msg): + """Ancillary routine to output an error and increment error count""" + + self.config.log.error(msg) + self.errors += 1 + + def parse_file(self, fname): + """ + Parse a single Kernel source. + """ + + # Prevent parsing the same file twice if results are cached + if fname in self.files: + return + + doc = KernelDoc(self.config, fname) + export_table, entries = doc.parse_kdoc() + + self.export_table[fname] = export_table + + self.files.add(fname) + self.export_files.add(fname) # parse_kdoc() already check exports + + self.results[fname] = entries + + def process_export_file(self, fname): + """ + Parses EXPORT_SYMBOL* macros from a single Kernel source file. + """ + + # Prevent parsing the same file twice if results are cached + if fname in self.export_files: + return + + doc = KernelDoc(self.config, fname) + export_table = doc.parse_export() + + if not export_table: + self.error(f"Error: Cannot check EXPORT_SYMBOL* on {fname}") + export_table = set() + + self.export_table[fname] = export_table + self.export_files.add(fname) + + def file_not_found_cb(self, fname): + """ + Callback to warn if a file was not found. + """ + + self.error(f"Cannot find file {fname}") + + def __init__(self, verbose=False, out_style=None, + werror=False, wreturn=False, wshort_desc=False, + wcontents_before_sections=False, + logger=None): + """ + Initialize startup variables and parse all files + """ + + if not verbose: + verbose = bool(os.environ.get("KBUILD_VERBOSE", 0)) + + if out_style is None: + out_style = OutputFormat() + + if not werror: + kcflags = os.environ.get("KCFLAGS", None) + if kcflags: + match = re.search(r"(\s|^)-Werror(\s|$)/", kcflags) + if match: + werror = True + + # reading this variable is for backwards compat just in case + # someone was calling it with the variable from outside the + # kernel's build system + kdoc_werror = os.environ.get("KDOC_WERROR", None) + if kdoc_werror: + werror = kdoc_werror + + # Some variables are global to the parser logic as a whole as they are + # used to send control configuration to KernelDoc class. As such, + # those variables are read-only inside the KernelDoc. + self.config = argparse.Namespace + + self.config.verbose = verbose + self.config.werror = werror + self.config.wreturn = wreturn + self.config.wshort_desc = wshort_desc + self.config.wcontents_before_sections = wcontents_before_sections + + if not logger: + self.config.log = logging.getLogger("kernel-doc") + else: + self.config.log = logger + + self.config.warning = self.warning + + self.config.src_tree = os.environ.get("SRCTREE", None) + + # Initialize variables that are internal to KernelFiles + + self.out_style = out_style + + self.errors = 0 + self.results = {} + + self.files = set() + self.export_files = set() + self.export_table = {} + + def parse(self, file_list, export_file=None): + """ + Parse all files + """ + + glob = GlobSourceFiles(srctree=self.config.src_tree) + + for fname in glob.parse_files(file_list, self.file_not_found_cb): + self.parse_file(fname) + + for fname in glob.parse_files(export_file, self.file_not_found_cb): + self.process_export_file(fname) + + def out_msg(self, fname, name, arg): + """ + Return output messages from a file name using the output style + filtering. + + If output type was not handled by the syler, return None. + """ + + # NOTE: we can add rules here to filter out unwanted parts, + # although OutputFormat.msg already does that. + + return self.out_style.msg(fname, name, arg) + + def msg(self, enable_lineno=False, export=False, internal=False, + symbol=None, nosymbol=None, no_doc_sections=False, + filenames=None, export_file=None): + """ + Interacts over the kernel-doc results and output messages, + returning kernel-doc markups on each interaction + """ + + self.out_style.set_config(self.config) + + if not filenames: + filenames = sorted(self.results.keys()) + + glob = GlobSourceFiles(srctree=self.config.src_tree) + + for fname in filenames: + function_table = set() + + if internal or export: + if not export_file: + export_file = [fname] + + for f in glob.parse_files(export_file, self.file_not_found_cb): + function_table |= self.export_table[f] + + if symbol: + for s in symbol: + function_table.add(s) + + self.out_style.set_filter(export, internal, symbol, nosymbol, + function_table, enable_lineno, + no_doc_sections) + + msg = "" + if fname not in self.results: + self.config.log.warning("No kernel-doc for file %s", fname) + continue + + for arg in self.results[fname]: + m = self.out_msg(fname, arg.name, arg) + + if m is None: + ln = arg.get("ln", 0) + dtype = arg.get('type', "") + + self.config.log.warning("%s:%d Can't handle %s", + fname, ln, dtype) + else: + msg += m + + if msg: + yield fname, msg diff --git a/scripts/lib/kdoc/kdoc_item.py b/scripts/lib/kdoc/kdoc_item.py new file mode 100644 index 0000000000..b3b2257645 --- /dev/null +++ b/scripts/lib/kdoc/kdoc_item.py @@ -0,0 +1,42 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# A class that will, eventually, encapsulate all of the parsed data that we +# then pass into the output modules. +# + +class KdocItem: + def __init__(self, name, type, start_line, **other_stuff): + self.name = name + self.type = type + self.declaration_start_line = start_line + self.sections = {} + self.sections_start_lines = {} + self.parameterlist = [] + self.parameterdesc_start_lines = [] + self.parameterdescs = {} + self.parametertypes = {} + # + # Just save everything else into our own dict so that the output + # side can grab it directly as before. As we move things into more + # structured data, this will, hopefully, fade away. + # + self.other_stuff = other_stuff + + def get(self, key, default = None): + return self.other_stuff.get(key, default) + + def __getitem__(self, key): + return self.get(key) + + # + # Tracking of section and parameter information. + # + def set_sections(self, sections, start_lines): + self.sections = sections + self.section_start_lines = start_lines + + def set_params(self, names, descs, types, starts): + self.parameterlist = names + self.parameterdescs = descs + self.parametertypes = types + self.parameterdesc_start_lines = starts diff --git a/scripts/lib/kdoc/kdoc_output.py b/scripts/lib/kdoc/kdoc_output.py new file mode 100644 index 0000000000..ea8914537b --- /dev/null +++ b/scripts/lib/kdoc/kdoc_output.py @@ -0,0 +1,749 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0 +# Copyright(c) 2025: Mauro Carvalho Chehab . +# +# pylint: disable=C0301,R0902,R0911,R0912,R0913,R0914,R0915,R0917 + +""" +Implement output filters to print kernel-doc documentation. + +The implementation uses a virtual base class (OutputFormat) which +contains a dispatches to virtual methods, and some code to filter +out output messages. + +The actual implementation is done on one separate class per each type +of output. Currently, there are output classes for ReST and man/troff. +""" + +import os +import re +from datetime import datetime + +from kdoc_parser import KernelDoc, type_param +from kdoc_re import KernRe + + +function_pointer = KernRe(r"([^\(]*\(\*)\s*\)\s*\(([^\)]*)\)", cache=False) + +# match expressions used to find embedded type information +type_constant = KernRe(r"\b``([^\`]+)``\b", cache=False) +type_constant2 = KernRe(r"\%([-_*\w]+)", cache=False) +type_func = KernRe(r"(\w+)\(\)", cache=False) +type_param_ref = KernRe(r"([\!~\*]?)\@(\w*((\.\w+)|(->\w+))*(\.\.\.)?)", cache=False) + +# Special RST handling for func ptr params +type_fp_param = KernRe(r"\@(\w+)\(\)", cache=False) + +# Special RST handling for structs with func ptr params +type_fp_param2 = KernRe(r"\@(\w+->\S+)\(\)", cache=False) + +type_env = KernRe(r"(\$\w+)", cache=False) +type_enum = KernRe(r"\&(enum\s*([_\w]+))", cache=False) +type_struct = KernRe(r"\&(struct\s*([_\w]+))", cache=False) +type_typedef = KernRe(r"\&(typedef\s*([_\w]+))", cache=False) +type_union = KernRe(r"\&(union\s*([_\w]+))", cache=False) +type_member = KernRe(r"\&([_\w]+)(\.|->)([_\w]+)", cache=False) +type_fallback = KernRe(r"\&([_\w]+)", cache=False) +type_member_func = type_member + KernRe(r"\(\)", cache=False) + + +class OutputFormat: + """ + Base class for OutputFormat. If used as-is, it means that only + warnings will be displayed. + """ + + # output mode. + OUTPUT_ALL = 0 # output all symbols and doc sections + OUTPUT_INCLUDE = 1 # output only specified symbols + OUTPUT_EXPORTED = 2 # output exported symbols + OUTPUT_INTERNAL = 3 # output non-exported symbols + + # Virtual member to be overriden at the inherited classes + highlights = [] + + def __init__(self): + """Declare internal vars and set mode to OUTPUT_ALL""" + + self.out_mode = self.OUTPUT_ALL + self.enable_lineno = None + self.nosymbol = {} + self.symbol = None + self.function_table = None + self.config = None + self.no_doc_sections = False + + self.data = "" + + def set_config(self, config): + """ + Setup global config variables used by both parser and output. + """ + + self.config = config + + def set_filter(self, export, internal, symbol, nosymbol, function_table, + enable_lineno, no_doc_sections): + """ + Initialize filter variables according with the requested mode. + + Only one choice is valid between export, internal and symbol. + + The nosymbol filter can be used on all modes. + """ + + self.enable_lineno = enable_lineno + self.no_doc_sections = no_doc_sections + self.function_table = function_table + + if symbol: + self.out_mode = self.OUTPUT_INCLUDE + elif export: + self.out_mode = self.OUTPUT_EXPORTED + elif internal: + self.out_mode = self.OUTPUT_INTERNAL + else: + self.out_mode = self.OUTPUT_ALL + + if nosymbol: + self.nosymbol = set(nosymbol) + + + def highlight_block(self, block): + """ + Apply the RST highlights to a sub-block of text. + """ + + for r, sub in self.highlights: + block = r.sub(sub, block) + + return block + + def out_warnings(self, args): + """ + Output warnings for identifiers that will be displayed. + """ + + for log_msg in args.warnings: + self.config.warning(log_msg) + + def check_doc(self, name, args): + """Check if DOC should be output""" + + if self.no_doc_sections: + return False + + if name in self.nosymbol: + return False + + if self.out_mode == self.OUTPUT_ALL: + self.out_warnings(args) + return True + + if self.out_mode == self.OUTPUT_INCLUDE: + if name in self.function_table: + self.out_warnings(args) + return True + + return False + + def check_declaration(self, dtype, name, args): + """ + Checks if a declaration should be output or not based on the + filtering criteria. + """ + + if name in self.nosymbol: + return False + + if self.out_mode == self.OUTPUT_ALL: + self.out_warnings(args) + return True + + if self.out_mode in [self.OUTPUT_INCLUDE, self.OUTPUT_EXPORTED]: + if name in self.function_table: + return True + + if self.out_mode == self.OUTPUT_INTERNAL: + if dtype != "function": + self.out_warnings(args) + return True + + if name not in self.function_table: + self.out_warnings(args) + return True + + return False + + def msg(self, fname, name, args): + """ + Handles a single entry from kernel-doc parser + """ + + self.data = "" + + dtype = args.type + + if dtype == "doc": + self.out_doc(fname, name, args) + return self.data + + if not self.check_declaration(dtype, name, args): + return self.data + + if dtype == "function": + self.out_function(fname, name, args) + return self.data + + if dtype == "enum": + self.out_enum(fname, name, args) + return self.data + + if dtype == "typedef": + self.out_typedef(fname, name, args) + return self.data + + if dtype in ["struct", "union"]: + self.out_struct(fname, name, args) + return self.data + + # Warn if some type requires an output logic + self.config.log.warning("doesn't now how to output '%s' block", + dtype) + + return None + + # Virtual methods to be overridden by inherited classes + # At the base class, those do nothing. + def out_doc(self, fname, name, args): + """Outputs a DOC block""" + + def out_function(self, fname, name, args): + """Outputs a function""" + + def out_enum(self, fname, name, args): + """Outputs an enum""" + + def out_typedef(self, fname, name, args): + """Outputs a typedef""" + + def out_struct(self, fname, name, args): + """Outputs a struct""" + + +class RestFormat(OutputFormat): + """Consts and functions used by ReST output""" + + highlights = [ + (type_constant, r"``\1``"), + (type_constant2, r"``\1``"), + + # Note: need to escape () to avoid func matching later + (type_member_func, r":c:type:`\1\2\3\\(\\) <\1>`"), + (type_member, r":c:type:`\1\2\3 <\1>`"), + (type_fp_param, r"**\1\\(\\)**"), + (type_fp_param2, r"**\1\\(\\)**"), + (type_func, r"\1()"), + (type_enum, r":c:type:`\1 <\2>`"), + (type_struct, r":c:type:`\1 <\2>`"), + (type_typedef, r":c:type:`\1 <\2>`"), + (type_union, r":c:type:`\1 <\2>`"), + + # in rst this can refer to any type + (type_fallback, r":c:type:`\1`"), + (type_param_ref, r"**\1\2**") + ] + blankline = "\n" + + sphinx_literal = KernRe(r'^[^.].*::$', cache=False) + sphinx_cblock = KernRe(r'^\.\.\ +code-block::', cache=False) + + def __init__(self): + """ + Creates class variables. + + Not really mandatory, but it is a good coding style and makes + pylint happy. + """ + + super().__init__() + self.lineprefix = "" + + def print_lineno(self, ln): + """Outputs a line number""" + + if self.enable_lineno and ln is not None: + ln += 1 + self.data += f".. LINENO {ln}\n" + + def output_highlight(self, args): + """ + Outputs a C symbol that may require being converted to ReST using + the self.highlights variable + """ + + input_text = args + output = "" + in_literal = False + litprefix = "" + block = "" + + for line in input_text.strip("\n").split("\n"): + + # If we're in a literal block, see if we should drop out of it. + # Otherwise, pass the line straight through unmunged. + if in_literal: + if line.strip(): # If the line is not blank + # If this is the first non-blank line in a literal block, + # figure out the proper indent. + if not litprefix: + r = KernRe(r'^(\s*)') + if r.match(line): + litprefix = '^' + r.group(1) + else: + litprefix = "" + + output += line + "\n" + elif not KernRe(litprefix).match(line): + in_literal = False + else: + output += line + "\n" + else: + output += line + "\n" + + # Not in a literal block (or just dropped out) + if not in_literal: + block += line + "\n" + if self.sphinx_literal.match(line) or self.sphinx_cblock.match(line): + in_literal = True + litprefix = "" + output += self.highlight_block(block) + block = "" + + # Handle any remaining block + if block: + output += self.highlight_block(block) + + # Print the output with the line prefix + for line in output.strip("\n").split("\n"): + self.data += self.lineprefix + line + "\n" + + def out_section(self, args, out_docblock=False): + """ + Outputs a block section. + + This could use some work; it's used to output the DOC: sections, and + starts by putting out the name of the doc section itself, but that + tends to duplicate a header already in the template file. + """ + for section, text in args.sections.items(): + # Skip sections that are in the nosymbol_table + if section in self.nosymbol: + continue + + if out_docblock: + if not self.out_mode == self.OUTPUT_INCLUDE: + self.data += f".. _{section}:\n\n" + self.data += f'{self.lineprefix}**{section}**\n\n' + else: + self.data += f'{self.lineprefix}**{section}**\n\n' + + self.print_lineno(args.section_start_lines.get(section, 0)) + self.output_highlight(text) + self.data += "\n" + self.data += "\n" + + def out_doc(self, fname, name, args): + if not self.check_doc(name, args): + return + self.out_section(args, out_docblock=True) + + def out_function(self, fname, name, args): + + oldprefix = self.lineprefix + signature = "" + + func_macro = args.get('func_macro', False) + if func_macro: + signature = name + else: + if args.get('functiontype'): + signature = args['functiontype'] + " " + signature += name + " (" + + ln = args.declaration_start_line + count = 0 + for parameter in args.parameterlist: + if count != 0: + signature += ", " + count += 1 + dtype = args.parametertypes.get(parameter, "") + + if function_pointer.search(dtype): + signature += function_pointer.group(1) + parameter + function_pointer.group(3) + else: + signature += dtype + + if not func_macro: + signature += ")" + + self.print_lineno(ln) + if args.get('typedef') or not args.get('functiontype'): + self.data += f".. c:macro:: {name}\n\n" + + if args.get('typedef'): + self.data += " **Typedef**: " + self.lineprefix = "" + self.output_highlight(args.get('purpose', "")) + self.data += "\n\n**Syntax**\n\n" + self.data += f" ``{signature}``\n\n" + else: + self.data += f"``{signature}``\n\n" + else: + self.data += f".. c:function:: {signature}\n\n" + + if not args.get('typedef'): + self.print_lineno(ln) + self.lineprefix = " " + self.output_highlight(args.get('purpose', "")) + self.data += "\n" + + # Put descriptive text into a container (HTML
) to help set + # function prototypes apart + self.lineprefix = " " + + if args.parameterlist: + self.data += ".. container:: kernelindent\n\n" + self.data += f"{self.lineprefix}**Parameters**\n\n" + + for parameter in args.parameterlist: + parameter_name = KernRe(r'\[.*').sub('', parameter) + dtype = args.parametertypes.get(parameter, "") + + if dtype: + self.data += f"{self.lineprefix}``{dtype}``\n" + else: + self.data += f"{self.lineprefix}``{parameter}``\n" + + self.print_lineno(args.parameterdesc_start_lines.get(parameter_name, 0)) + + self.lineprefix = " " + if parameter_name in args.parameterdescs and \ + args.parameterdescs[parameter_name] != KernelDoc.undescribed: + + self.output_highlight(args.parameterdescs[parameter_name]) + self.data += "\n" + else: + self.data += f"{self.lineprefix}*undescribed*\n\n" + self.lineprefix = " " + + self.out_section(args) + self.lineprefix = oldprefix + + def out_enum(self, fname, name, args): + + oldprefix = self.lineprefix + ln = args.declaration_start_line + + self.data += f"\n\n.. c:enum:: {name}\n\n" + + self.print_lineno(ln) + self.lineprefix = " " + self.output_highlight(args.get('purpose', '')) + self.data += "\n" + + self.data += ".. container:: kernelindent\n\n" + outer = self.lineprefix + " " + self.lineprefix = outer + " " + self.data += f"{outer}**Constants**\n\n" + + for parameter in args.parameterlist: + self.data += f"{outer}``{parameter}``\n" + + if args.parameterdescs.get(parameter, '') != KernelDoc.undescribed: + self.output_highlight(args.parameterdescs[parameter]) + else: + self.data += f"{self.lineprefix}*undescribed*\n\n" + self.data += "\n" + + self.lineprefix = oldprefix + self.out_section(args) + + def out_typedef(self, fname, name, args): + + oldprefix = self.lineprefix + ln = args.declaration_start_line + + self.data += f"\n\n.. c:type:: {name}\n\n" + + self.print_lineno(ln) + self.lineprefix = " " + + self.output_highlight(args.get('purpose', '')) + + self.data += "\n" + + self.lineprefix = oldprefix + self.out_section(args) + + def out_struct(self, fname, name, args): + + purpose = args.get('purpose', "") + declaration = args.get('definition', "") + dtype = args.type + ln = args.declaration_start_line + + self.data += f"\n\n.. c:{dtype}:: {name}\n\n" + + self.print_lineno(ln) + + oldprefix = self.lineprefix + self.lineprefix += " " + + self.output_highlight(purpose) + self.data += "\n" + + self.data += ".. container:: kernelindent\n\n" + self.data += f"{self.lineprefix}**Definition**::\n\n" + + self.lineprefix = self.lineprefix + " " + + declaration = declaration.replace("\t", self.lineprefix) + + self.data += f"{self.lineprefix}{dtype} {name}" + ' {' + "\n" + self.data += f"{declaration}{self.lineprefix}" + "};\n\n" + + self.lineprefix = " " + self.data += f"{self.lineprefix}**Members**\n\n" + for parameter in args.parameterlist: + if not parameter or parameter.startswith("#"): + continue + + parameter_name = parameter.split("[", maxsplit=1)[0] + + if args.parameterdescs.get(parameter_name) == KernelDoc.undescribed: + continue + + self.print_lineno(args.parameterdesc_start_lines.get(parameter_name, 0)) + + self.data += f"{self.lineprefix}``{parameter}``\n" + + self.lineprefix = " " + self.output_highlight(args.parameterdescs[parameter_name]) + self.lineprefix = " " + + self.data += "\n" + + self.data += "\n" + + self.lineprefix = oldprefix + self.out_section(args) + + +class ManFormat(OutputFormat): + """Consts and functions used by man pages output""" + + highlights = ( + (type_constant, r"\1"), + (type_constant2, r"\1"), + (type_func, r"\\fB\1\\fP"), + (type_enum, r"\\fI\1\\fP"), + (type_struct, r"\\fI\1\\fP"), + (type_typedef, r"\\fI\1\\fP"), + (type_union, r"\\fI\1\\fP"), + (type_param, r"\\fI\1\\fP"), + (type_param_ref, r"\\fI\1\2\\fP"), + (type_member, r"\\fI\1\2\3\\fP"), + (type_fallback, r"\\fI\1\\fP") + ) + blankline = "" + + date_formats = [ + "%a %b %d %H:%M:%S %Z %Y", + "%a %b %d %H:%M:%S %Y", + "%Y-%m-%d", + "%b %d %Y", + "%B %d %Y", + "%m %d %Y", + ] + + def __init__(self, modulename): + """ + Creates class variables. + + Not really mandatory, but it is a good coding style and makes + pylint happy. + """ + + super().__init__() + self.modulename = modulename + + dt = None + tstamp = os.environ.get("KBUILD_BUILD_TIMESTAMP") + if tstamp: + for fmt in self.date_formats: + try: + dt = datetime.strptime(tstamp, fmt) + break + except ValueError: + pass + + if not dt: + dt = datetime.now() + + self.man_date = dt.strftime("%B %Y") + + def output_highlight(self, block): + """ + Outputs a C symbol that may require being highlighted with + self.highlights variable using troff syntax + """ + + contents = self.highlight_block(block) + + if isinstance(contents, list): + contents = "\n".join(contents) + + for line in contents.strip("\n").split("\n"): + line = KernRe(r"^\s*").sub("", line) + if not line: + continue + + if line[0] == ".": + self.data += "\\&" + line + "\n" + else: + self.data += line + "\n" + + def out_doc(self, fname, name, args): + if not self.check_doc(name, args): + return + + self.data += f'.TH "{self.modulename}" 9 "{self.modulename}" "{self.man_date}" "API Manual" LINUX' + "\n" + + for section, text in args.sections.items(): + self.data += f'.SH "{section}"' + "\n" + self.output_highlight(text) + + def out_function(self, fname, name, args): + """output function in man""" + + self.data += f'.TH "{name}" 9 "{name}" "{self.man_date}" "Kernel Hacker\'s Manual" LINUX' + "\n" + + self.data += ".SH NAME\n" + self.data += f"{name} \\- {args['purpose']}\n" + + self.data += ".SH SYNOPSIS\n" + if args.get('functiontype', ''): + self.data += f'.B "{args["functiontype"]}" {name}' + "\n" + else: + self.data += f'.B "{name}' + "\n" + + count = 0 + parenth = "(" + post = "," + + for parameter in args.parameterlist: + if count == len(args.parameterlist) - 1: + post = ");" + + dtype = args.parametertypes.get(parameter, "") + if function_pointer.match(dtype): + # Pointer-to-function + self.data += f'".BI "{parenth}{function_pointer.group(1)}" " ") ({function_pointer.group(2)}){post}"' + "\n" + else: + dtype = KernRe(r'([^\*])$').sub(r'\1 ', dtype) + + self.data += f'.BI "{parenth}{dtype}" "{post}"' + "\n" + count += 1 + parenth = "" + + if args.parameterlist: + self.data += ".SH ARGUMENTS\n" + + for parameter in args.parameterlist: + parameter_name = re.sub(r'\[.*', '', parameter) + + self.data += f'.IP "{parameter}" 12' + "\n" + self.output_highlight(args.parameterdescs.get(parameter_name, "")) + + for section, text in args.sections.items(): + self.data += f'.SH "{section.upper()}"' + "\n" + self.output_highlight(text) + + def out_enum(self, fname, name, args): + self.data += f'.TH "{self.modulename}" 9 "enum {name}" "{self.man_date}" "API Manual" LINUX' + "\n" + + self.data += ".SH NAME\n" + self.data += f"enum {name} \\- {args['purpose']}\n" + + self.data += ".SH SYNOPSIS\n" + self.data += f"enum {name}" + " {\n" + + count = 0 + for parameter in args.parameterlist: + self.data += f'.br\n.BI " {parameter}"' + "\n" + if count == len(args.parameterlist) - 1: + self.data += "\n};\n" + else: + self.data += ", \n.br\n" + + count += 1 + + self.data += ".SH Constants\n" + + for parameter in args.parameterlist: + parameter_name = KernRe(r'\[.*').sub('', parameter) + self.data += f'.IP "{parameter}" 12' + "\n" + self.output_highlight(args.parameterdescs.get(parameter_name, "")) + + for section, text in args.sections.items(): + self.data += f'.SH "{section}"' + "\n" + self.output_highlight(text) + + def out_typedef(self, fname, name, args): + module = self.modulename + purpose = args.get('purpose') + + self.data += f'.TH "{module}" 9 "{name}" "{self.man_date}" "API Manual" LINUX' + "\n" + + self.data += ".SH NAME\n" + self.data += f"typedef {name} \\- {purpose}\n" + + for section, text in args.sections.items(): + self.data += f'.SH "{section}"' + "\n" + self.output_highlight(text) + + def out_struct(self, fname, name, args): + module = self.modulename + purpose = args.get('purpose') + definition = args.get('definition') + + self.data += f'.TH "{module}" 9 "{args.type} {name}" "{self.man_date}" "API Manual" LINUX' + "\n" + + self.data += ".SH NAME\n" + self.data += f"{args.type} {name} \\- {purpose}\n" + + # Replace tabs with two spaces and handle newlines + declaration = definition.replace("\t", " ") + declaration = KernRe(r"\n").sub('"\n.br\n.BI "', declaration) + + self.data += ".SH SYNOPSIS\n" + self.data += f"{args.type} {name} " + "{" + "\n.br\n" + self.data += f'.BI "{declaration}\n' + "};\n.br\n\n" + + self.data += ".SH Members\n" + for parameter in args.parameterlist: + if parameter.startswith("#"): + continue + + parameter_name = re.sub(r"\[.*", "", parameter) + + if args.parameterdescs.get(parameter_name) == KernelDoc.undescribed: + continue + + self.data += f'.IP "{parameter}" 12' + "\n" + self.output_highlight(args.parameterdescs.get(parameter_name)) + + for section, text in args.sections.items(): + self.data += f'.SH "{section}"' + "\n" + self.output_highlight(text) diff --git a/scripts/lib/kdoc/kdoc_parser.py b/scripts/lib/kdoc/kdoc_parser.py new file mode 100644 index 0000000000..fe730099ec --- /dev/null +++ b/scripts/lib/kdoc/kdoc_parser.py @@ -0,0 +1,1669 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0 +# Copyright(c) 2025: Mauro Carvalho Chehab . +# +# pylint: disable=C0301,C0302,R0904,R0912,R0913,R0914,R0915,R0917,R1702 + +""" +kdoc_parser +=========== + +Read a C language source or header FILE and extract embedded +documentation comments +""" + +import sys +import re +from pprint import pformat + +from kdoc_re import NestedMatch, KernRe +from kdoc_item import KdocItem + +# +# Regular expressions used to parse kernel-doc markups at KernelDoc class. +# +# Let's declare them in lowercase outside any class to make easier to +# convert from the python script. +# +# As those are evaluated at the beginning, no need to cache them +# + +# Allow whitespace at end of comment start. +doc_start = KernRe(r'^/\*\*\s*$', cache=False) + +doc_end = KernRe(r'\*/', cache=False) +doc_com = KernRe(r'\s*\*\s*', cache=False) +doc_com_body = KernRe(r'\s*\* ?', cache=False) +doc_decl = doc_com + KernRe(r'(\w+)', cache=False) + +# @params and a strictly limited set of supported section names +# Specifically: +# Match @word: +# @...: +# @{section-name}: +# while trying to not match literal block starts like "example::" +# +known_section_names = 'description|context|returns?|notes?|examples?' +known_sections = KernRe(known_section_names, flags = re.I) +doc_sect = doc_com + \ + KernRe(r'\s*(\@[.\w]+|\@\.\.\.|' + known_section_names + r')\s*:([^:].*)?$', + flags=re.I, cache=False) + +doc_content = doc_com_body + KernRe(r'(.*)', cache=False) +doc_inline_start = KernRe(r'^\s*/\*\*\s*$', cache=False) +doc_inline_sect = KernRe(r'\s*\*\s*(@\s*[\w][\w\.]*\s*):(.*)', cache=False) +doc_inline_end = KernRe(r'^\s*\*/\s*$', cache=False) +doc_inline_oneline = KernRe(r'^\s*/\*\*\s*(@[\w\s]+):\s*(.*)\s*\*/\s*$', cache=False) +attribute = KernRe(r"__attribute__\s*\(\([a-z0-9,_\*\s\(\)]*\)\)", + flags=re.I | re.S, cache=False) + +export_symbol = KernRe(r'^\s*EXPORT_SYMBOL(_GPL)?\s*\(\s*(\w+)\s*\)\s*', cache=False) +export_symbol_ns = KernRe(r'^\s*EXPORT_SYMBOL_NS(_GPL)?\s*\(\s*(\w+)\s*,\s*"\S+"\)\s*', cache=False) + +type_param = KernRe(r"\@(\w*((\.\w+)|(->\w+))*(\.\.\.)?)", cache=False) + +# +# Tests for the beginning of a kerneldoc block in its various forms. +# +doc_block = doc_com + KernRe(r'DOC:\s*(.*)?', cache=False) +doc_begin_data = KernRe(r"^\s*\*?\s*(struct|union|enum|typedef)\b\s*(\w*)", cache = False) +doc_begin_func = KernRe(str(doc_com) + # initial " * ' + r"(?:\w+\s*\*\s*)?" + # type (not captured) + r'(?:define\s+)?' + # possible "define" (not captured) + r'(\w+)\s*(?:\(\w*\))?\s*' + # name and optional "(...)" + r'(?:[-:].*)?$', # description (not captured) + cache = False) + +# +# A little helper to get rid of excess white space +# +multi_space = KernRe(r'\s\s+') +def trim_whitespace(s): + return multi_space.sub(' ', s.strip()) + +class state: + """ + State machine enums + """ + + # Parser states + NORMAL = 0 # normal code + NAME = 1 # looking for function name + DECLARATION = 2 # We have seen a declaration which might not be done + BODY = 3 # the body of the comment + SPECIAL_SECTION = 4 # doc section ending with a blank line + PROTO = 5 # scanning prototype + DOCBLOCK = 6 # documentation block + INLINE_NAME = 7 # gathering doc outside main block + INLINE_TEXT = 8 # reading the body of inline docs + + name = [ + "NORMAL", + "NAME", + "DECLARATION", + "BODY", + "SPECIAL_SECTION", + "PROTO", + "DOCBLOCK", + "INLINE_NAME", + "INLINE_TEXT", + ] + + +SECTION_DEFAULT = "Description" # default section + +class KernelEntry: + + def __init__(self, config, ln): + self.config = config + + self._contents = [] + self.prototype = "" + + self.warnings = [] + + self.parameterlist = [] + self.parameterdescs = {} + self.parametertypes = {} + self.parameterdesc_start_lines = {} + + self.section_start_lines = {} + self.sections = {} + + self.anon_struct_union = False + + self.leading_space = None + + # State flags + self.brcount = 0 + self.declaration_start_line = ln + 1 + + # + # Management of section contents + # + def add_text(self, text): + self._contents.append(text) + + def contents(self): + return '\n'.join(self._contents) + '\n' + + # TODO: rename to emit_message after removal of kernel-doc.pl + def emit_msg(self, log_msg, warning=True): + """Emit a message""" + + if not warning: + self.config.log.info(log_msg) + return + + # Delegate warning output to output logic, as this way it + # will report warnings/info only for symbols that are output + + self.warnings.append(log_msg) + return + + # + # Begin a new section. + # + def begin_section(self, line_no, title = SECTION_DEFAULT, dump = False): + if dump: + self.dump_section(start_new = True) + self.section = title + self.new_start_line = line_no + + def dump_section(self, start_new=True): + """ + Dumps section contents to arrays/hashes intended for that purpose. + """ + # + # If we have accumulated no contents in the default ("description") + # section, don't bother. + # + if self.section == SECTION_DEFAULT and not self._contents: + return + name = self.section + contents = self.contents() + + if type_param.match(name): + name = type_param.group(1) + + self.parameterdescs[name] = contents + self.parameterdesc_start_lines[name] = self.new_start_line + + self.new_start_line = 0 + + else: + if name in self.sections and self.sections[name] != "": + # Only warn on user-specified duplicate section names + if name != SECTION_DEFAULT: + self.emit_msg(self.new_start_line, + f"duplicate section name '{name}'\n") + # Treat as a new paragraph - add a blank line + self.sections[name] += '\n' + contents + else: + self.sections[name] = contents + self.section_start_lines[name] = self.new_start_line + self.new_start_line = 0 + +# self.config.log.debug("Section: %s : %s", name, pformat(vars(self))) + + if start_new: + self.section = SECTION_DEFAULT + self._contents = [] + + +class KernelDoc: + """ + Read a C language source or header FILE and extract embedded + documentation comments. + """ + + # Section names + + section_context = "Context" + section_return = "Return" + + undescribed = "-- undescribed --" + + def __init__(self, config, fname): + """Initialize internal variables""" + + self.fname = fname + self.config = config + + # Initial state for the state machines + self.state = state.NORMAL + + # Store entry currently being processed + self.entry = None + + # Place all potential outputs into an array + self.entries = [] + + # + # We need Python 3.7 for its "dicts remember the insertion + # order" guarantee + # + if sys.version_info.major == 3 and sys.version_info.minor < 7: + self.emit_msg(0, + 'Python 3.7 or later is required for correct results') + + def emit_msg(self, ln, msg, warning=True): + """Emit a message""" + + log_msg = f"{self.fname}:{ln} {msg}" + + if self.entry: + self.entry.emit_msg(log_msg, warning) + return + + if warning: + self.config.log.warning(log_msg) + else: + self.config.log.info(log_msg) + + def dump_section(self, start_new=True): + """ + Dumps section contents to arrays/hashes intended for that purpose. + """ + + if self.entry: + self.entry.dump_section(start_new) + + # TODO: rename it to store_declaration after removal of kernel-doc.pl + def output_declaration(self, dtype, name, **args): + """ + Stores the entry into an entry array. + + The actual output and output filters will be handled elsewhere + """ + + item = KdocItem(name, dtype, self.entry.declaration_start_line, **args) + item.warnings = self.entry.warnings + + # Drop empty sections + # TODO: improve empty sections logic to emit warnings + sections = self.entry.sections + for section in ["Description", "Return"]: + if section in sections and not sections[section].rstrip(): + del sections[section] + item.set_sections(sections, self.entry.section_start_lines) + item.set_params(self.entry.parameterlist, self.entry.parameterdescs, + self.entry.parametertypes, + self.entry.parameterdesc_start_lines) + self.entries.append(item) + + self.config.log.debug("Output: %s:%s = %s", dtype, name, pformat(args)) + + def reset_state(self, ln): + """ + Ancillary routine to create a new entry. It initializes all + variables used by the state machine. + """ + + self.entry = KernelEntry(self.config, ln) + + # State flags + self.state = state.NORMAL + + def push_parameter(self, ln, decl_type, param, dtype, + org_arg, declaration_name): + """ + Store parameters and their descriptions at self.entry. + """ + + if self.entry.anon_struct_union and dtype == "" and param == "}": + return # Ignore the ending }; from anonymous struct/union + + self.entry.anon_struct_union = False + + param = KernRe(r'[\[\)].*').sub('', param, count=1) + + if dtype == "" and param.endswith("..."): + if KernRe(r'\w\.\.\.$').search(param): + # For named variable parameters of the form `x...`, + # remove the dots + param = param[:-3] + else: + # Handles unnamed variable parameters + param = "..." + + if param not in self.entry.parameterdescs or \ + not self.entry.parameterdescs[param]: + + self.entry.parameterdescs[param] = "variable arguments" + + elif dtype == "" and (not param or param == "void"): + param = "void" + self.entry.parameterdescs[param] = "no arguments" + + elif dtype == "" and param in ["struct", "union"]: + # Handle unnamed (anonymous) union or struct + dtype = param + param = "{unnamed_" + param + "}" + self.entry.parameterdescs[param] = "anonymous\n" + self.entry.anon_struct_union = True + + # Handle cache group enforcing variables: they do not need + # to be described in header files + elif "__cacheline_group" in param: + # Ignore __cacheline_group_begin and __cacheline_group_end + return + + # Warn if parameter has no description + # (but ignore ones starting with # as these are not parameters + # but inline preprocessor statements) + if param not in self.entry.parameterdescs and not param.startswith("#"): + self.entry.parameterdescs[param] = self.undescribed + + if "." not in param: + if decl_type == 'function': + dname = f"{decl_type} parameter" + else: + dname = f"{decl_type} member" + + self.emit_msg(ln, + f"{dname} '{param}' not described in '{declaration_name}'") + + # Strip spaces from param so that it is one continuous string on + # parameterlist. This fixes a problem where check_sections() + # cannot find a parameter like "addr[6 + 2]" because it actually + # appears as "addr[6", "+", "2]" on the parameter list. + # However, it's better to maintain the param string unchanged for + # output, so just weaken the string compare in check_sections() + # to ignore "[blah" in a parameter string. + + self.entry.parameterlist.append(param) + org_arg = KernRe(r'\s\s+').sub(' ', org_arg) + self.entry.parametertypes[param] = org_arg + + + def create_parameter_list(self, ln, decl_type, args, + splitter, declaration_name): + """ + Creates a list of parameters, storing them at self.entry. + """ + + # temporarily replace all commas inside function pointer definition + arg_expr = KernRe(r'(\([^\),]+),') + while arg_expr.search(args): + args = arg_expr.sub(r"\1#", args) + + for arg in args.split(splitter): + # Strip comments + arg = KernRe(r'\/\*.*\*\/').sub('', arg) + + # Ignore argument attributes + arg = KernRe(r'\sPOS0?\s').sub(' ', arg) + + # Strip leading/trailing spaces + arg = arg.strip() + arg = KernRe(r'\s+').sub(' ', arg, count=1) + + if arg.startswith('#'): + # Treat preprocessor directive as a typeless variable just to fill + # corresponding data structures "correctly". Catch it later in + # output_* subs. + + # Treat preprocessor directive as a typeless variable + self.push_parameter(ln, decl_type, arg, "", + "", declaration_name) + + elif KernRe(r'\(.+\)\s*\(').search(arg): + # Pointer-to-function + + arg = arg.replace('#', ',') + + r = KernRe(r'[^\(]+\(\*?\s*([\w\[\]\.]*)\s*\)') + if r.match(arg): + param = r.group(1) + else: + self.emit_msg(ln, f"Invalid param: {arg}") + param = arg + + dtype = KernRe(r'([^\(]+\(\*?)\s*' + re.escape(param)).sub(r'\1', arg) + self.push_parameter(ln, decl_type, param, dtype, + arg, declaration_name) + + elif KernRe(r'\(.+\)\s*\[').search(arg): + # Array-of-pointers + + arg = arg.replace('#', ',') + r = KernRe(r'[^\(]+\(\s*\*\s*([\w\[\]\.]*?)\s*(\s*\[\s*[\w]+\s*\]\s*)*\)') + if r.match(arg): + param = r.group(1) + else: + self.emit_msg(ln, f"Invalid param: {arg}") + param = arg + + dtype = KernRe(r'([^\(]+\(\*?)\s*' + re.escape(param)).sub(r'\1', arg) + + self.push_parameter(ln, decl_type, param, dtype, + arg, declaration_name) + + elif arg: + arg = KernRe(r'\s*:\s*').sub(":", arg) + arg = KernRe(r'\s*\[').sub('[', arg) + + args = KernRe(r'\s*,\s*').split(arg) + if args[0] and '*' in args[0]: + args[0] = re.sub(r'(\*+)\s*', r' \1', args[0]) + + first_arg = [] + r = KernRe(r'^(.*\s+)(.*?\[.*\].*)$') + if args[0] and r.match(args[0]): + args.pop(0) + first_arg.extend(r.group(1)) + first_arg.append(r.group(2)) + else: + first_arg = KernRe(r'\s+').split(args.pop(0)) + + args.insert(0, first_arg.pop()) + dtype = ' '.join(first_arg) + + for param in args: + if KernRe(r'^(\*+)\s*(.*)').match(param): + r = KernRe(r'^(\*+)\s*(.*)') + if not r.match(param): + self.emit_msg(ln, f"Invalid param: {param}") + continue + + param = r.group(1) + + self.push_parameter(ln, decl_type, r.group(2), + f"{dtype} {r.group(1)}", + arg, declaration_name) + + elif KernRe(r'(.*?):(\w+)').search(param): + r = KernRe(r'(.*?):(\w+)') + if not r.match(param): + self.emit_msg(ln, f"Invalid param: {param}") + continue + + if dtype != "": # Skip unnamed bit-fields + self.push_parameter(ln, decl_type, r.group(1), + f"{dtype}:{r.group(2)}", + arg, declaration_name) + else: + self.push_parameter(ln, decl_type, param, dtype, + arg, declaration_name) + + def check_sections(self, ln, decl_name, decl_type): + """ + Check for errors inside sections, emitting warnings if not found + parameters are described. + """ + for section in self.entry.sections: + if section not in self.entry.parameterlist and \ + not known_sections.search(section): + if decl_type == 'function': + dname = f"{decl_type} parameter" + else: + dname = f"{decl_type} member" + self.emit_msg(ln, + f"Excess {dname} '{section}' description in '{decl_name}'") + + def check_return_section(self, ln, declaration_name, return_type): + """ + If the function doesn't return void, warns about the lack of a + return description. + """ + + if not self.config.wreturn: + return + + # Ignore an empty return type (It's a macro) + # Ignore functions with a "void" return type (but not "void *") + if not return_type or KernRe(r'void\s*\w*\s*$').search(return_type): + return + + if not self.entry.sections.get("Return", None): + self.emit_msg(ln, + f"No description found for return value of '{declaration_name}'") + + def dump_struct(self, ln, proto): + """ + Store an entry for an struct or union + """ + + type_pattern = r'(struct|union)' + + qualifiers = [ + "__attribute__", + "__packed", + "__aligned", + "____cacheline_aligned_in_smp", + "____cacheline_aligned", + ] + + definition_body = r'\{(.*)\}\s*' + "(?:" + '|'.join(qualifiers) + ")?" + struct_members = KernRe(type_pattern + r'([^\{\};]+)(\{)([^\{\}]*)(\})([^\{\}\;]*)(\;)') + + # Extract struct/union definition + members = None + declaration_name = None + decl_type = None + + r = KernRe(type_pattern + r'\s+(\w+)\s*' + definition_body) + if r.search(proto): + decl_type = r.group(1) + declaration_name = r.group(2) + members = r.group(3) + else: + r = KernRe(r'typedef\s+' + type_pattern + r'\s*' + definition_body + r'\s*(\w+)\s*;') + + if r.search(proto): + decl_type = r.group(1) + declaration_name = r.group(3) + members = r.group(2) + + if not members: + self.emit_msg(ln, f"{proto} error: Cannot parse struct or union!") + return + + if self.entry.identifier != declaration_name: + self.emit_msg(ln, + f"expecting prototype for {decl_type} {self.entry.identifier}. Prototype was for {decl_type} {declaration_name} instead\n") + return + + args_pattern = r'([^,)]+)' + + sub_prefixes = [ + (KernRe(r'\/\*\s*private:.*?\/\*\s*public:.*?\*\/', re.S | re.I), ''), + (KernRe(r'\/\*\s*private:.*', re.S | re.I), ''), + + # Strip comments + (KernRe(r'\/\*.*?\*\/', re.S), ''), + + # Strip attributes + (attribute, ' '), + (KernRe(r'\s*__aligned\s*\([^;]*\)', re.S), ' '), + (KernRe(r'\s*__counted_by\s*\([^;]*\)', re.S), ' '), + (KernRe(r'\s*__counted_by_(le|be)\s*\([^;]*\)', re.S), ' '), + (KernRe(r'\s*__packed\s*', re.S), ' '), + (KernRe(r'\s*CRYPTO_MINALIGN_ATTR', re.S), ' '), + (KernRe(r'\s*____cacheline_aligned_in_smp', re.S), ' '), + (KernRe(r'\s*____cacheline_aligned', re.S), ' '), + + # Unwrap struct_group macros based on this definition: + # __struct_group(TAG, NAME, ATTRS, MEMBERS...) + # which has variants like: struct_group(NAME, MEMBERS...) + # Only MEMBERS arguments require documentation. + # + # Parsing them happens on two steps: + # + # 1. drop struct group arguments that aren't at MEMBERS, + # storing them as STRUCT_GROUP(MEMBERS) + # + # 2. remove STRUCT_GROUP() ancillary macro. + # + # The original logic used to remove STRUCT_GROUP() using an + # advanced regex: + # + # \bSTRUCT_GROUP(\(((?:(?>[^)(]+)|(?1))*)\))[^;]*; + # + # with two patterns that are incompatible with + # Python re module, as it has: + # + # - a recursive pattern: (?1) + # - an atomic grouping: (?>...) + # + # I tried a simpler version: but it didn't work either: + # \bSTRUCT_GROUP\(([^\)]+)\)[^;]*; + # + # As it doesn't properly match the end parenthesis on some cases. + # + # So, a better solution was crafted: there's now a NestedMatch + # class that ensures that delimiters after a search are properly + # matched. So, the implementation to drop STRUCT_GROUP() will be + # handled in separate. + + (KernRe(r'\bstruct_group\s*\(([^,]*,)', re.S), r'STRUCT_GROUP('), + (KernRe(r'\bstruct_group_attr\s*\(([^,]*,){2}', re.S), r'STRUCT_GROUP('), + (KernRe(r'\bstruct_group_tagged\s*\(([^,]*),([^,]*),', re.S), r'struct \1 \2; STRUCT_GROUP('), + (KernRe(r'\b__struct_group\s*\(([^,]*,){3}', re.S), r'STRUCT_GROUP('), + + # Replace macros + # + # TODO: use NestedMatch for FOO($1, $2, ...) matches + # + # it is better to also move those to the NestedMatch logic, + # to ensure that parenthesis will be properly matched. + + (KernRe(r'__ETHTOOL_DECLARE_LINK_MODE_MASK\s*\(([^\)]+)\)', re.S), r'DECLARE_BITMAP(\1, __ETHTOOL_LINK_MODE_MASK_NBITS)'), + (KernRe(r'DECLARE_PHY_INTERFACE_MASK\s*\(([^\)]+)\)', re.S), r'DECLARE_BITMAP(\1, PHY_INTERFACE_MODE_MAX)'), + (KernRe(r'DECLARE_BITMAP\s*\(' + args_pattern + r',\s*' + args_pattern + r'\)', re.S), r'unsigned long \1[BITS_TO_LONGS(\2)]'), + (KernRe(r'DECLARE_HASHTABLE\s*\(' + args_pattern + r',\s*' + args_pattern + r'\)', re.S), r'unsigned long \1[1 << ((\2) - 1)]'), + (KernRe(r'DECLARE_KFIFO\s*\(' + args_pattern + r',\s*' + args_pattern + r',\s*' + args_pattern + r'\)', re.S), r'\2 *\1'), + (KernRe(r'DECLARE_KFIFO_PTR\s*\(' + args_pattern + r',\s*' + args_pattern + r'\)', re.S), r'\2 *\1'), + (KernRe(r'(?:__)?DECLARE_FLEX_ARRAY\s*\(' + args_pattern + r',\s*' + args_pattern + r'\)', re.S), r'\1 \2[]'), + (KernRe(r'DEFINE_DMA_UNMAP_ADDR\s*\(' + args_pattern + r'\)', re.S), r'dma_addr_t \1'), + (KernRe(r'DEFINE_DMA_UNMAP_LEN\s*\(' + args_pattern + r'\)', re.S), r'__u32 \1'), + (KernRe(r'VIRTIO_DECLARE_FEATURES\s*\(' + args_pattern + r'\)', re.S), r'u64 \1; u64 \1_array[VIRTIO_FEATURES_DWORDS]'), + ] + + # Regexes here are guaranteed to have the end limiter matching + # the start delimiter. Yet, right now, only one replace group + # is allowed. + + sub_nested_prefixes = [ + (re.compile(r'\bSTRUCT_GROUP\('), r'\1'), + ] + + for search, sub in sub_prefixes: + members = search.sub(sub, members) + + nested = NestedMatch() + + for search, sub in sub_nested_prefixes: + members = nested.sub(search, sub, members) + + # Keeps the original declaration as-is + declaration = members + + # Split nested struct/union elements + # + # This loop was simpler at the original kernel-doc perl version, as + # while ($members =~ m/$struct_members/) { ... } + # reads 'members' string on each interaction. + # + # Python behavior is different: it parses 'members' only once, + # creating a list of tuples from the first interaction. + # + # On other words, this won't get nested structs. + # + # So, we need to have an extra loop on Python to override such + # re limitation. + + while True: + tuples = struct_members.findall(members) + if not tuples: + break + + for t in tuples: + newmember = "" + maintype = t[0] + s_ids = t[5] + content = t[3] + + oldmember = "".join(t) + + for s_id in s_ids.split(','): + s_id = s_id.strip() + + newmember += f"{maintype} {s_id}; " + s_id = KernRe(r'[:\[].*').sub('', s_id) + s_id = KernRe(r'^\s*\**(\S+)\s*').sub(r'\1', s_id) + + for arg in content.split(';'): + arg = arg.strip() + + if not arg: + continue + + r = KernRe(r'^([^\(]+\(\*?\s*)([\w\.]*)(\s*\).*)') + if r.match(arg): + # Pointer-to-function + dtype = r.group(1) + name = r.group(2) + extra = r.group(3) + + if not name: + continue + + if not s_id: + # Anonymous struct/union + newmember += f"{dtype}{name}{extra}; " + else: + newmember += f"{dtype}{s_id}.{name}{extra}; " + + else: + arg = arg.strip() + # Handle bitmaps + arg = KernRe(r':\s*\d+\s*').sub('', arg) + + # Handle arrays + arg = KernRe(r'\[.*\]').sub('', arg) + + # Handle multiple IDs + arg = KernRe(r'\s*,\s*').sub(',', arg) + + r = KernRe(r'(.*)\s+([\S+,]+)') + + if r.search(arg): + dtype = r.group(1) + names = r.group(2) + else: + newmember += f"{arg}; " + continue + + for name in names.split(','): + name = KernRe(r'^\s*\**(\S+)\s*').sub(r'\1', name).strip() + + if not name: + continue + + if not s_id: + # Anonymous struct/union + newmember += f"{dtype} {name}; " + else: + newmember += f"{dtype} {s_id}.{name}; " + + members = members.replace(oldmember, newmember) + + # Ignore other nested elements, like enums + members = re.sub(r'(\{[^\{\}]*\})', '', members) + + self.create_parameter_list(ln, decl_type, members, ';', + declaration_name) + self.check_sections(ln, declaration_name, decl_type) + + # Adjust declaration for better display + declaration = KernRe(r'([\{;])').sub(r'\1\n', declaration) + declaration = KernRe(r'\}\s+;').sub('};', declaration) + + # Better handle inlined enums + while True: + r = KernRe(r'(enum\s+\{[^\}]+),([^\n])') + if not r.search(declaration): + break + + declaration = r.sub(r'\1,\n\2', declaration) + + def_args = declaration.split('\n') + level = 1 + declaration = "" + for clause in def_args: + + clause = clause.strip() + clause = KernRe(r'\s+').sub(' ', clause, count=1) + + if not clause: + continue + + if '}' in clause and level > 1: + level -= 1 + + if not KernRe(r'^\s*#').match(clause): + declaration += "\t" * level + + declaration += "\t" + clause + "\n" + if "{" in clause and "}" not in clause: + level += 1 + + self.output_declaration(decl_type, declaration_name, + definition=declaration, + purpose=self.entry.declaration_purpose) + + def dump_enum(self, ln, proto): + """ + Stores an enum inside self.entries array. + """ + + # Ignore members marked private + proto = KernRe(r'\/\*\s*private:.*?\/\*\s*public:.*?\*\/', flags=re.S).sub('', proto) + proto = KernRe(r'\/\*\s*private:.*}', flags=re.S).sub('}', proto) + + # Strip comments + proto = KernRe(r'\/\*.*?\*\/', flags=re.S).sub('', proto) + + # Strip #define macros inside enums + proto = KernRe(r'#\s*((define|ifdef|if)\s+|endif)[^;]*;', flags=re.S).sub('', proto) + + # + # Parse out the name and members of the enum. Typedef form first. + # + r = KernRe(r'typedef\s+enum\s*\{(.*)\}\s*(\w*)\s*;') + if r.search(proto): + declaration_name = r.group(2) + members = r.group(1).rstrip() + # + # Failing that, look for a straight enum + # + else: + r = KernRe(r'enum\s+(\w*)\s*\{(.*)\}') + if r.match(proto): + declaration_name = r.group(1) + members = r.group(2).rstrip() + # + # OK, this isn't going to work. + # + else: + self.emit_msg(ln, f"{proto}: error: Cannot parse enum!") + return + # + # Make sure we found what we were expecting. + # + if self.entry.identifier != declaration_name: + if self.entry.identifier == "": + self.emit_msg(ln, + f"{proto}: wrong kernel-doc identifier on prototype") + else: + self.emit_msg(ln, + f"expecting prototype for enum {self.entry.identifier}. " + f"Prototype was for enum {declaration_name} instead") + return + + if not declaration_name: + declaration_name = "(anonymous)" + # + # Parse out the name of each enum member, and verify that we + # have a description for it. + # + member_set = set() + members = KernRe(r'\([^;)]*\)').sub('', members) + for arg in members.split(','): + if not arg: + continue + arg = KernRe(r'^\s*(\w+).*').sub(r'\1', arg) + self.entry.parameterlist.append(arg) + if arg not in self.entry.parameterdescs: + self.entry.parameterdescs[arg] = self.undescribed + self.emit_msg(ln, + f"Enum value '{arg}' not described in enum '{declaration_name}'") + member_set.add(arg) + # + # Ensure that every described member actually exists in the enum. + # + for k in self.entry.parameterdescs: + if k not in member_set: + self.emit_msg(ln, + f"Excess enum value '%{k}' description in '{declaration_name}'") + + self.output_declaration('enum', declaration_name, + purpose=self.entry.declaration_purpose) + + def dump_declaration(self, ln, prototype): + """ + Stores a data declaration inside self.entries array. + """ + + if self.entry.decl_type == "enum": + self.dump_enum(ln, prototype) + elif self.entry.decl_type == "typedef": + self.dump_typedef(ln, prototype) + elif self.entry.decl_type in ["union", "struct"]: + self.dump_struct(ln, prototype) + else: + # This would be a bug + self.emit_message(ln, f'Unknown declaration type: {self.entry.decl_type}') + + def dump_function(self, ln, prototype): + """ + Stores a function of function macro inside self.entries array. + """ + + func_macro = False + return_type = '' + decl_type = 'function' + + # Prefixes that would be removed + sub_prefixes = [ + (r"^static +", "", 0), + (r"^extern +", "", 0), + (r"^asmlinkage +", "", 0), + (r"^inline +", "", 0), + (r"^__inline__ +", "", 0), + (r"^__inline +", "", 0), + (r"^__always_inline +", "", 0), + (r"^noinline +", "", 0), + (r"^__FORTIFY_INLINE +", "", 0), + (r"__init +", "", 0), + (r"__init_or_module +", "", 0), + (r"__deprecated +", "", 0), + (r"__flatten +", "", 0), + (r"__meminit +", "", 0), + (r"__must_check +", "", 0), + (r"__weak +", "", 0), + (r"__sched +", "", 0), + (r"_noprof", "", 0), + (r"__printf\s*\(\s*\d*\s*,\s*\d*\s*\) +", "", 0), + (r"__(?:re)?alloc_size\s*\(\s*\d+\s*(?:,\s*\d+\s*)?\) +", "", 0), + (r"__diagnose_as\s*\(\s*\S+\s*(?:,\s*\d+\s*)*\) +", "", 0), + (r"DECL_BUCKET_PARAMS\s*\(\s*(\S+)\s*,\s*(\S+)\s*\)", r"\1, \2", 0), + (r"__attribute_const__ +", "", 0), + + # It seems that Python support for re.X is broken: + # At least for me (Python 3.13), this didn't work +# (r""" +# __attribute__\s*\(\( +# (?: +# [\w\s]+ # attribute name +# (?:\([^)]*\))? # attribute arguments +# \s*,? # optional comma at the end +# )+ +# \)\)\s+ +# """, "", re.X), + + # So, remove whitespaces and comments from it + (r"__attribute__\s*\(\((?:[\w\s]+(?:\([^)]*\))?\s*,?)+\)\)\s+", "", 0), + ] + + for search, sub, flags in sub_prefixes: + prototype = KernRe(search, flags).sub(sub, prototype) + + # Macros are a special case, as they change the prototype format + new_proto = KernRe(r"^#\s*define\s+").sub("", prototype) + if new_proto != prototype: + is_define_proto = True + prototype = new_proto + else: + is_define_proto = False + + # Yes, this truly is vile. We are looking for: + # 1. Return type (may be nothing if we're looking at a macro) + # 2. Function name + # 3. Function parameters. + # + # All the while we have to watch out for function pointer parameters + # (which IIRC is what the two sections are for), C types (these + # regexps don't even start to express all the possibilities), and + # so on. + # + # If you mess with these regexps, it's a good idea to check that + # the following functions' documentation still comes out right: + # - parport_register_device (function pointer parameters) + # - atomic_set (macro) + # - pci_match_device, __copy_to_user (long return type) + + name = r'[a-zA-Z0-9_~:]+' + prototype_end1 = r'[^\(]*' + prototype_end2 = r'[^\{]*' + prototype_end = fr'\(({prototype_end1}|{prototype_end2})\)' + + # Besides compiling, Perl qr{[\w\s]+} works as a non-capturing group. + # So, this needs to be mapped in Python with (?:...)? or (?:...)+ + + type1 = r'(?:[\w\s]+)?' + type2 = r'(?:[\w\s]+\*+)+' + + found = False + + if is_define_proto: + r = KernRe(r'^()(' + name + r')\s+') + + if r.search(prototype): + return_type = '' + declaration_name = r.group(2) + func_macro = True + + found = True + + if not found: + patterns = [ + rf'^()({name})\s*{prototype_end}', + rf'^({type1})\s+({name})\s*{prototype_end}', + rf'^({type2})\s*({name})\s*{prototype_end}', + ] + + for p in patterns: + r = KernRe(p) + + if r.match(prototype): + + return_type = r.group(1) + declaration_name = r.group(2) + args = r.group(3) + + self.create_parameter_list(ln, decl_type, args, ',', + declaration_name) + + found = True + break + if not found: + self.emit_msg(ln, + f"cannot understand function prototype: '{prototype}'") + return + + if self.entry.identifier != declaration_name: + self.emit_msg(ln, + f"expecting prototype for {self.entry.identifier}(). Prototype was for {declaration_name}() instead") + return + + self.check_sections(ln, declaration_name, "function") + + self.check_return_section(ln, declaration_name, return_type) + + if 'typedef' in return_type: + self.output_declaration(decl_type, declaration_name, + typedef=True, + functiontype=return_type, + purpose=self.entry.declaration_purpose, + func_macro=func_macro) + else: + self.output_declaration(decl_type, declaration_name, + typedef=False, + functiontype=return_type, + purpose=self.entry.declaration_purpose, + func_macro=func_macro) + + def dump_typedef(self, ln, proto): + """ + Stores a typedef inside self.entries array. + """ + + typedef_type = r'((?:\s+[\w\*]+\b){0,7}\s+(?:\w+\b|\*+))\s*' + typedef_ident = r'\*?\s*(\w\S+)\s*' + typedef_args = r'\s*\((.*)\);' + + typedef1 = KernRe(r'typedef' + typedef_type + r'\(' + typedef_ident + r'\)' + typedef_args) + typedef2 = KernRe(r'typedef' + typedef_type + typedef_ident + typedef_args) + + # Strip comments + proto = KernRe(r'/\*.*?\*/', flags=re.S).sub('', proto) + + # Parse function typedef prototypes + for r in [typedef1, typedef2]: + if not r.match(proto): + continue + + return_type = r.group(1).strip() + declaration_name = r.group(2) + args = r.group(3) + + if self.entry.identifier != declaration_name: + self.emit_msg(ln, + f"expecting prototype for typedef {self.entry.identifier}. Prototype was for typedef {declaration_name} instead\n") + return + + decl_type = 'function' + self.create_parameter_list(ln, decl_type, args, ',', declaration_name) + + self.output_declaration(decl_type, declaration_name, + typedef=True, + functiontype=return_type, + purpose=self.entry.declaration_purpose) + return + + # Handle nested parentheses or brackets + r = KernRe(r'(\(*.\)\s*|\[*.\]\s*);$') + while r.search(proto): + proto = r.sub('', proto) + + # Parse simple typedefs + r = KernRe(r'typedef.*\s+(\w+)\s*;') + if r.match(proto): + declaration_name = r.group(1) + + if self.entry.identifier != declaration_name: + self.emit_msg(ln, + f"expecting prototype for typedef {self.entry.identifier}. Prototype was for typedef {declaration_name} instead\n") + return + + self.output_declaration('typedef', declaration_name, + purpose=self.entry.declaration_purpose) + return + + self.emit_msg(ln, "error: Cannot parse typedef!") + + @staticmethod + def process_export(function_set, line): + """ + process EXPORT_SYMBOL* tags + + This method doesn't use any variable from the class, so declare it + with a staticmethod decorator. + """ + + # We support documenting some exported symbols with different + # names. A horrible hack. + suffixes = [ '_noprof' ] + + # Note: it accepts only one EXPORT_SYMBOL* per line, as having + # multiple export lines would violate Kernel coding style. + + if export_symbol.search(line): + symbol = export_symbol.group(2) + elif export_symbol_ns.search(line): + symbol = export_symbol_ns.group(2) + else: + return False + # + # Found an export, trim out any special suffixes + # + for suffix in suffixes: + # Be backward compatible with Python < 3.9 + if symbol.endswith(suffix): + symbol = symbol[:-len(suffix)] + function_set.add(symbol) + return True + + def process_normal(self, ln, line): + """ + STATE_NORMAL: looking for the /** to begin everything. + """ + + if not doc_start.match(line): + return + + # start a new entry + self.reset_state(ln) + + # next line is always the function name + self.state = state.NAME + + def process_name(self, ln, line): + """ + STATE_NAME: Looking for the "name - description" line + """ + # + # Check for a DOC: block and handle them specially. + # + if doc_block.search(line): + + if not doc_block.group(1): + self.entry.begin_section(ln, "Introduction") + else: + self.entry.begin_section(ln, doc_block.group(1)) + + self.entry.identifier = self.entry.section + self.state = state.DOCBLOCK + # + # Otherwise we're looking for a normal kerneldoc declaration line. + # + elif doc_decl.search(line): + self.entry.identifier = doc_decl.group(1) + + # Test for data declaration + if doc_begin_data.search(line): + self.entry.decl_type = doc_begin_data.group(1) + self.entry.identifier = doc_begin_data.group(2) + # + # Look for a function description + # + elif doc_begin_func.search(line): + self.entry.identifier = doc_begin_func.group(1) + self.entry.decl_type = "function" + # + # We struck out. + # + else: + self.emit_msg(ln, + f"This comment starts with '/**', but isn't a kernel-doc comment. Refer Documentation/doc-guide/kernel-doc.rst\n{line}") + self.state = state.NORMAL + return + # + # OK, set up for a new kerneldoc entry. + # + self.state = state.BODY + self.entry.identifier = self.entry.identifier.strip(" ") + # if there's no @param blocks need to set up default section here + self.entry.begin_section(ln + 1) + # + # Find the description portion, which *should* be there but + # isn't always. + # (We should be able to capture this from the previous parsing - someday) + # + r = KernRe("[-:](.*)") + if r.search(line): + self.entry.declaration_purpose = trim_whitespace(r.group(1)) + self.state = state.DECLARATION + else: + self.entry.declaration_purpose = "" + + if not self.entry.declaration_purpose and self.config.wshort_desc: + self.emit_msg(ln, + f"missing initial short description on line:\n{line}") + + if not self.entry.identifier and self.entry.decl_type != "enum": + self.emit_msg(ln, + f"wrong kernel-doc identifier on line:\n{line}") + self.state = state.NORMAL + + if self.config.verbose: + self.emit_msg(ln, + f"Scanning doc for {self.entry.decl_type} {self.entry.identifier}", + warning=False) + # + # Failed to find an identifier. Emit a warning + # + else: + self.emit_msg(ln, f"Cannot find identifier on line:\n{line}") + + # + # Helper function to determine if a new section is being started. + # + def is_new_section(self, ln, line): + if doc_sect.search(line): + self.state = state.BODY + # + # Pick out the name of our new section, tweaking it if need be. + # + newsection = doc_sect.group(1) + if newsection.lower() == 'description': + newsection = 'Description' + elif newsection.lower() == 'context': + newsection = 'Context' + self.state = state.SPECIAL_SECTION + elif newsection.lower() in ["@return", "@returns", + "return", "returns"]: + newsection = "Return" + self.state = state.SPECIAL_SECTION + elif newsection[0] == '@': + self.state = state.SPECIAL_SECTION + # + # Initialize the contents, and get the new section going. + # + newcontents = doc_sect.group(2) + if not newcontents: + newcontents = "" + self.dump_section() + self.entry.begin_section(ln, newsection) + self.entry.leading_space = None + + self.entry.add_text(newcontents.lstrip()) + return True + return False + + # + # Helper function to detect (and effect) the end of a kerneldoc comment. + # + def is_comment_end(self, ln, line): + if doc_end.search(line): + self.dump_section() + + # Look for doc_com + + doc_end: + r = KernRe(r'\s*\*\s*[a-zA-Z_0-9:\.]+\*/') + if r.match(line): + self.emit_msg(ln, f"suspicious ending line: {line}") + + self.entry.prototype = "" + self.entry.new_start_line = ln + 1 + + self.state = state.PROTO + return True + return False + + + def process_decl(self, ln, line): + """ + STATE_DECLARATION: We've seen the beginning of a declaration + """ + if self.is_new_section(ln, line) or self.is_comment_end(ln, line): + return + # + # Look for anything with the " * " line beginning. + # + if doc_content.search(line): + cont = doc_content.group(1) + # + # A blank line means that we have moved out of the declaration + # part of the comment (without any "special section" parameter + # descriptions). + # + if cont == "": + self.state = state.BODY + # + # Otherwise we have more of the declaration section to soak up. + # + else: + self.entry.declaration_purpose = \ + trim_whitespace(self.entry.declaration_purpose + ' ' + cont) + else: + # Unknown line, ignore + self.emit_msg(ln, f"bad line: {line}") + + + def process_special(self, ln, line): + """ + STATE_SPECIAL_SECTION: a section ending with a blank line + """ + # + # If we have hit a blank line (only the " * " marker), then this + # section is done. + # + if KernRe(r"\s*\*\s*$").match(line): + self.entry.begin_section(ln, dump = True) + self.state = state.BODY + return + # + # Not a blank line, look for the other ways to end the section. + # + if self.is_new_section(ln, line) or self.is_comment_end(ln, line): + return + # + # OK, we should have a continuation of the text for this section. + # + if doc_content.search(line): + cont = doc_content.group(1) + # + # If the lines of text after the first in a special section have + # leading white space, we need to trim it out or Sphinx will get + # confused. For the second line (the None case), see what we + # find there and remember it. + # + if self.entry.leading_space is None: + r = KernRe(r'^(\s+)') + if r.match(cont): + self.entry.leading_space = len(r.group(1)) + else: + self.entry.leading_space = 0 + # + # Otherwise, before trimming any leading chars, be *sure* + # that they are white space. We should maybe warn if this + # isn't the case. + # + for i in range(0, self.entry.leading_space): + if cont[i] != " ": + self.entry.leading_space = i + break + # + # Add the trimmed result to the section and we're done. + # + self.entry.add_text(cont[self.entry.leading_space:]) + else: + # Unknown line, ignore + self.emit_msg(ln, f"bad line: {line}") + + def process_body(self, ln, line): + """ + STATE_BODY: the bulk of a kerneldoc comment. + """ + if self.is_new_section(ln, line) or self.is_comment_end(ln, line): + return + + if doc_content.search(line): + cont = doc_content.group(1) + self.entry.add_text(cont) + else: + # Unknown line, ignore + self.emit_msg(ln, f"bad line: {line}") + + def process_inline_name(self, ln, line): + """STATE_INLINE_NAME: beginning of docbook comments within a prototype.""" + + if doc_inline_sect.search(line): + self.entry.begin_section(ln, doc_inline_sect.group(1)) + self.entry.add_text(doc_inline_sect.group(2).lstrip()) + self.state = state.INLINE_TEXT + elif doc_inline_end.search(line): + self.dump_section() + self.state = state.PROTO + elif doc_content.search(line): + self.emit_msg(ln, f"Incorrect use of kernel-doc format: {line}") + self.state = state.PROTO + # else ... ?? + + def process_inline_text(self, ln, line): + """STATE_INLINE_TEXT: docbook comments within a prototype.""" + + if doc_inline_end.search(line): + self.dump_section() + self.state = state.PROTO + elif doc_content.search(line): + self.entry.add_text(doc_content.group(1)) + # else ... ?? + + def syscall_munge(self, ln, proto): # pylint: disable=W0613 + """ + Handle syscall definitions + """ + + is_void = False + + # Strip newlines/CR's + proto = re.sub(r'[\r\n]+', ' ', proto) + + # Check if it's a SYSCALL_DEFINE0 + if 'SYSCALL_DEFINE0' in proto: + is_void = True + + # Replace SYSCALL_DEFINE with correct return type & function name + proto = KernRe(r'SYSCALL_DEFINE.*\(').sub('long sys_', proto) + + r = KernRe(r'long\s+(sys_.*?),') + if r.search(proto): + proto = KernRe(',').sub('(', proto, count=1) + elif is_void: + proto = KernRe(r'\)').sub('(void)', proto, count=1) + + # Now delete all of the odd-numbered commas in the proto + # so that argument types & names don't have a comma between them + count = 0 + length = len(proto) + + if is_void: + length = 0 # skip the loop if is_void + + for ix in range(length): + if proto[ix] == ',': + count += 1 + if count % 2 == 1: + proto = proto[:ix] + ' ' + proto[ix + 1:] + + return proto + + def tracepoint_munge(self, ln, proto): + """ + Handle tracepoint definitions + """ + + tracepointname = None + tracepointargs = None + + # Match tracepoint name based on different patterns + r = KernRe(r'TRACE_EVENT\((.*?),') + if r.search(proto): + tracepointname = r.group(1) + + r = KernRe(r'DEFINE_SINGLE_EVENT\((.*?),') + if r.search(proto): + tracepointname = r.group(1) + + r = KernRe(r'DEFINE_EVENT\((.*?),(.*?),') + if r.search(proto): + tracepointname = r.group(2) + + if tracepointname: + tracepointname = tracepointname.lstrip() + + r = KernRe(r'TP_PROTO\((.*?)\)') + if r.search(proto): + tracepointargs = r.group(1) + + if not tracepointname or not tracepointargs: + self.emit_msg(ln, + f"Unrecognized tracepoint format:\n{proto}\n") + else: + proto = f"static inline void trace_{tracepointname}({tracepointargs})" + self.entry.identifier = f"trace_{self.entry.identifier}" + + return proto + + def process_proto_function(self, ln, line): + """Ancillary routine to process a function prototype""" + + # strip C99-style comments to end of line + line = KernRe(r"\/\/.*$", re.S).sub('', line) + # + # Soak up the line's worth of prototype text, stopping at { or ; if present. + # + if KernRe(r'\s*#\s*define').match(line): + self.entry.prototype = line + elif not line.startswith('#'): # skip other preprocessor stuff + r = KernRe(r'([^\{]*)') + if r.match(line): + self.entry.prototype += r.group(1) + " " + # + # If we now have the whole prototype, clean it up and declare victory. + # + if '{' in line or ';' in line or KernRe(r'\s*#\s*define').match(line): + # strip comments and surrounding spaces + self.entry.prototype = KernRe(r'/\*.*\*/').sub('', self.entry.prototype).strip() + # + # Handle self.entry.prototypes for function pointers like: + # int (*pcs_config)(struct foo) + # by turning it into + # int pcs_config(struct foo) + # + r = KernRe(r'^(\S+\s+)\(\s*\*(\S+)\)') + self.entry.prototype = r.sub(r'\1\2', self.entry.prototype) + # + # Handle special declaration syntaxes + # + if 'SYSCALL_DEFINE' in self.entry.prototype: + self.entry.prototype = self.syscall_munge(ln, + self.entry.prototype) + else: + r = KernRe(r'TRACE_EVENT|DEFINE_EVENT|DEFINE_SINGLE_EVENT') + if r.search(self.entry.prototype): + self.entry.prototype = self.tracepoint_munge(ln, + self.entry.prototype) + # + # ... and we're done + # + self.dump_function(ln, self.entry.prototype) + self.reset_state(ln) + + def process_proto_type(self, ln, line): + """Ancillary routine to process a type""" + + # Strip C99-style comments and surrounding whitespace + line = KernRe(r"//.*$", re.S).sub('', line).strip() + if not line: + return # nothing to see here + + # To distinguish preprocessor directive from regular declaration later. + if line.startswith('#'): + line += ";" + # + # Split the declaration on any of { } or ;, and accumulate pieces + # until we hit a semicolon while not inside {brackets} + # + r = KernRe(r'(.*?)([{};])') + for chunk in r.split(line): + if chunk: # Ignore empty matches + self.entry.prototype += chunk + # + # This cries out for a match statement ... someday after we can + # drop Python 3.9 ... + # + if chunk == '{': + self.entry.brcount += 1 + elif chunk == '}': + self.entry.brcount -= 1 + elif chunk == ';' and self.entry.brcount <= 0: + self.dump_declaration(ln, self.entry.prototype) + self.reset_state(ln) + return + # + # We hit the end of the line while still in the declaration; put + # in a space to represent the newline. + # + self.entry.prototype += ' ' + + def process_proto(self, ln, line): + """STATE_PROTO: reading a function/whatever prototype.""" + + if doc_inline_oneline.search(line): + self.entry.begin_section(ln, doc_inline_oneline.group(1)) + self.entry.add_text(doc_inline_oneline.group(2)) + self.dump_section() + + elif doc_inline_start.search(line): + self.state = state.INLINE_NAME + + elif self.entry.decl_type == 'function': + self.process_proto_function(ln, line) + + else: + self.process_proto_type(ln, line) + + def process_docblock(self, ln, line): + """STATE_DOCBLOCK: within a DOC: block.""" + + if doc_end.search(line): + self.dump_section() + self.output_declaration("doc", self.entry.identifier) + self.reset_state(ln) + + elif doc_content.search(line): + self.entry.add_text(doc_content.group(1)) + + def parse_export(self): + """ + Parses EXPORT_SYMBOL* macros from a single Kernel source file. + """ + + export_table = set() + + try: + with open(self.fname, "r", encoding="utf8", + errors="backslashreplace") as fp: + + for line in fp: + self.process_export(export_table, line) + + except IOError: + return None + + return export_table + + # + # The state/action table telling us which function to invoke in + # each state. + # + state_actions = { + state.NORMAL: process_normal, + state.NAME: process_name, + state.BODY: process_body, + state.DECLARATION: process_decl, + state.SPECIAL_SECTION: process_special, + state.INLINE_NAME: process_inline_name, + state.INLINE_TEXT: process_inline_text, + state.PROTO: process_proto, + state.DOCBLOCK: process_docblock, + } + + def parse_kdoc(self): + """ + Open and process each line of a C source file. + The parsing is controlled via a state machine, and the line is passed + to a different process function depending on the state. The process + function may update the state as needed. + + Besides parsing kernel-doc tags, it also parses export symbols. + """ + + prev = "" + prev_ln = None + export_table = set() + + try: + with open(self.fname, "r", encoding="utf8", + errors="backslashreplace") as fp: + for ln, line in enumerate(fp): + + line = line.expandtabs().strip("\n") + + # Group continuation lines on prototypes + if self.state == state.PROTO: + if line.endswith("\\"): + prev += line.rstrip("\\") + if not prev_ln: + prev_ln = ln + continue + + if prev: + ln = prev_ln + line = prev + line + prev = "" + prev_ln = None + + self.config.log.debug("%d %s: %s", + ln, state.name[self.state], + line) + + # This is an optimization over the original script. + # There, when export_file was used for the same file, + # it was read twice. Here, we use the already-existing + # loop to parse exported symbols as well. + # + if (self.state != state.NORMAL) or \ + not self.process_export(export_table, line): + # Hand this line to the appropriate state handler + self.state_actions[self.state](self, ln, line) + + except OSError: + self.config.log.error(f"Error: Cannot open file {self.fname}") + + return export_table, self.entries diff --git a/scripts/lib/kdoc/kdoc_re.py b/scripts/lib/kdoc/kdoc_re.py new file mode 100644 index 0000000000..612223e1e7 --- /dev/null +++ b/scripts/lib/kdoc/kdoc_re.py @@ -0,0 +1,270 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0 +# Copyright(c) 2025: Mauro Carvalho Chehab . + +""" +Regular expression ancillary classes. + +Those help caching regular expressions and do matching for kernel-doc. +""" + +import re + +# Local cache for regular expressions +re_cache = {} + + +class KernRe: + """ + Helper class to simplify regex declaration and usage, + + It calls re.compile for a given pattern. It also allows adding + regular expressions and define sub at class init time. + + Regular expressions can be cached via an argument, helping to speedup + searches. + """ + + def _add_regex(self, string, flags): + """ + Adds a new regex or re-use it from the cache. + """ + self.regex = re_cache.get(string, None) + if not self.regex: + self.regex = re.compile(string, flags=flags) + if self.cache: + re_cache[string] = self.regex + + def __init__(self, string, cache=True, flags=0): + """ + Compile a regular expression and initialize internal vars. + """ + + self.cache = cache + self.last_match = None + + self._add_regex(string, flags) + + def __str__(self): + """ + Return the regular expression pattern. + """ + return self.regex.pattern + + def __add__(self, other): + """ + Allows adding two regular expressions into one. + """ + + return KernRe(str(self) + str(other), cache=self.cache or other.cache, + flags=self.regex.flags | other.regex.flags) + + def match(self, string): + """ + Handles a re.match storing its results + """ + + self.last_match = self.regex.match(string) + return self.last_match + + def search(self, string): + """ + Handles a re.search storing its results + """ + + self.last_match = self.regex.search(string) + return self.last_match + + def findall(self, string): + """ + Alias to re.findall + """ + + return self.regex.findall(string) + + def split(self, string): + """ + Alias to re.split + """ + + return self.regex.split(string) + + def sub(self, sub, string, count=0): + """ + Alias to re.sub + """ + + return self.regex.sub(sub, string, count=count) + + def group(self, num): + """ + Returns the group results of the last match + """ + + return self.last_match.group(num) + + +class NestedMatch: + """ + Finding nested delimiters is hard with regular expressions. It is + even harder on Python with its normal re module, as there are several + advanced regular expressions that are missing. + + This is the case of this pattern: + + '\\bSTRUCT_GROUP(\\(((?:(?>[^)(]+)|(?1))*)\\))[^;]*;' + + which is used to properly match open/close parenthesis of the + string search STRUCT_GROUP(), + + Add a class that counts pairs of delimiters, using it to match and + replace nested expressions. + + The original approach was suggested by: + https://stackoverflow.com/questions/5454322/python-how-to-match-nested-parentheses-with-regex + + Although I re-implemented it to make it more generic and match 3 types + of delimiters. The logic checks if delimiters are paired. If not, it + will ignore the search string. + """ + + # TODO: make NestedMatch handle multiple match groups + # + # Right now, regular expressions to match it are defined only up to + # the start delimiter, e.g.: + # + # \bSTRUCT_GROUP\( + # + # is similar to: STRUCT_GROUP\((.*)\) + # except that the content inside the match group is delimiter's aligned. + # + # The content inside parenthesis are converted into a single replace + # group (e.g. r`\1'). + # + # It would be nice to change such definition to support multiple + # match groups, allowing a regex equivalent to. + # + # FOO\((.*), (.*), (.*)\) + # + # it is probably easier to define it not as a regular expression, but + # with some lexical definition like: + # + # FOO(arg1, arg2, arg3) + + DELIMITER_PAIRS = { + '{': '}', + '(': ')', + '[': ']', + } + + RE_DELIM = re.compile(r'[\{\}\[\]\(\)]') + + def _search(self, regex, line): + """ + Finds paired blocks for a regex that ends with a delimiter. + + The suggestion of using finditer to match pairs came from: + https://stackoverflow.com/questions/5454322/python-how-to-match-nested-parentheses-with-regex + but I ended using a different implementation to align all three types + of delimiters and seek for an initial regular expression. + + The algorithm seeks for open/close paired delimiters and place them + into a stack, yielding a start/stop position of each match when the + stack is zeroed. + + The algorithm shoud work fine for properly paired lines, but will + silently ignore end delimiters that preceeds an start delimiter. + This should be OK for kernel-doc parser, as unaligned delimiters + would cause compilation errors. So, we don't need to rise exceptions + to cover such issues. + """ + + stack = [] + + for match_re in regex.finditer(line): + start = match_re.start() + offset = match_re.end() + + d = line[offset - 1] + if d not in self.DELIMITER_PAIRS: + continue + + end = self.DELIMITER_PAIRS[d] + stack.append(end) + + for match in self.RE_DELIM.finditer(line[offset:]): + pos = match.start() + offset + + d = line[pos] + + if d in self.DELIMITER_PAIRS: + end = self.DELIMITER_PAIRS[d] + + stack.append(end) + continue + + # Does the end delimiter match what it is expected? + if stack and d == stack[-1]: + stack.pop() + + if not stack: + yield start, offset, pos + 1 + break + + def search(self, regex, line): + """ + This is similar to re.search: + + It matches a regex that it is followed by a delimiter, + returning occurrences only if all delimiters are paired. + """ + + for t in self._search(regex, line): + + yield line[t[0]:t[2]] + + def sub(self, regex, sub, line, count=0): + """ + This is similar to re.sub: + + It matches a regex that it is followed by a delimiter, + replacing occurrences only if all delimiters are paired. + + if r'\1' is used, it works just like re: it places there the + matched paired data with the delimiter stripped. + + If count is different than zero, it will replace at most count + items. + """ + out = "" + + cur_pos = 0 + n = 0 + + for start, end, pos in self._search(regex, line): + out += line[cur_pos:start] + + # Value, ignoring start/end delimiters + value = line[end:pos - 1] + + # replaces \1 at the sub string, if \1 is used there + new_sub = sub + new_sub = new_sub.replace(r'\1', value) + + out += new_sub + + # Drop end ';' if any + if line[pos] == ';': + pos += 1 + + cur_pos = pos + n += 1 + + if count and count >= n: + break + + # Append the remaining string + l = len(line) + out += line[cur_pos:l] + + return out From 7f58572cff7e79c7733ba504cd200af2287706f5 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 14 Aug 2025 18:13:19 +0100 Subject: [PATCH 0246/2396] scripts/kernel-doc: strip QEMU_ from function definitions This commit is the Python version of our older commit b30df2751e5 ("scripts/kernel-doc: strip QEMU_ from function definitions"). Some versions of Sphinx get confused if function attributes are left on the C code from kernel-doc; strip out any QEMU_* prefixes from function prototypes. Signed-off-by: Peter Maydell Reviewed-by: Paolo Bonzini Reviewed-by: Mauro Carvalho Chehab Message-id: 20250814171324.1614516-5-peter.maydell@linaro.org --- scripts/lib/kdoc/kdoc_parser.py | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/lib/kdoc/kdoc_parser.py b/scripts/lib/kdoc/kdoc_parser.py index fe730099ec..32b4356292 100644 --- a/scripts/lib/kdoc/kdoc_parser.py +++ b/scripts/lib/kdoc/kdoc_parser.py @@ -907,6 +907,7 @@ class KernelDoc: (r"^__always_inline +", "", 0), (r"^noinline +", "", 0), (r"^__FORTIFY_INLINE +", "", 0), + (r"QEMU_[A-Z_]+ +", "", 0), (r"__init +", "", 0), (r"__init_or_module +", "", 0), (r"__deprecated +", "", 0), From 9cbe72b868b7cb04f7553220738f1286b6f0dc32 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 14 Aug 2025 18:13:20 +0100 Subject: [PATCH 0247/2396] scripts/kernel-doc: tweak for QEMU coding standards This commit makes the equivalent changes to the Python script that we had for the old Perl script in commit 4cf41794411f ("docs: tweak kernel-doc for QEMU coding standards"). To repeat the rationale from that commit: Surprisingly, QEMU does have a pretty consistent doc comment style and it is not very different from the Linux kernel's. Of the documentation "sigils", only "#" separates the QEMU doc comment style from Linux's, and it has 200+ instances vs. 6 for the kernel's '&struct foo' (all in accel/tcg/translate-all.c), so it's clear that the two standards are different in this respect. In addition, our structs are typedefed and recognized by CamelCase names. Note that in 4cf41794411f we used '(?!)' as our type_fallback regex; this is strictly not quite a replacement for the upstream '\&([_\w]+)', because the latter includes a group that can later be matched with \1, and the former does not. The old perl script did not care about this, but the python version does, so we must include the extra set of brackets to ensure we have a group. This commit does not include all the same changes that 4cf41794411f did. Of the missing pieces, some had already gone in an earlier kernel-doc update; the parts we still had but do not include here are: @@ -2057,7 +2060,7 @@ } elsif (/$doc_decl/o) { $identifier = $1; - if (/\s*([\w\s]+?)(\(\))?\s*-/) { + if (/\s*([\w\s]+?)(\s*-|:)/) { $identifier = $1; } @@ -2067,7 +2070,7 @@ $contents = ""; $section = $section_default; $new_start_line = $. + 1; - if (/-(.*)/) { + if (/[-:](.*)/) { # strip leading/trailing/multiple spaces $descr= $1; $descr =~ s/^\s*//; The second of these is already in the upstream version: the line r = KernRe("[-:](.*)") in process_name() matches the regex we have. The first change has been refactored into the doc_begin_data and doc_begin_func changes. Since the output HTML for QEMU's documentation has no relevant changes with the new kerneldoc, we assume that this too has been handled upstream. Signed-off-by: Peter Maydell Reviewed-by: Paolo Bonzini Reviewed-by: Mauro Carvalho Chehab Message-id: 20250814171324.1614516-6-peter.maydell@linaro.org --- scripts/lib/kdoc/kdoc_output.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/scripts/lib/kdoc/kdoc_output.py b/scripts/lib/kdoc/kdoc_output.py index ea8914537b..39fa872dfc 100644 --- a/scripts/lib/kdoc/kdoc_output.py +++ b/scripts/lib/kdoc/kdoc_output.py @@ -38,12 +38,12 @@ type_fp_param = KernRe(r"\@(\w+)\(\)", cache=False) type_fp_param2 = KernRe(r"\@(\w+->\S+)\(\)", cache=False) type_env = KernRe(r"(\$\w+)", cache=False) -type_enum = KernRe(r"\&(enum\s*([_\w]+))", cache=False) -type_struct = KernRe(r"\&(struct\s*([_\w]+))", cache=False) -type_typedef = KernRe(r"\&(typedef\s*([_\w]+))", cache=False) -type_union = KernRe(r"\&(union\s*([_\w]+))", cache=False) -type_member = KernRe(r"\&([_\w]+)(\.|->)([_\w]+)", cache=False) -type_fallback = KernRe(r"\&([_\w]+)", cache=False) +type_enum = KernRe(r"#(enum\s*([_\w]+))", cache=False) +type_struct = KernRe(r"#(struct\s*([_\w]+))", cache=False) +type_typedef = KernRe(r"#(([A-Z][_\w]*))", cache=False) +type_union = KernRe(r"#(union\s*([_\w]+))", cache=False) +type_member = KernRe(r"#([_\w]+)(\.|->)([_\w]+)", cache=False) +type_fallback = KernRe(r"((?!))", cache=False) # this never matches type_member_func = type_member + KernRe(r"\(\)", cache=False) From 33be8171e2fe02173d24ffd61bd97bf9c2b37834 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 14 Aug 2025 18:13:21 +0100 Subject: [PATCH 0248/2396] scripts/kerneldoc: Switch to the Python kernel-doc script Change the Sphinx config to run the new Python kernel-doc script instead of the Perl one. The only difference between the two is that the new script does not handle the -sphinx-version option, instead assuming that Sphinx is always at least version 3: so we must delete the code that passes that option to avoid the Python script complaining about an unknown option. QEMU's minimum Sphinx version is already 3.4.3, so this doesn't change the set of versions we can handle. Signed-off-by: Peter Maydell Reviewed-by: Paolo Bonzini Reviewed-by: Mauro Carvalho Chehab Message-id: 20250814171324.1614516-7-peter.maydell@linaro.org --- docs/conf.py | 4 +++- docs/sphinx/kerneldoc.py | 5 ----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index f892a6e1da..e09769e5f8 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -341,7 +341,9 @@ man_make_section_directory = False # We use paths starting from qemu_docdir here so that you can run # sphinx-build from anywhere and the kerneldoc extension can still # find everything. -kerneldoc_bin = ['perl', os.path.join(qemu_docdir, '../scripts/kernel-doc')] +# Since kernel-doc is now a Python script, we should run it with whatever +# Python this sphinx is using (rather than letting it find one via env) +kerneldoc_bin = [sys.executable, os.path.join(qemu_docdir, '../scripts/kernel-doc.py')] kerneldoc_srctree = os.path.join(qemu_docdir, '..') hxtool_srctree = os.path.join(qemu_docdir, '..') qapidoc_srctree = os.path.join(qemu_docdir, '..') diff --git a/docs/sphinx/kerneldoc.py b/docs/sphinx/kerneldoc.py index 30bb343198..9721072e47 100644 --- a/docs/sphinx/kerneldoc.py +++ b/docs/sphinx/kerneldoc.py @@ -63,11 +63,6 @@ class KernelDocDirective(Directive): env = self.state.document.settings.env cmd = env.config.kerneldoc_bin + ['-rst', '-enable-lineno'] - # Pass the version string to kernel-doc, as it needs to use a different - # dialect, depending what the C domain supports for each specific - # Sphinx versions - cmd += ['-sphinx-version', sphinx.__version__] - # Pass through the warnings-as-errors flag if env.config.kerneldoc_werror: cmd += ['-Werror'] From 619d5f0211ac69d71291505a8528671ab83764e3 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 14 Aug 2025 18:13:22 +0100 Subject: [PATCH 0249/2396] scripts/kernel-doc: Delete the old Perl kernel-doc script We can now delete the old Perl kernel-doc script. For posterity, this is a complete diff of the local changes that we were carrying between the kernel's Perl script as of kernel commit 72b97d0b911872ba (the last time we synced it) and our local copy: --- /tmp/kdoc 2025-08-14 10:42:47.620331939 +0100 +++ scripts/kernel-doc 2025-02-17 10:44:34.528421457 +0000 @@ -1,5 +1,5 @@ #!/usr/bin/env perl -# SPDX-License-Identifier: GPL-2.0 +# SPDX-License-Identifier: GPL-2.0-only use warnings; use strict; @@ -224,12 +224,12 @@ my $type_fp_param = '\@(\w+)\(\)'; # Special RST handling for func ptr params my $type_fp_param2 = '\@(\w+->\S+)\(\)'; # Special RST handling for structs with func ptr params my $type_env = '(\$\w+)'; -my $type_enum = '\&(enum\s*([_\w]+))'; -my $type_struct = '\&(struct\s*([_\w]+))'; -my $type_typedef = '\&(typedef\s*([_\w]+))'; -my $type_union = '\&(union\s*([_\w]+))'; -my $type_member = '\&([_\w]+)(\.|->)([_\w]+)'; -my $type_fallback = '\&([_\w]+)'; +my $type_enum = '#(enum\s*([_\w]+))'; +my $type_struct = '#(struct\s*([_\w]+))'; +my $type_typedef = '#(([A-Z][_\w]*))'; +my $type_union = '#(union\s*([_\w]+))'; +my $type_member = '#([_\w]+)(\.|->)([_\w]+)'; +my $type_fallback = '(?!)'; # this never matches my $type_member_func = $type_member . '\(\)'; # Output conversion substitutions. @@ -1745,6 +1745,9 @@ )+ \)\)\s+//x; + # Strip QEMU specific compiler annotations + $prototype =~ s/QEMU_[A-Z_]+ +//; + # Yes, this truly is vile. We are looking for: # 1. Return type (may be nothing if we're looking at a macro) # 2. Function name @@ -2057,7 +2060,7 @@ } elsif (/$doc_decl/o) { $identifier = $1; - if (/\s*([\w\s]+?)(\(\))?\s*-/) { + if (/\s*([\w\s]+?)(\s*-|:)/) { $identifier = $1; } @@ -2067,7 +2070,7 @@ $contents = ""; $section = $section_default; $new_start_line = $. + 1; - if (/-(.*)/) { + if (/[-:](.*)/) { # strip leading/trailing/multiple spaces $descr= $1; $descr =~ s/^\s*//; These changes correspond to: 06e2329636f license: Update deprecated SPDX tag GPL-2.0 to GPL-2.0-only (a bulk change which we won't bother to re-apply to this third-party script) b30df2751e5 scripts/kernel-doc: strip QEMU_ from function definitions 4cf41794411 docs: tweak kernel-doc for QEMU coding standards We have already applied the equivalent of these changes to the Python code in libs/kdoc/ in the preceding commits. Signed-off-by: Peter Maydell Reviewed-by: Mauro Carvalho Chehab Reviewed-by: Paolo Bonzini --- .editorconfig | 2 +- scripts/kernel-doc | 2441 -------------------------------------------- 2 files changed, 1 insertion(+), 2442 deletions(-) delete mode 100755 scripts/kernel-doc diff --git a/.editorconfig b/.editorconfig index a04cb9054c..258d41ab48 100644 --- a/.editorconfig +++ b/.editorconfig @@ -55,7 +55,7 @@ indent_size = 4 emacs_mode = perl # but user kernel "style" for imported scripts -[scripts/{kernel-doc,get_maintainer.pl,checkpatch.pl}] +[scripts/{get_maintainer.pl,checkpatch.pl}] indent_style = tab indent_size = 8 emacs_mode = perl diff --git a/scripts/kernel-doc b/scripts/kernel-doc deleted file mode 100755 index 117ec8fcd1..0000000000 --- a/scripts/kernel-doc +++ /dev/null @@ -1,2441 +0,0 @@ -#!/usr/bin/env perl -# SPDX-License-Identifier: GPL-2.0-only - -use warnings; -use strict; - -## Copyright (c) 1998 Michael Zucchi, All Rights Reserved ## -## Copyright (C) 2000, 1 Tim Waugh ## -## Copyright (C) 2001 Simon Huggins ## -## Copyright (C) 2005-2012 Randy Dunlap ## -## Copyright (C) 2012 Dan Luedtke ## -## ## -## #define enhancements by Armin Kuster ## -## Copyright (c) 2000 MontaVista Software, Inc. ## -## ## -## This software falls under the GNU General Public License. ## -## Please read the COPYING file for more information ## - -# 18/01/2001 - Cleanups -# Functions prototyped as foo(void) same as foo() -# Stop eval'ing where we don't need to. -# -- huggie@earth.li - -# 27/06/2001 - Allowed whitespace after initial "/**" and -# allowed comments before function declarations. -# -- Christian Kreibich - -# Still to do: -# - add perldoc documentation -# - Look more closely at some of the scarier bits :) - -# 26/05/2001 - Support for separate source and object trees. -# Return error code. -# Keith Owens - -# 23/09/2001 - Added support for typedefs, structs, enums and unions -# Support for Context section; can be terminated using empty line -# Small fixes (like spaces vs. \s in regex) -# -- Tim Jansen - -# 25/07/2012 - Added support for HTML5 -# -- Dan Luedtke - -sub usage { - my $message = <<"EOF"; -Usage: $0 [OPTION ...] FILE ... - -Read C language source or header FILEs, extract embedded documentation comments, -and print formatted documentation to standard output. - -The documentation comments are identified by "/**" opening comment mark. See -Documentation/doc-guide/kernel-doc.rst for the documentation comment syntax. - -Output format selection (mutually exclusive): - -man Output troff manual page format. This is the default. - -rst Output reStructuredText format. - -none Do not output documentation, only warnings. - -Output format selection modifier (affects only ReST output): - - -sphinx-version Use the ReST C domain dialect compatible with an - specific Sphinx Version. - If not specified, kernel-doc will auto-detect using - the sphinx-build version found on PATH. - -Output selection (mutually exclusive): - -export Only output documentation for symbols that have been - exported using EXPORT_SYMBOL() or EXPORT_SYMBOL_GPL() - in any input FILE or -export-file FILE. - -internal Only output documentation for symbols that have NOT been - exported using EXPORT_SYMBOL() or EXPORT_SYMBOL_GPL() - in any input FILE or -export-file FILE. - -function NAME Only output documentation for the given function(s) - or DOC: section title(s). All other functions and DOC: - sections are ignored. May be specified multiple times. - -nosymbol NAME Exclude the specified symbols from the output - documentation. May be specified multiple times. - -Output selection modifiers: - -no-doc-sections Do not output DOC: sections. - -enable-lineno Enable output of #define LINENO lines. Only works with - reStructuredText format. - -export-file FILE Specify an additional FILE in which to look for - EXPORT_SYMBOL() and EXPORT_SYMBOL_GPL(). To be used with - -export or -internal. May be specified multiple times. - -Other parameters: - -v Verbose output, more warnings and other information. - -h Print this help. - -Werror Treat warnings as errors. - -EOF - print $message; - exit 1; -} - -# -# format of comments. -# In the following table, (...)? signifies optional structure. -# (...)* signifies 0 or more structure elements -# /** -# * function_name(:)? (- short description)? -# (* @parameterx: (description of parameter x)?)* -# (* a blank line)? -# * (Description:)? (Description of function)? -# * (section header: (section description)? )* -# (*)?*/ -# -# So .. the trivial example would be: -# -# /** -# * my_function -# */ -# -# If the Description: header tag is omitted, then there must be a blank line -# after the last parameter specification. -# e.g. -# /** -# * my_function - does my stuff -# * @my_arg: its mine damnit -# * -# * Does my stuff explained. -# */ -# -# or, could also use: -# /** -# * my_function - does my stuff -# * @my_arg: its mine damnit -# * Description: Does my stuff explained. -# */ -# etc. -# -# Besides functions you can also write documentation for structs, unions, -# enums and typedefs. Instead of the function name you must write the name -# of the declaration; the struct/union/enum/typedef must always precede -# the name. Nesting of declarations is not supported. -# Use the argument mechanism to document members or constants. -# e.g. -# /** -# * struct my_struct - short description -# * @a: first member -# * @b: second member -# * -# * Longer description -# */ -# struct my_struct { -# int a; -# int b; -# /* private: */ -# int c; -# }; -# -# All descriptions can be multiline, except the short function description. -# -# For really longs structs, you can also describe arguments inside the -# body of the struct. -# eg. -# /** -# * struct my_struct - short description -# * @a: first member -# * @b: second member -# * -# * Longer description -# */ -# struct my_struct { -# int a; -# int b; -# /** -# * @c: This is longer description of C -# * -# * You can use paragraphs to describe arguments -# * using this method. -# */ -# int c; -# }; -# -# This should be use only for struct/enum members. -# -# You can also add additional sections. When documenting kernel functions you -# should document the "Context:" of the function, e.g. whether the functions -# can be called form interrupts. Unlike other sections you can end it with an -# empty line. -# A non-void function should have a "Return:" section describing the return -# value(s). -# Example-sections should contain the string EXAMPLE so that they are marked -# appropriately in DocBook. -# -# Example: -# /** -# * user_function - function that can only be called in user context -# * @a: some argument -# * Context: !in_interrupt() -# * -# * Some description -# * Example: -# * user_function(22); -# */ -# ... -# -# -# All descriptive text is further processed, scanning for the following special -# patterns, which are highlighted appropriately. -# -# 'funcname()' - function -# '$ENVVAR' - environmental variable -# '&struct_name' - name of a structure (up to two words including 'struct') -# '&struct_name.member' - name of a structure member -# '@parameter' - name of a parameter -# '%CONST' - name of a constant. -# '``LITERAL``' - literal string without any spaces on it. - -## init lots of data - -my $errors = 0; -my $warnings = 0; -my $anon_struct_union = 0; - -# match expressions used to find embedded type information -my $type_constant = '\b``([^\`]+)``\b'; -my $type_constant2 = '\%([-_\w]+)'; -my $type_func = '(\w+)\(\)'; -my $type_param = '\@(\w*((\.\w+)|(->\w+))*(\.\.\.)?)'; -my $type_param_ref = '([\!]?)\@(\w*((\.\w+)|(->\w+))*(\.\.\.)?)'; -my $type_fp_param = '\@(\w+)\(\)'; # Special RST handling for func ptr params -my $type_fp_param2 = '\@(\w+->\S+)\(\)'; # Special RST handling for structs with func ptr params -my $type_env = '(\$\w+)'; -my $type_enum = '#(enum\s*([_\w]+))'; -my $type_struct = '#(struct\s*([_\w]+))'; -my $type_typedef = '#(([A-Z][_\w]*))'; -my $type_union = '#(union\s*([_\w]+))'; -my $type_member = '#([_\w]+)(\.|->)([_\w]+)'; -my $type_fallback = '(?!)'; # this never matches -my $type_member_func = $type_member . '\(\)'; - -# Output conversion substitutions. -# One for each output format - -# these are pretty rough -my @highlights_man = ( - [$type_constant, "\$1"], - [$type_constant2, "\$1"], - [$type_func, "\\\\fB\$1\\\\fP"], - [$type_enum, "\\\\fI\$1\\\\fP"], - [$type_struct, "\\\\fI\$1\\\\fP"], - [$type_typedef, "\\\\fI\$1\\\\fP"], - [$type_union, "\\\\fI\$1\\\\fP"], - [$type_param, "\\\\fI\$1\\\\fP"], - [$type_param_ref, "\\\\fI\$1\$2\\\\fP"], - [$type_member, "\\\\fI\$1\$2\$3\\\\fP"], - [$type_fallback, "\\\\fI\$1\\\\fP"] - ); -my $blankline_man = ""; - -# rst-mode -my @highlights_rst = ( - [$type_constant, "``\$1``"], - [$type_constant2, "``\$1``"], - # Note: need to escape () to avoid func matching later - [$type_member_func, "\\:c\\:type\\:`\$1\$2\$3\\\\(\\\\) <\$1>`"], - [$type_member, "\\:c\\:type\\:`\$1\$2\$3 <\$1>`"], - [$type_fp_param, "**\$1\\\\(\\\\)**"], - [$type_fp_param2, "**\$1\\\\(\\\\)**"], - [$type_func, "\$1()"], - [$type_enum, "\\:c\\:type\\:`\$1 <\$2>`"], - [$type_struct, "\\:c\\:type\\:`\$1 <\$2>`"], - [$type_typedef, "\\:c\\:type\\:`\$1 <\$2>`"], - [$type_union, "\\:c\\:type\\:`\$1 <\$2>`"], - # in rst this can refer to any type - [$type_fallback, "\\:c\\:type\\:`\$1`"], - [$type_param_ref, "**\$1\$2**"] - ); -my $blankline_rst = "\n"; - -# read arguments -if ($#ARGV == -1) { - usage(); -} - -my $kernelversion; -my ($sphinx_major, $sphinx_minor, $sphinx_patch); - -my $dohighlight = ""; - -my $verbose = 0; -my $Werror = 0; -my $output_mode = "rst"; -my $output_preformatted = 0; -my $no_doc_sections = 0; -my $enable_lineno = 0; -my @highlights = @highlights_rst; -my $blankline = $blankline_rst; -my $modulename = "Kernel API"; - -use constant { - OUTPUT_ALL => 0, # output all symbols and doc sections - OUTPUT_INCLUDE => 1, # output only specified symbols - OUTPUT_EXPORTED => 2, # output exported symbols - OUTPUT_INTERNAL => 3, # output non-exported symbols -}; -my $output_selection = OUTPUT_ALL; -my $show_not_found = 0; # No longer used - -my @export_file_list; - -my @build_time; -if (defined($ENV{'KBUILD_BUILD_TIMESTAMP'}) && - (my $seconds = `date -d"${ENV{'KBUILD_BUILD_TIMESTAMP'}}" +%s`) ne '') { - @build_time = gmtime($seconds); -} else { - @build_time = localtime; -} - -my $man_date = ('January', 'February', 'March', 'April', 'May', 'June', - 'July', 'August', 'September', 'October', - 'November', 'December')[$build_time[4]] . - " " . ($build_time[5]+1900); - -# Essentially these are globals. -# They probably want to be tidied up, made more localised or something. -# CAVEAT EMPTOR! Some of the others I localised may not want to be, which -# could cause "use of undefined value" or other bugs. -my ($function, %function_table, %parametertypes, $declaration_purpose); -my %nosymbol_table = (); -my $declaration_start_line; -my ($type, $declaration_name, $return_type); -my ($newsection, $newcontents, $prototype, $brcount, %source_map); - -if (defined($ENV{'KBUILD_VERBOSE'})) { - $verbose = "$ENV{'KBUILD_VERBOSE'}"; -} - -if (defined($ENV{'KDOC_WERROR'})) { - $Werror = "$ENV{'KDOC_WERROR'}"; -} - -if (defined($ENV{'KCFLAGS'})) { - my $kcflags = "$ENV{'KCFLAGS'}"; - - if ($kcflags =~ /Werror/) { - $Werror = 1; - } -} - -# Generated docbook code is inserted in a template at a point where -# docbook v3.1 requires a non-zero sequence of RefEntry's; see: -# https://www.oasis-open.org/docbook/documentation/reference/html/refentry.html -# We keep track of number of generated entries and generate a dummy -# if needs be to ensure the expanded template can be postprocessed -# into html. -my $section_counter = 0; - -my $lineprefix=""; - -# Parser states -use constant { - STATE_NORMAL => 0, # normal code - STATE_NAME => 1, # looking for function name - STATE_BODY_MAYBE => 2, # body - or maybe more description - STATE_BODY => 3, # the body of the comment - STATE_BODY_WITH_BLANK_LINE => 4, # the body, which has a blank line - STATE_PROTO => 5, # scanning prototype - STATE_DOCBLOCK => 6, # documentation block - STATE_INLINE => 7, # gathering doc outside main block -}; -my $state; -my $in_doc_sect; -my $leading_space; - -# Inline documentation state -use constant { - STATE_INLINE_NA => 0, # not applicable ($state != STATE_INLINE) - STATE_INLINE_NAME => 1, # looking for member name (@foo:) - STATE_INLINE_TEXT => 2, # looking for member documentation - STATE_INLINE_END => 3, # done - STATE_INLINE_ERROR => 4, # error - Comment without header was found. - # Spit a warning as it's not - # proper kernel-doc and ignore the rest. -}; -my $inline_doc_state; - -#declaration types: can be -# 'function', 'struct', 'union', 'enum', 'typedef' -my $decl_type; - -my $doc_start = '^/\*\*\s*$'; # Allow whitespace at end of comment start. -my $doc_end = '\*/'; -my $doc_com = '\s*\*\s*'; -my $doc_com_body = '\s*\* ?'; -my $doc_decl = $doc_com . '(\w+)'; -# @params and a strictly limited set of supported section names -my $doc_sect = $doc_com . - '\s*(\@[.\w]+|\@\.\.\.|description|context|returns?|notes?|examples?)\s*:(.*)'; -my $doc_content = $doc_com_body . '(.*)'; -my $doc_block = $doc_com . 'DOC:\s*(.*)?'; -my $doc_inline_start = '^\s*/\*\*\s*$'; -my $doc_inline_sect = '\s*\*\s*(@\s*[\w][\w\.]*\s*):(.*)'; -my $doc_inline_end = '^\s*\*/\s*$'; -my $doc_inline_oneline = '^\s*/\*\*\s*(@[\w\s]+):\s*(.*)\s*\*/\s*$'; -my $export_symbol = '^\s*EXPORT_SYMBOL(_GPL)?\s*\(\s*(\w+)\s*\)\s*;'; - -my %parameterdescs; -my %parameterdesc_start_lines; -my @parameterlist; -my %sections; -my @sectionlist; -my %section_start_lines; -my $sectcheck; -my $struct_actual; - -my $contents = ""; -my $new_start_line = 0; - -# the canonical section names. see also $doc_sect above. -my $section_default = "Description"; # default section -my $section_intro = "Introduction"; -my $section = $section_default; -my $section_context = "Context"; -my $section_return = "Return"; - -my $undescribed = "-- undescribed --"; - -reset_state(); - -while ($ARGV[0] =~ m/^--?(.*)/) { - my $cmd = $1; - shift @ARGV; - if ($cmd eq "man") { - $output_mode = "man"; - @highlights = @highlights_man; - $blankline = $blankline_man; - } elsif ($cmd eq "rst") { - $output_mode = "rst"; - @highlights = @highlights_rst; - $blankline = $blankline_rst; - } elsif ($cmd eq "none") { - $output_mode = "none"; - } elsif ($cmd eq "module") { # not needed for XML, inherits from calling document - $modulename = shift @ARGV; - } elsif ($cmd eq "function") { # to only output specific functions - $output_selection = OUTPUT_INCLUDE; - $function = shift @ARGV; - $function_table{$function} = 1; - } elsif ($cmd eq "nosymbol") { # Exclude specific symbols - my $symbol = shift @ARGV; - $nosymbol_table{$symbol} = 1; - } elsif ($cmd eq "export") { # only exported symbols - $output_selection = OUTPUT_EXPORTED; - %function_table = (); - } elsif ($cmd eq "internal") { # only non-exported symbols - $output_selection = OUTPUT_INTERNAL; - %function_table = (); - } elsif ($cmd eq "export-file") { - my $file = shift @ARGV; - push(@export_file_list, $file); - } elsif ($cmd eq "v") { - $verbose = 1; - } elsif ($cmd eq "Werror") { - $Werror = 1; - } elsif (($cmd eq "h") || ($cmd eq "help")) { - usage(); - } elsif ($cmd eq 'no-doc-sections') { - $no_doc_sections = 1; - } elsif ($cmd eq 'enable-lineno') { - $enable_lineno = 1; - } elsif ($cmd eq 'show-not-found') { - $show_not_found = 1; # A no-op but don't fail - } elsif ($cmd eq "sphinx-version") { - my $ver_string = shift @ARGV; - if ($ver_string =~ m/^(\d+)(\.\d+)?(\.\d+)?/) { - $sphinx_major = $1; - if (defined($2)) { - $sphinx_minor = substr($2,1); - } else { - $sphinx_minor = 0; - } - if (defined($3)) { - $sphinx_patch = substr($3,1) - } else { - $sphinx_patch = 0; - } - } else { - die "Sphinx version should either major.minor or major.minor.patch format\n"; - } - } else { - # Unknown argument - usage(); - } -} - -# continue execution near EOF; - -# The C domain dialect changed on Sphinx 3. So, we need to check the -# version in order to produce the right tags. -sub findprog($) -{ - foreach(split(/:/, $ENV{PATH})) { - return "$_/$_[0]" if(-x "$_/$_[0]"); - } -} - -sub get_sphinx_version() -{ - my $ver; - - my $cmd = "sphinx-build"; - if (!findprog($cmd)) { - my $cmd = "sphinx-build3"; - if (!findprog($cmd)) { - $sphinx_major = 1; - $sphinx_minor = 2; - $sphinx_patch = 0; - printf STDERR "Warning: Sphinx version not found. Using default (Sphinx version %d.%d.%d)\n", - $sphinx_major, $sphinx_minor, $sphinx_patch; - return; - } - } - - open IN, "$cmd --version 2>&1 |"; - while () { - if (m/^\s*sphinx-build\s+([\d]+)\.([\d\.]+)(\+\/[\da-f]+)?$/) { - $sphinx_major = $1; - $sphinx_minor = $2; - $sphinx_patch = $3; - last; - } - # Sphinx 1.2.x uses a different format - if (m/^\s*Sphinx.*\s+([\d]+)\.([\d\.]+)$/) { - $sphinx_major = $1; - $sphinx_minor = $2; - $sphinx_patch = $3; - last; - } - } - close IN; -} - -# get kernel version from env -sub get_kernel_version() { - my $version = 'unknown kernel version'; - - if (defined($ENV{'KERNELVERSION'})) { - $version = $ENV{'KERNELVERSION'}; - } - return $version; -} - -# -sub print_lineno { - my $lineno = shift; - if ($enable_lineno && defined($lineno)) { - print "#define LINENO " . $lineno . "\n"; - } -} -## -# dumps section contents to arrays/hashes intended for that purpose. -# -sub dump_section { - my $file = shift; - my $name = shift; - my $contents = join "\n", @_; - - if ($name =~ m/$type_param/) { - $name = $1; - $parameterdescs{$name} = $contents; - $sectcheck = $sectcheck . $name . " "; - $parameterdesc_start_lines{$name} = $new_start_line; - $new_start_line = 0; - } elsif ($name eq "@\.\.\.") { - $name = "..."; - $parameterdescs{$name} = $contents; - $sectcheck = $sectcheck . $name . " "; - $parameterdesc_start_lines{$name} = $new_start_line; - $new_start_line = 0; - } else { - if (defined($sections{$name}) && ($sections{$name} ne "")) { - # Only warn on user specified duplicate section names. - if ($name ne $section_default) { - print STDERR "${file}:$.: warning: duplicate section name '$name'\n"; - ++$warnings; - } - $sections{$name} .= $contents; - } else { - $sections{$name} = $contents; - push @sectionlist, $name; - $section_start_lines{$name} = $new_start_line; - $new_start_line = 0; - } - } -} - -## -# dump DOC: section after checking that it should go out -# -sub dump_doc_section { - my $file = shift; - my $name = shift; - my $contents = join "\n", @_; - - if ($no_doc_sections) { - return; - } - - return if (defined($nosymbol_table{$name})); - - if (($output_selection == OUTPUT_ALL) || - (($output_selection == OUTPUT_INCLUDE) && - defined($function_table{$name}))) - { - dump_section($file, $name, $contents); - output_blockhead({'sectionlist' => \@sectionlist, - 'sections' => \%sections, - 'module' => $modulename, - 'content-only' => ($output_selection != OUTPUT_ALL), }); - } -} - -## -# output function -# -# parameterdescs, a hash. -# function => "function name" -# parameterlist => @list of parameters -# parameterdescs => %parameter descriptions -# sectionlist => @list of sections -# sections => %section descriptions -# - -sub output_highlight { - my $contents = join "\n",@_; - my $line; - -# DEBUG -# if (!defined $contents) { -# use Carp; -# confess "output_highlight got called with no args?\n"; -# } - -# print STDERR "contents b4:$contents\n"; - eval $dohighlight; - die $@ if $@; -# print STDERR "contents af:$contents\n"; - - foreach $line (split "\n", $contents) { - if (! $output_preformatted) { - $line =~ s/^\s*//; - } - if ($line eq ""){ - if (! $output_preformatted) { - print $lineprefix, $blankline; - } - } else { - if ($output_mode eq "man" && substr($line, 0, 1) eq ".") { - print "\\&$line"; - } else { - print $lineprefix, $line; - } - } - print "\n"; - } -} - -## -# output function in man -sub output_function_man(%) { - my %args = %{$_[0]}; - my ($parameter, $section); - my $count; - - print ".TH \"$args{'function'}\" 9 \"$args{'function'}\" \"$man_date\" \"Kernel Hacker's Manual\" LINUX\n"; - - print ".SH NAME\n"; - print $args{'function'} . " \\- " . $args{'purpose'} . "\n"; - - print ".SH SYNOPSIS\n"; - if ($args{'functiontype'} ne "") { - print ".B \"" . $args{'functiontype'} . "\" " . $args{'function'} . "\n"; - } else { - print ".B \"" . $args{'function'} . "\n"; - } - $count = 0; - my $parenth = "("; - my $post = ","; - foreach my $parameter (@{$args{'parameterlist'}}) { - if ($count == $#{$args{'parameterlist'}}) { - $post = ");"; - } - $type = $args{'parametertypes'}{$parameter}; - if ($type =~ m/([^\(]*\(\*)\s*\)\s*\(([^\)]*)\)/) { - # pointer-to-function - print ".BI \"" . $parenth . $1 . "\" " . " \") (" . $2 . ")" . $post . "\"\n"; - } else { - $type =~ s/([^\*])$/$1 /; - print ".BI \"" . $parenth . $type . "\" " . " \"" . $post . "\"\n"; - } - $count++; - $parenth = ""; - } - - print ".SH ARGUMENTS\n"; - foreach $parameter (@{$args{'parameterlist'}}) { - my $parameter_name = $parameter; - $parameter_name =~ s/\[.*//; - - print ".IP \"" . $parameter . "\" 12\n"; - output_highlight($args{'parameterdescs'}{$parameter_name}); - } - foreach $section (@{$args{'sectionlist'}}) { - print ".SH \"", uc $section, "\"\n"; - output_highlight($args{'sections'}{$section}); - } -} - -## -# output enum in man -sub output_enum_man(%) { - my %args = %{$_[0]}; - my ($parameter, $section); - my $count; - - print ".TH \"$args{'module'}\" 9 \"enum $args{'enum'}\" \"$man_date\" \"API Manual\" LINUX\n"; - - print ".SH NAME\n"; - print "enum " . $args{'enum'} . " \\- " . $args{'purpose'} . "\n"; - - print ".SH SYNOPSIS\n"; - print "enum " . $args{'enum'} . " {\n"; - $count = 0; - foreach my $parameter (@{$args{'parameterlist'}}) { - print ".br\n.BI \" $parameter\"\n"; - if ($count == $#{$args{'parameterlist'}}) { - print "\n};\n"; - last; - } - else { - print ", \n.br\n"; - } - $count++; - } - - print ".SH Constants\n"; - foreach $parameter (@{$args{'parameterlist'}}) { - my $parameter_name = $parameter; - $parameter_name =~ s/\[.*//; - - print ".IP \"" . $parameter . "\" 12\n"; - output_highlight($args{'parameterdescs'}{$parameter_name}); - } - foreach $section (@{$args{'sectionlist'}}) { - print ".SH \"$section\"\n"; - output_highlight($args{'sections'}{$section}); - } -} - -## -# output struct in man -sub output_struct_man(%) { - my %args = %{$_[0]}; - my ($parameter, $section); - - print ".TH \"$args{'module'}\" 9 \"" . $args{'type'} . " " . $args{'struct'} . "\" \"$man_date\" \"API Manual\" LINUX\n"; - - print ".SH NAME\n"; - print $args{'type'} . " " . $args{'struct'} . " \\- " . $args{'purpose'} . "\n"; - - my $declaration = $args{'definition'}; - $declaration =~ s/\t/ /g; - $declaration =~ s/\n/"\n.br\n.BI \"/g; - print ".SH SYNOPSIS\n"; - print $args{'type'} . " " . $args{'struct'} . " {\n.br\n"; - print ".BI \"$declaration\n};\n.br\n\n"; - - print ".SH Members\n"; - foreach $parameter (@{$args{'parameterlist'}}) { - ($parameter =~ /^#/) && next; - - my $parameter_name = $parameter; - $parameter_name =~ s/\[.*//; - - ($args{'parameterdescs'}{$parameter_name} ne $undescribed) || next; - print ".IP \"" . $parameter . "\" 12\n"; - output_highlight($args{'parameterdescs'}{$parameter_name}); - } - foreach $section (@{$args{'sectionlist'}}) { - print ".SH \"$section\"\n"; - output_highlight($args{'sections'}{$section}); - } -} - -## -# output typedef in man -sub output_typedef_man(%) { - my %args = %{$_[0]}; - my ($parameter, $section); - - print ".TH \"$args{'module'}\" 9 \"$args{'typedef'}\" \"$man_date\" \"API Manual\" LINUX\n"; - - print ".SH NAME\n"; - print "typedef " . $args{'typedef'} . " \\- " . $args{'purpose'} . "\n"; - - foreach $section (@{$args{'sectionlist'}}) { - print ".SH \"$section\"\n"; - output_highlight($args{'sections'}{$section}); - } -} - -sub output_blockhead_man(%) { - my %args = %{$_[0]}; - my ($parameter, $section); - my $count; - - print ".TH \"$args{'module'}\" 9 \"$args{'module'}\" \"$man_date\" \"API Manual\" LINUX\n"; - - foreach $section (@{$args{'sectionlist'}}) { - print ".SH \"$section\"\n"; - output_highlight($args{'sections'}{$section}); - } -} - -## -# output in restructured text -# - -# -# This could use some work; it's used to output the DOC: sections, and -# starts by putting out the name of the doc section itself, but that tends -# to duplicate a header already in the template file. -# -sub output_blockhead_rst(%) { - my %args = %{$_[0]}; - my ($parameter, $section); - - foreach $section (@{$args{'sectionlist'}}) { - next if (defined($nosymbol_table{$section})); - - if ($output_selection != OUTPUT_INCLUDE) { - print "**$section**\n\n"; - } - print_lineno($section_start_lines{$section}); - output_highlight_rst($args{'sections'}{$section}); - print "\n"; - } -} - -# -# Apply the RST highlights to a sub-block of text. -# -sub highlight_block($) { - # The dohighlight kludge requires the text be called $contents - my $contents = shift; - eval $dohighlight; - die $@ if $@; - return $contents; -} - -# -# Regexes used only here. -# -my $sphinx_literal = '^[^.].*::$'; -my $sphinx_cblock = '^\.\.\ +code-block::'; - -sub output_highlight_rst { - my $input = join "\n",@_; - my $output = ""; - my $line; - my $in_literal = 0; - my $litprefix; - my $block = ""; - - foreach $line (split "\n",$input) { - # - # If we're in a literal block, see if we should drop out - # of it. Otherwise pass the line straight through unmunged. - # - if ($in_literal) { - if (! ($line =~ /^\s*$/)) { - # - # If this is the first non-blank line in a literal - # block we need to figure out what the proper indent is. - # - if ($litprefix eq "") { - $line =~ /^(\s*)/; - $litprefix = '^' . $1; - $output .= $line . "\n"; - } elsif (! ($line =~ /$litprefix/)) { - $in_literal = 0; - } else { - $output .= $line . "\n"; - } - } else { - $output .= $line . "\n"; - } - } - # - # Not in a literal block (or just dropped out) - # - if (! $in_literal) { - $block .= $line . "\n"; - if (($line =~ /$sphinx_literal/) || ($line =~ /$sphinx_cblock/)) { - $in_literal = 1; - $litprefix = ""; - $output .= highlight_block($block); - $block = "" - } - } - } - - if ($block) { - $output .= highlight_block($block); - } - foreach $line (split "\n", $output) { - print $lineprefix . $line . "\n"; - } -} - -sub output_function_rst(%) { - my %args = %{$_[0]}; - my ($parameter, $section); - my $oldprefix = $lineprefix; - my $start = ""; - my $is_macro = 0; - - if ($sphinx_major < 3) { - if ($args{'typedef'}) { - print ".. c:type:: ". $args{'function'} . "\n\n"; - print_lineno($declaration_start_line); - print " **Typedef**: "; - $lineprefix = ""; - output_highlight_rst($args{'purpose'}); - $start = "\n\n**Syntax**\n\n ``"; - $is_macro = 1; - } else { - print ".. c:function:: "; - } - } else { - if ($args{'typedef'} || $args{'functiontype'} eq "") { - $is_macro = 1; - print ".. c:macro:: ". $args{'function'} . "\n\n"; - } else { - print ".. c:function:: "; - } - - if ($args{'typedef'}) { - print_lineno($declaration_start_line); - print " **Typedef**: "; - $lineprefix = ""; - output_highlight_rst($args{'purpose'}); - $start = "\n\n**Syntax**\n\n ``"; - } else { - print "``" if ($is_macro); - } - } - if ($args{'functiontype'} ne "") { - $start .= $args{'functiontype'} . " " . $args{'function'} . " ("; - } else { - $start .= $args{'function'} . " ("; - } - print $start; - - my $count = 0; - foreach my $parameter (@{$args{'parameterlist'}}) { - if ($count ne 0) { - print ", "; - } - $count++; - $type = $args{'parametertypes'}{$parameter}; - - if ($type =~ m/([^\(]*\(\*)\s*\)\s*\(([^\)]*)\)/) { - # pointer-to-function - print $1 . $parameter . ") (" . $2 . ")"; - } else { - print $type; - } - } - if ($is_macro) { - print ")``\n\n"; - } else { - print ")\n\n"; - } - if (!$args{'typedef'}) { - print_lineno($declaration_start_line); - $lineprefix = " "; - output_highlight_rst($args{'purpose'}); - print "\n"; - } - - print "**Parameters**\n\n"; - $lineprefix = " "; - foreach $parameter (@{$args{'parameterlist'}}) { - my $parameter_name = $parameter; - $parameter_name =~ s/\[.*//; - $type = $args{'parametertypes'}{$parameter}; - - if ($type ne "") { - print "``$type``\n"; - } else { - print "``$parameter``\n"; - } - - print_lineno($parameterdesc_start_lines{$parameter_name}); - - if (defined($args{'parameterdescs'}{$parameter_name}) && - $args{'parameterdescs'}{$parameter_name} ne $undescribed) { - output_highlight_rst($args{'parameterdescs'}{$parameter_name}); - } else { - print " *undescribed*\n"; - } - print "\n"; - } - - $lineprefix = $oldprefix; - output_section_rst(@_); -} - -sub output_section_rst(%) { - my %args = %{$_[0]}; - my $section; - my $oldprefix = $lineprefix; - $lineprefix = ""; - - foreach $section (@{$args{'sectionlist'}}) { - print "**$section**\n\n"; - print_lineno($section_start_lines{$section}); - output_highlight_rst($args{'sections'}{$section}); - print "\n"; - } - print "\n"; - $lineprefix = $oldprefix; -} - -sub output_enum_rst(%) { - my %args = %{$_[0]}; - my ($parameter); - my $oldprefix = $lineprefix; - my $count; - - if ($sphinx_major < 3) { - my $name = "enum " . $args{'enum'}; - print "\n\n.. c:type:: " . $name . "\n\n"; - } else { - my $name = $args{'enum'}; - print "\n\n.. c:enum:: " . $name . "\n\n"; - } - print_lineno($declaration_start_line); - $lineprefix = " "; - output_highlight_rst($args{'purpose'}); - print "\n"; - - print "**Constants**\n\n"; - $lineprefix = " "; - foreach $parameter (@{$args{'parameterlist'}}) { - print "``$parameter``\n"; - if ($args{'parameterdescs'}{$parameter} ne $undescribed) { - output_highlight_rst($args{'parameterdescs'}{$parameter}); - } else { - print " *undescribed*\n"; - } - print "\n"; - } - - $lineprefix = $oldprefix; - output_section_rst(@_); -} - -sub output_typedef_rst(%) { - my %args = %{$_[0]}; - my ($parameter); - my $oldprefix = $lineprefix; - my $name; - - if ($sphinx_major < 3) { - $name = "typedef " . $args{'typedef'}; - } else { - $name = $args{'typedef'}; - } - print "\n\n.. c:type:: " . $name . "\n\n"; - print_lineno($declaration_start_line); - $lineprefix = " "; - output_highlight_rst($args{'purpose'}); - print "\n"; - - $lineprefix = $oldprefix; - output_section_rst(@_); -} - -sub output_struct_rst(%) { - my %args = %{$_[0]}; - my ($parameter); - my $oldprefix = $lineprefix; - - if ($sphinx_major < 3) { - my $name = $args{'type'} . " " . $args{'struct'}; - print "\n\n.. c:type:: " . $name . "\n\n"; - } else { - my $name = $args{'struct'}; - if ($args{'type'} eq 'union') { - print "\n\n.. c:union:: " . $name . "\n\n"; - } else { - print "\n\n.. c:struct:: " . $name . "\n\n"; - } - } - print_lineno($declaration_start_line); - $lineprefix = " "; - output_highlight_rst($args{'purpose'}); - print "\n"; - - print "**Definition**\n\n"; - print "::\n\n"; - my $declaration = $args{'definition'}; - $declaration =~ s/\t/ /g; - print " " . $args{'type'} . " " . $args{'struct'} . " {\n$declaration };\n\n"; - - print "**Members**\n\n"; - $lineprefix = " "; - foreach $parameter (@{$args{'parameterlist'}}) { - ($parameter =~ /^#/) && next; - - my $parameter_name = $parameter; - $parameter_name =~ s/\[.*//; - - ($args{'parameterdescs'}{$parameter_name} ne $undescribed) || next; - $type = $args{'parametertypes'}{$parameter}; - print_lineno($parameterdesc_start_lines{$parameter_name}); - print "``" . $parameter . "``\n"; - output_highlight_rst($args{'parameterdescs'}{$parameter_name}); - print "\n"; - } - print "\n"; - - $lineprefix = $oldprefix; - output_section_rst(@_); -} - -## none mode output functions - -sub output_function_none(%) { -} - -sub output_enum_none(%) { -} - -sub output_typedef_none(%) { -} - -sub output_struct_none(%) { -} - -sub output_blockhead_none(%) { -} - -## -# generic output function for all types (function, struct/union, typedef, enum); -# calls the generated, variable output_ function name based on -# functype and output_mode -sub output_declaration { - no strict 'refs'; - my $name = shift; - my $functype = shift; - my $func = "output_${functype}_$output_mode"; - - return if (defined($nosymbol_table{$name})); - - if (($output_selection == OUTPUT_ALL) || - (($output_selection == OUTPUT_INCLUDE || - $output_selection == OUTPUT_EXPORTED) && - defined($function_table{$name})) || - ($output_selection == OUTPUT_INTERNAL && - !($functype eq "function" && defined($function_table{$name})))) - { - &$func(@_); - $section_counter++; - } -} - -## -# generic output function - calls the right one based on current output mode. -sub output_blockhead { - no strict 'refs'; - my $func = "output_blockhead_" . $output_mode; - &$func(@_); - $section_counter++; -} - -## -# takes a declaration (struct, union, enum, typedef) and -# invokes the right handler. NOT called for functions. -sub dump_declaration($$) { - no strict 'refs'; - my ($prototype, $file) = @_; - my $func = "dump_" . $decl_type; - &$func(@_); -} - -sub dump_union($$) { - dump_struct(@_); -} - -sub dump_struct($$) { - my $x = shift; - my $file = shift; - - if ($x =~ /(struct|union)\s+(\w+)\s*\{(.*)\}(\s*(__packed|__aligned|____cacheline_aligned_in_smp|____cacheline_aligned|__attribute__\s*\(\([a-z0-9,_\s\(\)]*\)\)))*/) { - my $decl_type = $1; - $declaration_name = $2; - my $members = $3; - - # ignore members marked private: - $members =~ s/\/\*\s*private:.*?\/\*\s*public:.*?\*\///gosi; - $members =~ s/\/\*\s*private:.*//gosi; - # strip comments: - $members =~ s/\/\*.*?\*\///gos; - # strip attributes - $members =~ s/\s*__attribute__\s*\(\([a-z0-9,_\*\s\(\)]*\)\)/ /gi; - $members =~ s/\s*__aligned\s*\([^;]*\)/ /gos; - $members =~ s/\s*__packed\s*/ /gos; - $members =~ s/\s*CRYPTO_MINALIGN_ATTR/ /gos; - $members =~ s/\s*____cacheline_aligned_in_smp/ /gos; - $members =~ s/\s*____cacheline_aligned/ /gos; - - # replace DECLARE_BITMAP - $members =~ s/__ETHTOOL_DECLARE_LINK_MODE_MASK\s*\(([^\)]+)\)/DECLARE_BITMAP($1, __ETHTOOL_LINK_MODE_MASK_NBITS)/gos; - $members =~ s/DECLARE_BITMAP\s*\(([^,)]+),\s*([^,)]+)\)/unsigned long $1\[BITS_TO_LONGS($2)\]/gos; - # replace DECLARE_HASHTABLE - $members =~ s/DECLARE_HASHTABLE\s*\(([^,)]+),\s*([^,)]+)\)/unsigned long $1\[1 << (($2) - 1)\]/gos; - # replace DECLARE_KFIFO - $members =~ s/DECLARE_KFIFO\s*\(([^,)]+),\s*([^,)]+),\s*([^,)]+)\)/$2 \*$1/gos; - # replace DECLARE_KFIFO_PTR - $members =~ s/DECLARE_KFIFO_PTR\s*\(([^,)]+),\s*([^,)]+)\)/$2 \*$1/gos; - - my $declaration = $members; - - # Split nested struct/union elements as newer ones - while ($members =~ m/(struct|union)([^\{\};]+)\{([^\{\}]*)\}([^\{\}\;]*)\;/) { - my $newmember; - my $maintype = $1; - my $ids = $4; - my $content = $3; - foreach my $id(split /,/, $ids) { - $newmember .= "$maintype $id; "; - - $id =~ s/[:\[].*//; - $id =~ s/^\s*\**(\S+)\s*/$1/; - foreach my $arg (split /;/, $content) { - next if ($arg =~ m/^\s*$/); - if ($arg =~ m/^([^\(]+\(\*?\s*)([\w\.]*)(\s*\).*)/) { - # pointer-to-function - my $type = $1; - my $name = $2; - my $extra = $3; - next if (!$name); - if ($id =~ m/^\s*$/) { - # anonymous struct/union - $newmember .= "$type$name$extra; "; - } else { - $newmember .= "$type$id.$name$extra; "; - } - } else { - my $type; - my $names; - $arg =~ s/^\s+//; - $arg =~ s/\s+$//; - # Handle bitmaps - $arg =~ s/:\s*\d+\s*//g; - # Handle arrays - $arg =~ s/\[.*\]//g; - # The type may have multiple words, - # and multiple IDs can be defined, like: - # const struct foo, *bar, foobar - # So, we remove spaces when parsing the - # names, in order to match just names - # and commas for the names - $arg =~ s/\s*,\s*/,/g; - if ($arg =~ m/(.*)\s+([\S+,]+)/) { - $type = $1; - $names = $2; - } else { - $newmember .= "$arg; "; - next; - } - foreach my $name (split /,/, $names) { - $name =~ s/^\s*\**(\S+)\s*/$1/; - next if (($name =~ m/^\s*$/)); - if ($id =~ m/^\s*$/) { - # anonymous struct/union - $newmember .= "$type $name; "; - } else { - $newmember .= "$type $id.$name; "; - } - } - } - } - } - $members =~ s/(struct|union)([^\{\};]+)\{([^\{\}]*)\}([^\{\}\;]*)\;/$newmember/; - } - - # Ignore other nested elements, like enums - $members =~ s/(\{[^\{\}]*\})//g; - - create_parameterlist($members, ';', $file, $declaration_name); - check_sections($file, $declaration_name, $decl_type, $sectcheck, $struct_actual); - - # Adjust declaration for better display - $declaration =~ s/([\{;])/$1\n/g; - $declaration =~ s/\}\s+;/};/g; - # Better handle inlined enums - do {} while ($declaration =~ s/(enum\s+\{[^\}]+),([^\n])/$1,\n$2/); - - my @def_args = split /\n/, $declaration; - my $level = 1; - $declaration = ""; - foreach my $clause (@def_args) { - $clause =~ s/^\s+//; - $clause =~ s/\s+$//; - $clause =~ s/\s+/ /; - next if (!$clause); - $level-- if ($clause =~ m/(\})/ && $level > 1); - if (!($clause =~ m/^\s*#/)) { - $declaration .= "\t" x $level; - } - $declaration .= "\t" . $clause . "\n"; - $level++ if ($clause =~ m/(\{)/ && !($clause =~m/\}/)); - } - output_declaration($declaration_name, - 'struct', - {'struct' => $declaration_name, - 'module' => $modulename, - 'definition' => $declaration, - 'parameterlist' => \@parameterlist, - 'parameterdescs' => \%parameterdescs, - 'parametertypes' => \%parametertypes, - 'sectionlist' => \@sectionlist, - 'sections' => \%sections, - 'purpose' => $declaration_purpose, - 'type' => $decl_type - }); - } - else { - print STDERR "${file}:$.: error: Cannot parse struct or union!\n"; - ++$errors; - } -} - - -sub show_warnings($$) { - my $functype = shift; - my $name = shift; - - return 0 if (defined($nosymbol_table{$name})); - - return 1 if ($output_selection == OUTPUT_ALL); - - if ($output_selection == OUTPUT_EXPORTED) { - if (defined($function_table{$name})) { - return 1; - } else { - return 0; - } - } - if ($output_selection == OUTPUT_INTERNAL) { - if (!($functype eq "function" && defined($function_table{$name}))) { - return 1; - } else { - return 0; - } - } - if ($output_selection == OUTPUT_INCLUDE) { - if (defined($function_table{$name})) { - return 1; - } else { - return 0; - } - } - die("Please add the new output type at show_warnings()"); -} - -sub dump_enum($$) { - my $x = shift; - my $file = shift; - my $members; - - - $x =~ s@/\*.*?\*/@@gos; # strip comments. - # strip #define macros inside enums - $x =~ s@#\s*((define|ifdef)\s+|endif)[^;]*;@@gos; - - if ($x =~ /typedef\s+enum\s*\{(.*)\}\s*(\w*)\s*;/) { - $declaration_name = $2; - $members = $1; - } elsif ($x =~ /enum\s+(\w*)\s*\{(.*)\}/) { - $declaration_name = $1; - $members = $2; - } - - if ($declaration_name) { - my %_members; - - $members =~ s/\s+$//; - - foreach my $arg (split ',', $members) { - $arg =~ s/^\s*(\w+).*/$1/; - push @parameterlist, $arg; - if (!$parameterdescs{$arg}) { - $parameterdescs{$arg} = $undescribed; - if (show_warnings("enum", $declaration_name)) { - print STDERR "${file}:$.: warning: Enum value '$arg' not described in enum '$declaration_name'\n"; - } - } - $_members{$arg} = 1; - } - - while (my ($k, $v) = each %parameterdescs) { - if (!exists($_members{$k})) { - if (show_warnings("enum", $declaration_name)) { - print STDERR "${file}:$.: warning: Excess enum value '$k' description in '$declaration_name'\n"; - } - } - } - - output_declaration($declaration_name, - 'enum', - {'enum' => $declaration_name, - 'module' => $modulename, - 'parameterlist' => \@parameterlist, - 'parameterdescs' => \%parameterdescs, - 'sectionlist' => \@sectionlist, - 'sections' => \%sections, - 'purpose' => $declaration_purpose - }); - } else { - print STDERR "${file}:$.: error: Cannot parse enum!\n"; - ++$errors; - } -} - -my $typedef_type = qr { ((?:\s+[\w\*]+){1,8})\s* }x; -my $typedef_ident = qr { \*?\s*(\w\S+)\s* }x; -my $typedef_args = qr { \s*\((.*)\); }x; - -my $typedef1 = qr { typedef$typedef_type\($typedef_ident\)$typedef_args }x; -my $typedef2 = qr { typedef$typedef_type$typedef_ident$typedef_args }x; - -sub dump_typedef($$) { - my $x = shift; - my $file = shift; - - $x =~ s@/\*.*?\*/@@gos; # strip comments. - - # Parse function typedef prototypes - if ($x =~ $typedef1 || $x =~ $typedef2) { - $return_type = $1; - $declaration_name = $2; - my $args = $3; - $return_type =~ s/^\s+//; - - create_parameterlist($args, ',', $file, $declaration_name); - - output_declaration($declaration_name, - 'function', - {'function' => $declaration_name, - 'typedef' => 1, - 'module' => $modulename, - 'functiontype' => $return_type, - 'parameterlist' => \@parameterlist, - 'parameterdescs' => \%parameterdescs, - 'parametertypes' => \%parametertypes, - 'sectionlist' => \@sectionlist, - 'sections' => \%sections, - 'purpose' => $declaration_purpose - }); - return; - } - - while (($x =~ /\(*.\)\s*;$/) || ($x =~ /\[*.\]\s*;$/)) { - $x =~ s/\(*.\)\s*;$/;/; - $x =~ s/\[*.\]\s*;$/;/; - } - - if ($x =~ /typedef.*\s+(\w+)\s*;/) { - $declaration_name = $1; - - output_declaration($declaration_name, - 'typedef', - {'typedef' => $declaration_name, - 'module' => $modulename, - 'sectionlist' => \@sectionlist, - 'sections' => \%sections, - 'purpose' => $declaration_purpose - }); - } - else { - print STDERR "${file}:$.: error: Cannot parse typedef!\n"; - ++$errors; - } -} - -sub save_struct_actual($) { - my $actual = shift; - - # strip all spaces from the actual param so that it looks like one string item - $actual =~ s/\s*//g; - $struct_actual = $struct_actual . $actual . " "; -} - -sub create_parameterlist($$$$) { - my $args = shift; - my $splitter = shift; - my $file = shift; - my $declaration_name = shift; - my $type; - my $param; - - # temporarily replace commas inside function pointer definition - while ($args =~ /(\([^\),]+),/) { - $args =~ s/(\([^\),]+),/$1#/g; - } - - foreach my $arg (split($splitter, $args)) { - # strip comments - $arg =~ s/\/\*.*\*\///; - # strip leading/trailing spaces - $arg =~ s/^\s*//; - $arg =~ s/\s*$//; - $arg =~ s/\s+/ /; - - if ($arg =~ /^#/) { - # Treat preprocessor directive as a typeless variable just to fill - # corresponding data structures "correctly". Catch it later in - # output_* subs. - push_parameter($arg, "", "", $file); - } elsif ($arg =~ m/\(.+\)\s*\(/) { - # pointer-to-function - $arg =~ tr/#/,/; - $arg =~ m/[^\(]+\(\*?\s*([\w\.]*)\s*\)/; - $param = $1; - $type = $arg; - $type =~ s/([^\(]+\(\*?)\s*$param/$1/; - save_struct_actual($param); - push_parameter($param, $type, $arg, $file, $declaration_name); - } elsif ($arg) { - $arg =~ s/\s*:\s*/:/g; - $arg =~ s/\s*\[/\[/g; - - my @args = split('\s*,\s*', $arg); - if ($args[0] =~ m/\*/) { - $args[0] =~ s/(\*+)\s*/ $1/; - } - - my @first_arg; - if ($args[0] =~ /^(.*\s+)(.*?\[.*\].*)$/) { - shift @args; - push(@first_arg, split('\s+', $1)); - push(@first_arg, $2); - } else { - @first_arg = split('\s+', shift @args); - } - - unshift(@args, pop @first_arg); - $type = join " ", @first_arg; - - foreach $param (@args) { - if ($param =~ m/^(\*+)\s*(.*)/) { - save_struct_actual($2); - - push_parameter($2, "$type $1", $arg, $file, $declaration_name); - } - elsif ($param =~ m/(.*?):(\d+)/) { - if ($type ne "") { # skip unnamed bit-fields - save_struct_actual($1); - push_parameter($1, "$type:$2", $arg, $file, $declaration_name) - } - } - else { - save_struct_actual($param); - push_parameter($param, $type, $arg, $file, $declaration_name); - } - } - } - } -} - -sub push_parameter($$$$$) { - my $param = shift; - my $type = shift; - my $org_arg = shift; - my $file = shift; - my $declaration_name = shift; - - if (($anon_struct_union == 1) && ($type eq "") && - ($param eq "}")) { - return; # ignore the ending }; from anon. struct/union - } - - $anon_struct_union = 0; - $param =~ s/[\[\)].*//; - - if ($type eq "" && $param =~ /\.\.\.$/) - { - if ($param =~ /\w\.\.\.$/) { - # for named variable parameters of the form `x...`, remove the dots - $param =~ s/\.\.\.$//; - } else { - # handles unnamed variable parameters - $param = "..."; - } - if (!defined $parameterdescs{$param} || $parameterdescs{$param} eq "") { - $parameterdescs{$param} = "variable arguments"; - } - } - elsif ($type eq "" && ($param eq "" or $param eq "void")) - { - $param="void"; - $parameterdescs{void} = "no arguments"; - } - elsif ($type eq "" && ($param eq "struct" or $param eq "union")) - # handle unnamed (anonymous) union or struct: - { - $type = $param; - $param = "{unnamed_" . $param . "}"; - $parameterdescs{$param} = "anonymous\n"; - $anon_struct_union = 1; - } - - # warn if parameter has no description - # (but ignore ones starting with # as these are not parameters - # but inline preprocessor statements); - # Note: It will also ignore void params and unnamed structs/unions - if (!defined $parameterdescs{$param} && $param !~ /^#/) { - $parameterdescs{$param} = $undescribed; - - if (show_warnings($type, $declaration_name) && $param !~ /\./) { - print STDERR - "${file}:$.: warning: Function parameter or member '$param' not described in '$declaration_name'\n"; - ++$warnings; - } - } - - # strip spaces from $param so that it is one continuous string - # on @parameterlist; - # this fixes a problem where check_sections() cannot find - # a parameter like "addr[6 + 2]" because it actually appears - # as "addr[6", "+", "2]" on the parameter list; - # but it's better to maintain the param string unchanged for output, - # so just weaken the string compare in check_sections() to ignore - # "[blah" in a parameter string; - ###$param =~ s/\s*//g; - push @parameterlist, $param; - $org_arg =~ s/\s\s+/ /g; - $parametertypes{$param} = $org_arg; -} - -sub check_sections($$$$$) { - my ($file, $decl_name, $decl_type, $sectcheck, $prmscheck) = @_; - my @sects = split ' ', $sectcheck; - my @prms = split ' ', $prmscheck; - my $err; - my ($px, $sx); - my $prm_clean; # strip trailing "[array size]" and/or beginning "*" - - foreach $sx (0 .. $#sects) { - $err = 1; - foreach $px (0 .. $#prms) { - $prm_clean = $prms[$px]; - $prm_clean =~ s/\[.*\]//; - $prm_clean =~ s/__attribute__\s*\(\([a-z,_\*\s\(\)]*\)\)//i; - # ignore array size in a parameter string; - # however, the original param string may contain - # spaces, e.g.: addr[6 + 2] - # and this appears in @prms as "addr[6" since the - # parameter list is split at spaces; - # hence just ignore "[..." for the sections check; - $prm_clean =~ s/\[.*//; - - ##$prm_clean =~ s/^\**//; - if ($prm_clean eq $sects[$sx]) { - $err = 0; - last; - } - } - if ($err) { - if ($decl_type eq "function") { - print STDERR "${file}:$.: warning: " . - "Excess function parameter " . - "'$sects[$sx]' " . - "description in '$decl_name'\n"; - ++$warnings; - } - } - } -} - -## -# Checks the section describing the return value of a function. -sub check_return_section { - my $file = shift; - my $declaration_name = shift; - my $return_type = shift; - - # Ignore an empty return type (It's a macro) - # Ignore functions with a "void" return type. (But don't ignore "void *") - if (($return_type eq "") || ($return_type =~ /void\s*\w*\s*$/)) { - return; - } - - if (!defined($sections{$section_return}) || - $sections{$section_return} eq "") { - print STDERR "${file}:$.: warning: " . - "No description found for return value of " . - "'$declaration_name'\n"; - ++$warnings; - } -} - -## -# takes a function prototype and the name of the current file being -# processed and spits out all the details stored in the global -# arrays/hashes. -sub dump_function($$) { - my $prototype = shift; - my $file = shift; - my $noret = 0; - - print_lineno($new_start_line); - - $prototype =~ s/^static +//; - $prototype =~ s/^extern +//; - $prototype =~ s/^asmlinkage +//; - $prototype =~ s/^inline +//; - $prototype =~ s/^__inline__ +//; - $prototype =~ s/^__inline +//; - $prototype =~ s/^__always_inline +//; - $prototype =~ s/^noinline +//; - $prototype =~ s/__init +//; - $prototype =~ s/__init_or_module +//; - $prototype =~ s/__meminit +//; - $prototype =~ s/__must_check +//; - $prototype =~ s/__weak +//; - $prototype =~ s/__sched +//; - $prototype =~ s/__printf\s*\(\s*\d*\s*,\s*\d*\s*\) +//; - my $define = $prototype =~ s/^#\s*define\s+//; #ak added - $prototype =~ s/__attribute__\s*\(\( - (?: - [\w\s]++ # attribute name - (?:\([^)]*+\))? # attribute arguments - \s*+,? # optional comma at the end - )+ - \)\)\s+//x; - - # Strip QEMU specific compiler annotations - $prototype =~ s/QEMU_[A-Z_]+ +//; - - # Yes, this truly is vile. We are looking for: - # 1. Return type (may be nothing if we're looking at a macro) - # 2. Function name - # 3. Function parameters. - # - # All the while we have to watch out for function pointer parameters - # (which IIRC is what the two sections are for), C types (these - # regexps don't even start to express all the possibilities), and - # so on. - # - # If you mess with these regexps, it's a good idea to check that - # the following functions' documentation still comes out right: - # - parport_register_device (function pointer parameters) - # - atomic_set (macro) - # - pci_match_device, __copy_to_user (long return type) - - if ($define && $prototype =~ m/^()([a-zA-Z0-9_~:]+)\s+/) { - # This is an object-like macro, it has no return type and no parameter - # list. - # Function-like macros are not allowed to have spaces between - # declaration_name and opening parenthesis (notice the \s+). - $return_type = $1; - $declaration_name = $2; - $noret = 1; - } elsif ($prototype =~ m/^()([a-zA-Z0-9_~:]+)\s*\(([^\(]*)\)/ || - $prototype =~ m/^(\w+)\s+([a-zA-Z0-9_~:]+)\s*\(([^\(]*)\)/ || - $prototype =~ m/^(\w+\s*\*+)\s*([a-zA-Z0-9_~:]+)\s*\(([^\(]*)\)/ || - $prototype =~ m/^(\w+\s+\w+)\s+([a-zA-Z0-9_~:]+)\s*\(([^\(]*)\)/ || - $prototype =~ m/^(\w+\s+\w+\s*\*+)\s*([a-zA-Z0-9_~:]+)\s*\(([^\(]*)\)/ || - $prototype =~ m/^(\w+\s+\w+\s+\w+)\s+([a-zA-Z0-9_~:]+)\s*\(([^\(]*)\)/ || - $prototype =~ m/^(\w+\s+\w+\s+\w+\s*\*+)\s*([a-zA-Z0-9_~:]+)\s*\(([^\(]*)\)/ || - $prototype =~ m/^()([a-zA-Z0-9_~:]+)\s*\(([^\{]*)\)/ || - $prototype =~ m/^(\w+)\s+([a-zA-Z0-9_~:]+)\s*\(([^\{]*)\)/ || - $prototype =~ m/^(\w+\s*\*+)\s*([a-zA-Z0-9_~:]+)\s*\(([^\{]*)\)/ || - $prototype =~ m/^(\w+\s+\w+)\s+([a-zA-Z0-9_~:]+)\s*\(([^\{]*)\)/ || - $prototype =~ m/^(\w+\s+\w+\s*\*+)\s*([a-zA-Z0-9_~:]+)\s*\(([^\{]*)\)/ || - $prototype =~ m/^(\w+\s+\w+\s+\w+)\s+([a-zA-Z0-9_~:]+)\s*\(([^\{]*)\)/ || - $prototype =~ m/^(\w+\s+\w+\s+\w+\s*\*+)\s*([a-zA-Z0-9_~:]+)\s*\(([^\{]*)\)/ || - $prototype =~ m/^(\w+\s+\w+\s+\w+\s+\w+)\s+([a-zA-Z0-9_~:]+)\s*\(([^\{]*)\)/ || - $prototype =~ m/^(\w+\s+\w+\s+\w+\s+\w+\s*\*+)\s*([a-zA-Z0-9_~:]+)\s*\(([^\{]*)\)/ || - $prototype =~ m/^(\w+\s+\w+\s*\*+\s*\w+\s*\*+\s*)\s*([a-zA-Z0-9_~:]+)\s*\(([^\{]*)\)/) { - $return_type = $1; - $declaration_name = $2; - my $args = $3; - - create_parameterlist($args, ',', $file, $declaration_name); - } else { - print STDERR "${file}:$.: warning: cannot understand function prototype: '$prototype'\n"; - return; - } - - my $prms = join " ", @parameterlist; - check_sections($file, $declaration_name, "function", $sectcheck, $prms); - - # This check emits a lot of warnings at the moment, because many - # functions don't have a 'Return' doc section. So until the number - # of warnings goes sufficiently down, the check is only performed in - # verbose mode. - # TODO: always perform the check. - if ($verbose && !$noret) { - check_return_section($file, $declaration_name, $return_type); - } - - # The function parser can be called with a typedef parameter. - # Handle it. - if ($return_type =~ /typedef/) { - output_declaration($declaration_name, - 'function', - {'function' => $declaration_name, - 'typedef' => 1, - 'module' => $modulename, - 'functiontype' => $return_type, - 'parameterlist' => \@parameterlist, - 'parameterdescs' => \%parameterdescs, - 'parametertypes' => \%parametertypes, - 'sectionlist' => \@sectionlist, - 'sections' => \%sections, - 'purpose' => $declaration_purpose - }); - } else { - output_declaration($declaration_name, - 'function', - {'function' => $declaration_name, - 'module' => $modulename, - 'functiontype' => $return_type, - 'parameterlist' => \@parameterlist, - 'parameterdescs' => \%parameterdescs, - 'parametertypes' => \%parametertypes, - 'sectionlist' => \@sectionlist, - 'sections' => \%sections, - 'purpose' => $declaration_purpose - }); - } -} - -sub reset_state { - $function = ""; - %parameterdescs = (); - %parametertypes = (); - @parameterlist = (); - %sections = (); - @sectionlist = (); - $sectcheck = ""; - $struct_actual = ""; - $prototype = ""; - - $state = STATE_NORMAL; - $inline_doc_state = STATE_INLINE_NA; -} - -sub tracepoint_munge($) { - my $file = shift; - my $tracepointname = 0; - my $tracepointargs = 0; - - if ($prototype =~ m/TRACE_EVENT\((.*?),/) { - $tracepointname = $1; - } - if ($prototype =~ m/DEFINE_SINGLE_EVENT\((.*?),/) { - $tracepointname = $1; - } - if ($prototype =~ m/DEFINE_EVENT\((.*?),(.*?),/) { - $tracepointname = $2; - } - $tracepointname =~ s/^\s+//; #strip leading whitespace - if ($prototype =~ m/TP_PROTO\((.*?)\)/) { - $tracepointargs = $1; - } - if (($tracepointname eq 0) || ($tracepointargs eq 0)) { - print STDERR "${file}:$.: warning: Unrecognized tracepoint format: \n". - "$prototype\n"; - } else { - $prototype = "static inline void trace_$tracepointname($tracepointargs)"; - } -} - -sub syscall_munge() { - my $void = 0; - - $prototype =~ s@[\r\n]+@ @gos; # strip newlines/CR's -## if ($prototype =~ m/SYSCALL_DEFINE0\s*\(\s*(a-zA-Z0-9_)*\s*\)/) { - if ($prototype =~ m/SYSCALL_DEFINE0/) { - $void = 1; -## $prototype = "long sys_$1(void)"; - } - - $prototype =~ s/SYSCALL_DEFINE.*\(/long sys_/; # fix return type & func name - if ($prototype =~ m/long (sys_.*?),/) { - $prototype =~ s/,/\(/; - } elsif ($void) { - $prototype =~ s/\)/\(void\)/; - } - - # now delete all of the odd-number commas in $prototype - # so that arg types & arg names don't have a comma between them - my $count = 0; - my $len = length($prototype); - if ($void) { - $len = 0; # skip the for-loop - } - for (my $ix = 0; $ix < $len; $ix++) { - if (substr($prototype, $ix, 1) eq ',') { - $count++; - if ($count % 2 == 1) { - substr($prototype, $ix, 1) = ' '; - } - } - } -} - -sub process_proto_function($$) { - my $x = shift; - my $file = shift; - - $x =~ s@\/\/.*$@@gos; # strip C99-style comments to end of line - - if ($x =~ m#\s*/\*\s+MACDOC\s*#io || ($x =~ /^#/ && $x !~ /^#\s*define/)) { - # do nothing - } - elsif ($x =~ /([^\{]*)/) { - $prototype .= $1; - } - - if (($x =~ /\{/) || ($x =~ /\#\s*define/) || ($x =~ /;/)) { - $prototype =~ s@/\*.*?\*/@@gos; # strip comments. - $prototype =~ s@[\r\n]+@ @gos; # strip newlines/cr's. - $prototype =~ s@^\s+@@gos; # strip leading spaces - - # Handle prototypes for function pointers like: - # int (*pcs_config)(struct foo) - $prototype =~ s@^(\S+\s+)\(\s*\*(\S+)\)@$1$2@gos; - - if ($prototype =~ /SYSCALL_DEFINE/) { - syscall_munge(); - } - if ($prototype =~ /TRACE_EVENT/ || $prototype =~ /DEFINE_EVENT/ || - $prototype =~ /DEFINE_SINGLE_EVENT/) - { - tracepoint_munge($file); - } - dump_function($prototype, $file); - reset_state(); - } -} - -sub process_proto_type($$) { - my $x = shift; - my $file = shift; - - $x =~ s@[\r\n]+@ @gos; # strip newlines/cr's. - $x =~ s@^\s+@@gos; # strip leading spaces - $x =~ s@\s+$@@gos; # strip trailing spaces - $x =~ s@\/\/.*$@@gos; # strip C99-style comments to end of line - - if ($x =~ /^#/) { - # To distinguish preprocessor directive from regular declaration later. - $x .= ";"; - } - - while (1) { - if ( $x =~ /([^\{\};]*)([\{\};])(.*)/ ) { - if( length $prototype ) { - $prototype .= " " - } - $prototype .= $1 . $2; - ($2 eq '{') && $brcount++; - ($2 eq '}') && $brcount--; - if (($2 eq ';') && ($brcount == 0)) { - dump_declaration($prototype, $file); - reset_state(); - last; - } - $x = $3; - } else { - $prototype .= $x; - last; - } - } -} - - -sub map_filename($) { - my $file; - my ($orig_file) = @_; - - if (defined($ENV{'SRCTREE'})) { - $file = "$ENV{'SRCTREE'}" . "/" . $orig_file; - } else { - $file = $orig_file; - } - - if (defined($source_map{$file})) { - $file = $source_map{$file}; - } - - return $file; -} - -sub process_export_file($) { - my ($orig_file) = @_; - my $file = map_filename($orig_file); - - if (!open(IN,"<$file")) { - print STDERR "Error: Cannot open file $file\n"; - ++$errors; - return; - } - - while () { - if (/$export_symbol/) { - next if (defined($nosymbol_table{$2})); - $function_table{$2} = 1; - } - } - - close(IN); -} - -# -# Parsers for the various processing states. -# -# STATE_NORMAL: looking for the /** to begin everything. -# -sub process_normal() { - if (/$doc_start/o) { - $state = STATE_NAME; # next line is always the function name - $in_doc_sect = 0; - $declaration_start_line = $. + 1; - } -} - -# -# STATE_NAME: Looking for the "name - description" line -# -sub process_name($$) { - my $file = shift; - my $identifier; - my $descr; - - if (/$doc_block/o) { - $state = STATE_DOCBLOCK; - $contents = ""; - $new_start_line = $.; - - if ( $1 eq "" ) { - $section = $section_intro; - } else { - $section = $1; - } - } - elsif (/$doc_decl/o) { - $identifier = $1; - if (/\s*([\w\s]+?)(\s*-|:)/) { - $identifier = $1; - } - - $state = STATE_BODY; - # if there's no @param blocks need to set up default section - # here - $contents = ""; - $section = $section_default; - $new_start_line = $. + 1; - if (/[-:](.*)/) { - # strip leading/trailing/multiple spaces - $descr= $1; - $descr =~ s/^\s*//; - $descr =~ s/\s*$//; - $descr =~ s/\s+/ /g; - $declaration_purpose = $descr; - $state = STATE_BODY_MAYBE; - } else { - $declaration_purpose = ""; - } - - if (($declaration_purpose eq "") && $verbose) { - print STDERR "${file}:$.: warning: missing initial short description on line:\n"; - print STDERR $_; - ++$warnings; - } - - if ($identifier =~ m/^struct\b/) { - $decl_type = 'struct'; - } elsif ($identifier =~ m/^union\b/) { - $decl_type = 'union'; - } elsif ($identifier =~ m/^enum\b/) { - $decl_type = 'enum'; - } elsif ($identifier =~ m/^typedef\b/) { - $decl_type = 'typedef'; - } else { - $decl_type = 'function'; - } - - if ($verbose) { - print STDERR "${file}:$.: info: Scanning doc for $identifier\n"; - } - } else { - print STDERR "${file}:$.: warning: Cannot understand $_ on line $.", - " - I thought it was a doc line\n"; - ++$warnings; - $state = STATE_NORMAL; - } -} - - -# -# STATE_BODY and STATE_BODY_MAYBE: the bulk of a kerneldoc comment. -# -sub process_body($$) { - my $file = shift; - - # Until all named variable macro parameters are - # documented using the bare name (`x`) rather than with - # dots (`x...`), strip the dots: - if ($section =~ /\w\.\.\.$/) { - $section =~ s/\.\.\.$//; - - if ($verbose) { - print STDERR "${file}:$.: warning: Variable macro arguments should be documented without dots\n"; - ++$warnings; - } - } - - if ($state == STATE_BODY_WITH_BLANK_LINE && /^\s*\*\s?\S/) { - dump_section($file, $section, $contents); - $section = $section_default; - $new_start_line = $.; - $contents = ""; - } - - if (/$doc_sect/i) { # case insensitive for supported section names - $newsection = $1; - $newcontents = $2; - - # map the supported section names to the canonical names - if ($newsection =~ m/^description$/i) { - $newsection = $section_default; - } elsif ($newsection =~ m/^context$/i) { - $newsection = $section_context; - } elsif ($newsection =~ m/^returns?$/i) { - $newsection = $section_return; - } elsif ($newsection =~ m/^\@return$/) { - # special: @return is a section, not a param description - $newsection = $section_return; - } - - if (($contents ne "") && ($contents ne "\n")) { - if (!$in_doc_sect && $verbose) { - print STDERR "${file}:$.: warning: contents before sections\n"; - ++$warnings; - } - dump_section($file, $section, $contents); - $section = $section_default; - } - - $in_doc_sect = 1; - $state = STATE_BODY; - $contents = $newcontents; - $new_start_line = $.; - while (substr($contents, 0, 1) eq " ") { - $contents = substr($contents, 1); - } - if ($contents ne "") { - $contents .= "\n"; - } - $section = $newsection; - $leading_space = undef; - } elsif (/$doc_end/) { - if (($contents ne "") && ($contents ne "\n")) { - dump_section($file, $section, $contents); - $section = $section_default; - $contents = ""; - } - # look for doc_com + + doc_end: - if ($_ =~ m'\s*\*\s*[a-zA-Z_0-9:\.]+\*/') { - print STDERR "${file}:$.: warning: suspicious ending line: $_"; - ++$warnings; - } - - $prototype = ""; - $state = STATE_PROTO; - $brcount = 0; - $new_start_line = $. + 1; - } elsif (/$doc_content/) { - if ($1 eq "") { - if ($section eq $section_context) { - dump_section($file, $section, $contents); - $section = $section_default; - $contents = ""; - $new_start_line = $.; - $state = STATE_BODY; - } else { - if ($section ne $section_default) { - $state = STATE_BODY_WITH_BLANK_LINE; - } else { - $state = STATE_BODY; - } - $contents .= "\n"; - } - } elsif ($state == STATE_BODY_MAYBE) { - # Continued declaration purpose - chomp($declaration_purpose); - $declaration_purpose .= " " . $1; - $declaration_purpose =~ s/\s+/ /g; - } else { - my $cont = $1; - if ($section =~ m/^@/ || $section eq $section_context) { - if (!defined $leading_space) { - if ($cont =~ m/^(\s+)/) { - $leading_space = $1; - } else { - $leading_space = ""; - } - } - $cont =~ s/^$leading_space//; - } - $contents .= $cont . "\n"; - } - } else { - # i dont know - bad line? ignore. - print STDERR "${file}:$.: warning: bad line: $_"; - ++$warnings; - } -} - - -# -# STATE_PROTO: reading a function/whatever prototype. -# -sub process_proto($$) { - my $file = shift; - - if (/$doc_inline_oneline/) { - $section = $1; - $contents = $2; - if ($contents ne "") { - $contents .= "\n"; - dump_section($file, $section, $contents); - $section = $section_default; - $contents = ""; - } - } elsif (/$doc_inline_start/) { - $state = STATE_INLINE; - $inline_doc_state = STATE_INLINE_NAME; - } elsif ($decl_type eq 'function') { - process_proto_function($_, $file); - } else { - process_proto_type($_, $file); - } -} - -# -# STATE_DOCBLOCK: within a DOC: block. -# -sub process_docblock($$) { - my $file = shift; - - if (/$doc_end/) { - dump_doc_section($file, $section, $contents); - $section = $section_default; - $contents = ""; - $function = ""; - %parameterdescs = (); - %parametertypes = (); - @parameterlist = (); - %sections = (); - @sectionlist = (); - $prototype = ""; - $state = STATE_NORMAL; - } elsif (/$doc_content/) { - if ( $1 eq "" ) { - $contents .= $blankline; - } else { - $contents .= $1 . "\n"; - } - } -} - -# -# STATE_INLINE: docbook comments within a prototype. -# -sub process_inline($$) { - my $file = shift; - - # First line (state 1) needs to be a @parameter - if ($inline_doc_state == STATE_INLINE_NAME && /$doc_inline_sect/o) { - $section = $1; - $contents = $2; - $new_start_line = $.; - if ($contents ne "") { - while (substr($contents, 0, 1) eq " ") { - $contents = substr($contents, 1); - } - $contents .= "\n"; - } - $inline_doc_state = STATE_INLINE_TEXT; - # Documentation block end */ - } elsif (/$doc_inline_end/) { - if (($contents ne "") && ($contents ne "\n")) { - dump_section($file, $section, $contents); - $section = $section_default; - $contents = ""; - } - $state = STATE_PROTO; - $inline_doc_state = STATE_INLINE_NA; - # Regular text - } elsif (/$doc_content/) { - if ($inline_doc_state == STATE_INLINE_TEXT) { - $contents .= $1 . "\n"; - # nuke leading blank lines - if ($contents =~ /^\s*$/) { - $contents = ""; - } - } elsif ($inline_doc_state == STATE_INLINE_NAME) { - $inline_doc_state = STATE_INLINE_ERROR; - print STDERR "${file}:$.: warning: "; - print STDERR "Incorrect use of kernel-doc format: $_"; - ++$warnings; - } - } -} - - -sub process_file($) { - my $file; - my $initial_section_counter = $section_counter; - my ($orig_file) = @_; - - $file = map_filename($orig_file); - - if (!open(IN_FILE,"<$file")) { - print STDERR "Error: Cannot open file $file\n"; - ++$errors; - return; - } - - $. = 1; - - $section_counter = 0; - while () { - while (s/\\\s*$//) { - $_ .= ; - } - # Replace tabs by spaces - while ($_ =~ s/\t+/' ' x (length($&) * 8 - length($`) % 8)/e) {}; - # Hand this line to the appropriate state handler - if ($state == STATE_NORMAL) { - process_normal(); - } elsif ($state == STATE_NAME) { - process_name($file, $_); - } elsif ($state == STATE_BODY || $state == STATE_BODY_MAYBE || - $state == STATE_BODY_WITH_BLANK_LINE) { - process_body($file, $_); - } elsif ($state == STATE_INLINE) { # scanning for inline parameters - process_inline($file, $_); - } elsif ($state == STATE_PROTO) { - process_proto($file, $_); - } elsif ($state == STATE_DOCBLOCK) { - process_docblock($file, $_); - } - } - - # Make sure we got something interesting. - if ($initial_section_counter == $section_counter && $ - output_mode ne "none") { - if ($output_selection == OUTPUT_INCLUDE) { - print STDERR "${file}:1: warning: '$_' not found\n" - for keys %function_table; - } - else { - print STDERR "${file}:1: warning: no structured comments found\n"; - } - } - close IN_FILE; -} - - -if ($output_mode eq "rst") { - get_sphinx_version() if (!$sphinx_major); -} - -$kernelversion = get_kernel_version(); - -# generate a sequence of code that will splice in highlighting information -# using the s// operator. -for (my $k = 0; $k < @highlights; $k++) { - my $pattern = $highlights[$k][0]; - my $result = $highlights[$k][1]; -# print STDERR "scanning pattern:$pattern, highlight:($result)\n"; - $dohighlight .= "\$contents =~ s:$pattern:$result:gs;\n"; -} - -# Read the file that maps relative names to absolute names for -# separate source and object directories and for shadow trees. -if (open(SOURCE_MAP, "<.tmp_filelist.txt")) { - my ($relname, $absname); - while() { - chop(); - ($relname, $absname) = (split())[0..1]; - $relname =~ s:^/+::; - $source_map{$relname} = $absname; - } - close(SOURCE_MAP); -} - -if ($output_selection == OUTPUT_EXPORTED || - $output_selection == OUTPUT_INTERNAL) { - - push(@export_file_list, @ARGV); - - foreach (@export_file_list) { - chomp; - process_export_file($_); - } -} - -foreach (@ARGV) { - chomp; - process_file($_); -} -if ($verbose && $errors) { - print STDERR "$errors errors\n"; -} -if ($verbose && $warnings) { - print STDERR "$warnings warnings\n"; -} - -if ($Werror && $warnings) { - print STDERR "$warnings warnings as Errors\n"; - exit($warnings); -} else { - exit($output_mode eq "none" ? 0 : $errors) -} From e0ca100425853aa362acefeb027800d952fb222d Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 14 Aug 2025 18:13:23 +0100 Subject: [PATCH 0250/2396] MAINTAINERS: Put kernel-doc under the "docs build machinery" section We never had a MAINTAINERS entry for the old kernel-doc script; add the files for the new Python kernel-doc under "Sphinx documentation configuration and build machinery", as the most appropriate subsection. Mauro has kindly volunteered to help with maintenance/review of this area of the codebase, so add him as a maintainer. Signed-off-by: Peter Maydell Reviewed-by: Paolo Bonzini Reviewed-by: Mauro Carvalho Chehab Message-id: 20250814171324.1614516-9-peter.maydell@linaro.org --- MAINTAINERS | 3 +++ 1 file changed, 3 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 8f074e4371..8147fff352 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4441,6 +4441,7 @@ F: po/*.po Sphinx documentation configuration and build machinery M: John Snow M: Peter Maydell +M: Mauro Carvalho Chehab S: Maintained F: docs/conf.py F: docs/*/conf.py @@ -4449,6 +4450,8 @@ F: docs/sphinx/ F: docs/_templates/ F: docs/devel/docs.rst F: docs/devel/qapi-domain.rst +F: scripts/kernel-doc +F: scripts/lib/kdoc/ Rust build system integration M: Manos Pitsidianakis From c2fae597099ef6ff81ca63d69ee28eddb982d894 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 19 Aug 2025 15:56:58 +0100 Subject: [PATCH 0251/2396] target/arm: Correct condition of aa64_atomics feature function The ARMv8.1-Atomics feature (renamed FEAT_LSE in more modern versions of the Arm ARM) has always ben indicated by ID_AA64ISAR0.ATOMIC being 0b0010 or greater; 0b0001 is a reserved unused value. We were incorrectly checking for != 0; this had no harmful effects because all the CPUs set their value for this field to either 0 (for not having the feature) or 2 (if they do have it), but it's better to match what the architecture specifies here. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20250819145659.2165160-1-peter.maydell@linaro.org --- target/arm/cpu-features.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/arm/cpu-features.h b/target/arm/cpu-features.h index 41511d0835..d48754bcf2 100644 --- a/target/arm/cpu-features.h +++ b/target/arm/cpu-features.h @@ -408,7 +408,7 @@ static inline bool isar_feature_aa64_crc32(const ARMISARegisters *id) static inline bool isar_feature_aa64_atomics(const ARMISARegisters *id) { - return FIELD_EX64_IDREG(id, ID_AA64ISAR0, ATOMIC) != 0; + return FIELD_EX64_IDREG(id, ID_AA64ISAR0, ATOMIC) >= 2; } static inline bool isar_feature_aa64_rdm(const ARMISARegisters *id) From 1748c0d59228c7790940d8be381df1c3108022b1 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 15 Aug 2025 22:26:47 +1000 Subject: [PATCH 0252/2396] qemu/atomic: Finish renaming atomic128-cas.h headers The aarch64 header was not renamed with the others, meaning it was skipped in favor of the generic version. Cc: qemu-stable@nongnu.org Fixes: 15606965400b ("qemu/atomic: Rename atomic128-cas.h headers using .h.inc suffix") Signed-off-by: Richard Henderson Reviewed-by: Peter Maydell Message-id: 20250815122653.701782-2-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- .../include/aarch64/host/{atomic128-cas.h => atomic128-cas.h.inc} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename host/include/aarch64/host/{atomic128-cas.h => atomic128-cas.h.inc} (100%) diff --git a/host/include/aarch64/host/atomic128-cas.h b/host/include/aarch64/host/atomic128-cas.h.inc similarity index 100% rename from host/include/aarch64/host/atomic128-cas.h rename to host/include/aarch64/host/atomic128-cas.h.inc From 1d5e88e7b5b83130deb312d05de11267baac4c05 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 15 Aug 2025 22:26:48 +1000 Subject: [PATCH 0253/2396] qemu/atomic: Add atomic16 primitives for xchg, fetch_and, fetch_or Signed-off-by: Richard Henderson Reviewed-by: Peter Maydell Message-id: 20250815122653.701782-3-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- host/include/aarch64/host/atomic128-cas.h.inc | 57 +++++++++++ host/include/generic/host/atomic128-cas.h.inc | 96 +++++++++++++++++++ 2 files changed, 153 insertions(+) diff --git a/host/include/aarch64/host/atomic128-cas.h.inc b/host/include/aarch64/host/atomic128-cas.h.inc index 991da4ef54..aec27df182 100644 --- a/host/include/aarch64/host/atomic128-cas.h.inc +++ b/host/include/aarch64/host/atomic128-cas.h.inc @@ -38,6 +38,63 @@ static inline Int128 atomic16_cmpxchg(Int128 *ptr, Int128 cmp, Int128 new) return int128_make128(oldl, oldh); } +static inline Int128 atomic16_xchg(Int128 *ptr, Int128 new) +{ + uint64_t newl = int128_getlo(new), newh = int128_gethi(new); + uint64_t oldl, oldh; + uint32_t tmp; + + asm("0: ldaxp %[oldl], %[oldh], %[mem]\n\t" + "stlxp %w[tmp], %[newl], %[newh], %[mem]\n\t" + "cbnz %w[tmp], 0b" + : [mem] "+m"(*ptr), [tmp] "=&r"(tmp), + [oldl] "=&r"(oldl), [oldh] "=&r"(oldh) + : [newl] "r"(newl), [newh] "r"(newh) + : "memory"); + + return int128_make128(oldl, oldh); +} + +static inline Int128 atomic16_fetch_and(Int128 *ptr, Int128 new) +{ + uint64_t newl = int128_getlo(new), newh = int128_gethi(new); + uint64_t oldl, oldh, tmpl, tmph; + uint32_t tmp; + + asm("0: ldaxp %[oldl], %[oldh], %[mem]\n\t" + "and %[tmpl], %[oldl], %[newl]\n\t" + "and %[tmph], %[oldh], %[newh]\n\t" + "stlxp %w[tmp], %[tmpl], %[tmph], %[mem]\n\t" + "cbnz %w[tmp], 0b" + : [mem] "+m"(*ptr), [tmp] "=&r"(tmp), + [oldl] "=&r"(oldl), [oldh] "=&r"(oldh) + : [newl] "r"(newl), [newh] "r"(newh), + [tmpl] "r"(tmpl), [tmph] "r"(tmph) + : "memory"); + + return int128_make128(oldl, oldh); +} + +static inline Int128 atomic16_fetch_or(Int128 *ptr, Int128 new) +{ + uint64_t newl = int128_getlo(new), newh = int128_gethi(new); + uint64_t oldl, oldh, tmpl, tmph; + uint32_t tmp; + + asm("0: ldaxp %[oldl], %[oldh], %[mem]\n\t" + "orr %[tmpl], %[oldl], %[newl]\n\t" + "orr %[tmph], %[oldh], %[newh]\n\t" + "stlxp %w[tmp], %[tmpl], %[tmph], %[mem]\n\t" + "cbnz %w[tmp], 0b" + : [mem] "+m"(*ptr), [tmp] "=&r"(tmp), + [oldl] "=&r"(oldl), [oldh] "=&r"(oldh) + : [newl] "r"(newl), [newh] "r"(newh), + [tmpl] "r"(tmpl), [tmph] "r"(tmph) + : "memory"); + + return int128_make128(oldl, oldh); +} + # define CONFIG_CMPXCHG128 1 # define HAVE_CMPXCHG128 1 #endif diff --git a/host/include/generic/host/atomic128-cas.h.inc b/host/include/generic/host/atomic128-cas.h.inc index 6b40cc2271..990162c56f 100644 --- a/host/include/generic/host/atomic128-cas.h.inc +++ b/host/include/generic/host/atomic128-cas.h.inc @@ -23,6 +23,51 @@ atomic16_cmpxchg(Int128 *ptr, Int128 cmp, Int128 new) r.i = qatomic_cmpxchg__nocheck(ptr_align, c.i, n.i); return r.s; } + +/* + * Since we're looping anyway, use weak compare and swap. + * If the host supports weak, this will eliminate a second loop hidden + * within the atomic operation itself; otherwise the weak parameter is + * ignored. + */ +static inline Int128 ATTRIBUTE_ATOMIC128_OPT +atomic16_xchg(Int128 *ptr, Int128 new) +{ + __int128_t *ptr_align = __builtin_assume_aligned(ptr, 16); + Int128 old = *ptr_align; + + while (!__atomic_compare_exchange_n(ptr_align, &old, new, true, + __ATOMIC_SEQ_CST, 0)) { + continue; + } + return old; +} + +static inline Int128 ATTRIBUTE_ATOMIC128_OPT +atomic16_fetch_and(Int128 *ptr, Int128 val) +{ + __int128_t *ptr_align = __builtin_assume_aligned(ptr, 16); + Int128 old = *ptr_align; + + while (!__atomic_compare_exchange_n(ptr_align, &old, old & val, true, + __ATOMIC_SEQ_CST, 0)) { + continue; + } + return old; +} + +static inline Int128 ATTRIBUTE_ATOMIC128_OPT +atomic16_fetch_or(Int128 *ptr, Int128 val) +{ + __int128_t *ptr_align = __builtin_assume_aligned(ptr, 16); + Int128 old = *ptr_align; + + while (!__atomic_compare_exchange_n(ptr_align, &old, old | val, true, + __ATOMIC_SEQ_CST, 0)) { + continue; + } + return old; +} # define HAVE_CMPXCHG128 1 #elif defined(CONFIG_CMPXCHG128) static inline Int128 ATTRIBUTE_ATOMIC128_OPT @@ -36,6 +81,57 @@ atomic16_cmpxchg(Int128 *ptr, Int128 cmp, Int128 new) r.i = __sync_val_compare_and_swap_16(ptr_align, c.i, n.i); return r.s; } + +static inline Int128 ATTRIBUTE_ATOMIC128_OPT +atomic16_xchg(Int128 *ptr, Int128 new) +{ + Int128Aligned *ptr_align = __builtin_assume_aligned(ptr, 16); + Int128Alias o, n; + + n.s = new; + o.s = *ptr_align; + while (1) { + __int128 c = __sync_val_compare_and_swap_16(ptr_align, o.i, n.i); + if (c == o.i) { + return o.s; + } + o.i = c; + } +} + +static inline Int128 ATTRIBUTE_ATOMIC128_OPT +atomic16_fetch_and(Int128 *ptr, Int128 val) +{ + Int128Aligned *ptr_align = __builtin_assume_aligned(ptr, 16); + Int128Alias o, v; + + v.s = val; + o.s = *ptr_align; + while (1) { + __int128 c = __sync_val_compare_and_swap_16(ptr_align, o.i, o.i & v.i); + if (c == o.i) { + return o.s; + } + o.i = c; + } +} + +static inline Int128 ATTRIBUTE_ATOMIC128_OPT +atomic16_fetch_or(Int128 *ptr, Int128 val) +{ + Int128Aligned *ptr_align = __builtin_assume_aligned(ptr, 16); + Int128Alias o, v; + + v.s = val; + o.s = *ptr_align; + while (1) { + __int128 c = __sync_val_compare_and_swap_16(ptr_align, o.i, o.i | v.i); + if (c == o.i) { + return o.s; + } + o.i = c; + } +} # define HAVE_CMPXCHG128 1 #else /* Fallback definition that must be optimized away, or error. */ From 33aefd187eaff71dbc686c43f7acfdd0a81c7de4 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 15 Aug 2025 22:26:49 +1000 Subject: [PATCH 0254/2396] accel/tcg: Add cpu_atomic_*_mmu for 16-byte xchg, fetch_and, fetch_or Signed-off-by: Richard Henderson Reviewed-by: Peter Maydell Message-id: 20250815122653.701782-4-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- accel/tcg/atomic_template.h | 80 +++++++++++++++++++++++++++-- include/accel/tcg/cpu-ldst-common.h | 13 +++-- 2 files changed, 86 insertions(+), 7 deletions(-) diff --git a/accel/tcg/atomic_template.h b/accel/tcg/atomic_template.h index 08a475c10c..ae5203b439 100644 --- a/accel/tcg/atomic_template.h +++ b/accel/tcg/atomic_template.h @@ -100,7 +100,6 @@ ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, vaddr addr, return ret; } -#if DATA_SIZE < 16 ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, vaddr addr, ABI_TYPE val, MemOpIdx oi, uintptr_t retaddr) { @@ -108,7 +107,11 @@ ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, vaddr addr, ABI_TYPE val, DATA_SIZE, retaddr); DATA_TYPE ret; +#if DATA_SIZE == 16 + ret = atomic16_xchg(haddr, val); +#else ret = qatomic_xchg__nocheck(haddr, val); +#endif ATOMIC_MMU_CLEANUP; atomic_trace_rmw_post(env, addr, VALUE_LOW(ret), @@ -119,6 +122,39 @@ ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, vaddr addr, ABI_TYPE val, return ret; } +#if DATA_SIZE == 16 +ABI_TYPE ATOMIC_NAME(fetch_and)(CPUArchState *env, vaddr addr, ABI_TYPE val, + MemOpIdx oi, uintptr_t retaddr) +{ + DATA_TYPE *haddr = atomic_mmu_lookup(env_cpu(env), addr, oi, + DATA_SIZE, retaddr); + DATA_TYPE ret = atomic16_fetch_and(haddr, val); + ATOMIC_MMU_CLEANUP; + atomic_trace_rmw_post(env, addr, + VALUE_LOW(ret), + VALUE_HIGH(ret), + VALUE_LOW(val), + VALUE_HIGH(val), + oi); + return ret; +} + +ABI_TYPE ATOMIC_NAME(fetch_or)(CPUArchState *env, vaddr addr, ABI_TYPE val, + MemOpIdx oi, uintptr_t retaddr) +{ + DATA_TYPE *haddr = atomic_mmu_lookup(env_cpu(env), addr, oi, + DATA_SIZE, retaddr); + DATA_TYPE ret = atomic16_fetch_or(haddr, val); + ATOMIC_MMU_CLEANUP; + atomic_trace_rmw_post(env, addr, + VALUE_LOW(ret), + VALUE_HIGH(ret), + VALUE_LOW(val), + VALUE_HIGH(val), + oi); + return ret; +} +#else #define GEN_ATOMIC_HELPER(X) \ ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, vaddr addr, \ ABI_TYPE val, MemOpIdx oi, uintptr_t retaddr) \ @@ -188,7 +224,7 @@ GEN_ATOMIC_HELPER_FN(smax_fetch, MAX, SDATA_TYPE, new) GEN_ATOMIC_HELPER_FN(umax_fetch, MAX, DATA_TYPE, new) #undef GEN_ATOMIC_HELPER_FN -#endif /* DATA SIZE < 16 */ +#endif /* DATA SIZE == 16 */ #undef END @@ -225,7 +261,6 @@ ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, vaddr addr, return BSWAP(ret); } -#if DATA_SIZE < 16 ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, vaddr addr, ABI_TYPE val, MemOpIdx oi, uintptr_t retaddr) { @@ -233,7 +268,11 @@ ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, vaddr addr, ABI_TYPE val, DATA_SIZE, retaddr); ABI_TYPE ret; +#if DATA_SIZE == 16 + ret = atomic16_xchg(haddr, BSWAP(val)); +#else ret = qatomic_xchg__nocheck(haddr, BSWAP(val)); +#endif ATOMIC_MMU_CLEANUP; atomic_trace_rmw_post(env, addr, VALUE_LOW(ret), @@ -244,6 +283,39 @@ ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, vaddr addr, ABI_TYPE val, return BSWAP(ret); } +#if DATA_SIZE == 16 +ABI_TYPE ATOMIC_NAME(fetch_and)(CPUArchState *env, vaddr addr, ABI_TYPE val, + MemOpIdx oi, uintptr_t retaddr) +{ + DATA_TYPE *haddr = atomic_mmu_lookup(env_cpu(env), addr, oi, + DATA_SIZE, retaddr); + DATA_TYPE ret = atomic16_fetch_and(haddr, BSWAP(val)); + ATOMIC_MMU_CLEANUP; + atomic_trace_rmw_post(env, addr, + VALUE_LOW(ret), + VALUE_HIGH(ret), + VALUE_LOW(val), + VALUE_HIGH(val), + oi); + return BSWAP(ret); +} + +ABI_TYPE ATOMIC_NAME(fetch_or)(CPUArchState *env, vaddr addr, ABI_TYPE val, + MemOpIdx oi, uintptr_t retaddr) +{ + DATA_TYPE *haddr = atomic_mmu_lookup(env_cpu(env), addr, oi, + DATA_SIZE, retaddr); + DATA_TYPE ret = atomic16_fetch_or(haddr, BSWAP(val)); + ATOMIC_MMU_CLEANUP; + atomic_trace_rmw_post(env, addr, + VALUE_LOW(ret), + VALUE_HIGH(ret), + VALUE_LOW(val), + VALUE_HIGH(val), + oi); + return BSWAP(ret); +} +#else #define GEN_ATOMIC_HELPER(X) \ ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, vaddr addr, \ ABI_TYPE val, MemOpIdx oi, uintptr_t retaddr) \ @@ -317,7 +389,7 @@ GEN_ATOMIC_HELPER_FN(add_fetch, ADD, DATA_TYPE, new) #undef ADD #undef GEN_ATOMIC_HELPER_FN -#endif /* DATA_SIZE < 16 */ +#endif /* DATA_SIZE == 16 */ #undef END #endif /* DATA_SIZE > 1 */ diff --git a/include/accel/tcg/cpu-ldst-common.h b/include/accel/tcg/cpu-ldst-common.h index 8bf17c2fab..17a3250ded 100644 --- a/include/accel/tcg/cpu-ldst-common.h +++ b/include/accel/tcg/cpu-ldst-common.h @@ -100,9 +100,6 @@ GEN_ATOMIC_HELPER_ALL(umax_fetch) GEN_ATOMIC_HELPER_ALL(xchg) -#undef GEN_ATOMIC_HELPER_ALL -#undef GEN_ATOMIC_HELPER - Int128 cpu_atomic_cmpxchgo_le_mmu(CPUArchState *env, vaddr addr, Int128 cmpv, Int128 newv, MemOpIdx oi, uintptr_t retaddr); @@ -110,6 +107,16 @@ Int128 cpu_atomic_cmpxchgo_be_mmu(CPUArchState *env, vaddr addr, Int128 cmpv, Int128 newv, MemOpIdx oi, uintptr_t retaddr); +GEN_ATOMIC_HELPER(xchg, Int128, o_le) +GEN_ATOMIC_HELPER(xchg, Int128, o_be) +GEN_ATOMIC_HELPER(fetch_and, Int128, o_le) +GEN_ATOMIC_HELPER(fetch_and, Int128, o_be) +GEN_ATOMIC_HELPER(fetch_or, Int128, o_le) +GEN_ATOMIC_HELPER(fetch_or, Int128, o_be) + +#undef GEN_ATOMIC_HELPER_ALL +#undef GEN_ATOMIC_HELPER + uint8_t cpu_ldb_code_mmu(CPUArchState *env, vaddr addr, MemOpIdx oi, uintptr_t ra); uint16_t cpu_ldw_code_mmu(CPUArchState *env, vaddr addr, From 092ac2481a4301d0282227bb4ee8641b3f39e437 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 15 Aug 2025 22:26:50 +1000 Subject: [PATCH 0255/2396] tcg: Add tcg_gen_atomic_{xchg,fetch_and,fetch_or}_i128 Signed-off-by: Richard Henderson Reviewed-by: Peter Maydell Message-id: 20250815122653.701782-5-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- accel/tcg/atomic_common.c.inc | 9 ++++ accel/tcg/tcg-runtime.h | 12 +++++ include/tcg/tcg-op-common.h | 7 +++ include/tcg/tcg-op.h | 3 ++ tcg/tcg-op-ldst.c | 97 +++++++++++++++++++++++++++++++++-- 5 files changed, 125 insertions(+), 3 deletions(-) diff --git a/accel/tcg/atomic_common.c.inc b/accel/tcg/atomic_common.c.inc index 6056598c23..bca93a0ac4 100644 --- a/accel/tcg/atomic_common.c.inc +++ b/accel/tcg/atomic_common.c.inc @@ -122,5 +122,14 @@ GEN_ATOMIC_HELPERS(umax_fetch) GEN_ATOMIC_HELPERS(xchg) +#if HAVE_CMPXCHG128 +ATOMIC_HELPER(xchgo_be, Int128) +ATOMIC_HELPER(xchgo_le, Int128) +ATOMIC_HELPER(fetch_ando_be, Int128) +ATOMIC_HELPER(fetch_ando_le, Int128) +ATOMIC_HELPER(fetch_oro_be, Int128) +ATOMIC_HELPER(fetch_oro_le, Int128) +#endif + #undef ATOMIC_HELPER #undef GEN_ATOMIC_HELPERS diff --git a/accel/tcg/tcg-runtime.h b/accel/tcg/tcg-runtime.h index c23b5e66c4..8436599b9f 100644 --- a/accel/tcg/tcg-runtime.h +++ b/accel/tcg/tcg-runtime.h @@ -63,6 +63,18 @@ DEF_HELPER_FLAGS_5(atomic_cmpxchgo_be, TCG_CALL_NO_WG, i128, env, i64, i128, i128, i32) DEF_HELPER_FLAGS_5(atomic_cmpxchgo_le, TCG_CALL_NO_WG, i128, env, i64, i128, i128, i32) +DEF_HELPER_FLAGS_4(atomic_xchgo_be, TCG_CALL_NO_WG, + i128, env, i64, i128, i32) +DEF_HELPER_FLAGS_4(atomic_xchgo_le, TCG_CALL_NO_WG, + i128, env, i64, i128, i32) +DEF_HELPER_FLAGS_4(atomic_fetch_ando_be, TCG_CALL_NO_WG, + i128, env, i64, i128, i32) +DEF_HELPER_FLAGS_4(atomic_fetch_ando_le, TCG_CALL_NO_WG, + i128, env, i64, i128, i32) +DEF_HELPER_FLAGS_4(atomic_fetch_oro_be, TCG_CALL_NO_WG, + i128, env, i64, i128, i32) +DEF_HELPER_FLAGS_4(atomic_fetch_oro_le, TCG_CALL_NO_WG, + i128, env, i64, i128, i32) #endif DEF_HELPER_FLAGS_5(nonatomic_cmpxchgo, TCG_CALL_NO_WG, diff --git a/include/tcg/tcg-op-common.h b/include/tcg/tcg-op-common.h index e1071adebf..f752ef440b 100644 --- a/include/tcg/tcg-op-common.h +++ b/include/tcg/tcg-op-common.h @@ -344,6 +344,8 @@ void tcg_gen_atomic_xchg_i32_chk(TCGv_i32, TCGTemp *, TCGv_i32, TCGArg, MemOp, TCGType); void tcg_gen_atomic_xchg_i64_chk(TCGv_i64, TCGTemp *, TCGv_i64, TCGArg, MemOp, TCGType); +void tcg_gen_atomic_xchg_i128_chk(TCGv_i128, TCGTemp *, TCGv_i128, + TCGArg, MemOp, TCGType); void tcg_gen_atomic_fetch_add_i32_chk(TCGv_i32, TCGTemp *, TCGv_i32, TCGArg, MemOp, TCGType); @@ -411,6 +413,11 @@ void tcg_gen_atomic_umax_fetch_i32_chk(TCGv_i32, TCGTemp *, TCGv_i32, void tcg_gen_atomic_umax_fetch_i64_chk(TCGv_i64, TCGTemp *, TCGv_i64, TCGArg, MemOp, TCGType); +void tcg_gen_atomic_fetch_and_i128_chk(TCGv_i128, TCGTemp *, TCGv_i128, + TCGArg, MemOp, TCGType); +void tcg_gen_atomic_fetch_or_i128_chk(TCGv_i128, TCGTemp *, TCGv_i128, + TCGArg, MemOp, TCGType); + /* Vector ops */ void tcg_gen_mov_vec(TCGv_vec, TCGv_vec); diff --git a/include/tcg/tcg-op.h b/include/tcg/tcg-op.h index c912578fdd..232733cb71 100644 --- a/include/tcg/tcg-op.h +++ b/include/tcg/tcg-op.h @@ -134,13 +134,16 @@ DEF_ATOMIC3(tcg_gen_nonatomic_cmpxchg, i128) DEF_ATOMIC2(tcg_gen_atomic_xchg, i32) DEF_ATOMIC2(tcg_gen_atomic_xchg, i64) +DEF_ATOMIC2(tcg_gen_atomic_xchg, i128) DEF_ATOMIC2(tcg_gen_atomic_fetch_add, i32) DEF_ATOMIC2(tcg_gen_atomic_fetch_add, i64) DEF_ATOMIC2(tcg_gen_atomic_fetch_and, i32) DEF_ATOMIC2(tcg_gen_atomic_fetch_and, i64) +DEF_ATOMIC2(tcg_gen_atomic_fetch_and, i128) DEF_ATOMIC2(tcg_gen_atomic_fetch_or, i32) DEF_ATOMIC2(tcg_gen_atomic_fetch_or, i64) +DEF_ATOMIC2(tcg_gen_atomic_fetch_or, i128) DEF_ATOMIC2(tcg_gen_atomic_fetch_xor, i32) DEF_ATOMIC2(tcg_gen_atomic_fetch_xor, i64) DEF_ATOMIC2(tcg_gen_atomic_fetch_smin, i32) diff --git a/tcg/tcg-op-ldst.c b/tcg/tcg-op-ldst.c index 548496002d..67c15fd4d0 100644 --- a/tcg/tcg-op-ldst.c +++ b/tcg/tcg-op-ldst.c @@ -801,6 +801,8 @@ typedef void (*gen_atomic_op_i32)(TCGv_i32, TCGv_env, TCGv_i64, TCGv_i32, TCGv_i32); typedef void (*gen_atomic_op_i64)(TCGv_i64, TCGv_env, TCGv_i64, TCGv_i64, TCGv_i32); +typedef void (*gen_atomic_op_i128)(TCGv_i128, TCGv_env, TCGv_i64, + TCGv_i128, TCGv_i32); #ifdef CONFIG_ATOMIC64 # define WITH_ATOMIC64(X) X, @@ -1201,6 +1203,94 @@ static void do_atomic_op_i64(TCGv_i64 ret, TCGTemp *addr, TCGv_i64 val, } } +static void do_nonatomic_op_i128(TCGv_i128 ret, TCGTemp *addr, TCGv_i128 val, + TCGArg idx, MemOp memop, bool new_val, + void (*gen)(TCGv_i64, TCGv_i64, TCGv_i64)) +{ + TCGv_i128 t = tcg_temp_ebb_new_i128(); + TCGv_i128 r = tcg_temp_ebb_new_i128(); + + tcg_gen_qemu_ld_i128_int(r, addr, idx, memop); + gen(TCGV128_LOW(t), TCGV128_LOW(r), TCGV128_LOW(val)); + gen(TCGV128_HIGH(t), TCGV128_HIGH(r), TCGV128_HIGH(val)); + tcg_gen_qemu_st_i128_int(t, addr, idx, memop); + + tcg_gen_mov_i128(ret, r); + tcg_temp_free_i128(t); + tcg_temp_free_i128(r); +} + +static void do_atomic_op_i128(TCGv_i128 ret, TCGTemp *addr, TCGv_i128 val, + TCGArg idx, MemOp memop, void * const table[]) +{ + gen_atomic_op_i128 gen = table[memop & (MO_SIZE | MO_BSWAP)]; + + if (gen) { + MemOpIdx oi = make_memop_idx(memop & ~MO_SIGN, idx); + TCGv_i64 a64 = maybe_extend_addr64(addr); + gen(ret, tcg_env, a64, val, tcg_constant_i32(oi)); + maybe_free_addr64(a64); + return; + } + + gen_helper_exit_atomic(tcg_env); + /* Produce a result */ + tcg_gen_movi_i64(TCGV128_LOW(ret), 0); + tcg_gen_movi_i64(TCGV128_HIGH(ret), 0); +} + +#define GEN_ATOMIC_HELPER128(NAME, OP, NEW) \ +static void * const table_##NAME[(MO_SIZE | MO_BSWAP) + 1] = { \ + [MO_8] = gen_helper_atomic_##NAME##b, \ + [MO_16 | MO_LE] = gen_helper_atomic_##NAME##w_le, \ + [MO_16 | MO_BE] = gen_helper_atomic_##NAME##w_be, \ + [MO_32 | MO_LE] = gen_helper_atomic_##NAME##l_le, \ + [MO_32 | MO_BE] = gen_helper_atomic_##NAME##l_be, \ + WITH_ATOMIC64([MO_64 | MO_LE] = gen_helper_atomic_##NAME##q_le) \ + WITH_ATOMIC64([MO_64 | MO_BE] = gen_helper_atomic_##NAME##q_be) \ + WITH_ATOMIC128([MO_128 | MO_LE] = gen_helper_atomic_##NAME##o_le) \ + WITH_ATOMIC128([MO_128 | MO_BE] = gen_helper_atomic_##NAME##o_be) \ +}; \ +void tcg_gen_atomic_##NAME##_i32_chk(TCGv_i32 ret, TCGTemp *addr, \ + TCGv_i32 val, TCGArg idx, \ + MemOp memop, TCGType addr_type) \ +{ \ + tcg_debug_assert(addr_type == tcg_ctx->addr_type); \ + tcg_debug_assert((memop & MO_SIZE) <= MO_32); \ + if (tcg_ctx->gen_tb->cflags & CF_PARALLEL) { \ + do_atomic_op_i32(ret, addr, val, idx, memop, table_##NAME); \ + } else { \ + do_nonatomic_op_i32(ret, addr, val, idx, memop, NEW, \ + tcg_gen_##OP##_i32); \ + } \ +} \ +void tcg_gen_atomic_##NAME##_i64_chk(TCGv_i64 ret, TCGTemp *addr, \ + TCGv_i64 val, TCGArg idx, \ + MemOp memop, TCGType addr_type) \ +{ \ + tcg_debug_assert(addr_type == tcg_ctx->addr_type); \ + tcg_debug_assert((memop & MO_SIZE) <= MO_64); \ + if (tcg_ctx->gen_tb->cflags & CF_PARALLEL) { \ + do_atomic_op_i64(ret, addr, val, idx, memop, table_##NAME); \ + } else { \ + do_nonatomic_op_i64(ret, addr, val, idx, memop, NEW, \ + tcg_gen_##OP##_i64); \ + } \ +} \ +void tcg_gen_atomic_##NAME##_i128_chk(TCGv_i128 ret, TCGTemp *addr, \ + TCGv_i128 val, TCGArg idx, \ + MemOp memop, TCGType addr_type) \ +{ \ + tcg_debug_assert(addr_type == tcg_ctx->addr_type); \ + tcg_debug_assert((memop & MO_SIZE) == MO_128); \ + if (tcg_ctx->gen_tb->cflags & CF_PARALLEL) { \ + do_atomic_op_i128(ret, addr, val, idx, memop, table_##NAME); \ + } else { \ + do_nonatomic_op_i128(ret, addr, val, idx, memop, NEW, \ + tcg_gen_##OP##_i64); \ + } \ +} + #define GEN_ATOMIC_HELPER(NAME, OP, NEW) \ static void * const table_##NAME[(MO_SIZE | MO_BSWAP) + 1] = { \ [MO_8] = gen_helper_atomic_##NAME##b, \ @@ -1239,8 +1329,8 @@ void tcg_gen_atomic_##NAME##_i64_chk(TCGv_i64 ret, TCGTemp *addr, \ } GEN_ATOMIC_HELPER(fetch_add, add, 0) -GEN_ATOMIC_HELPER(fetch_and, and, 0) -GEN_ATOMIC_HELPER(fetch_or, or, 0) +GEN_ATOMIC_HELPER128(fetch_and, and, 0) +GEN_ATOMIC_HELPER128(fetch_or, or, 0) GEN_ATOMIC_HELPER(fetch_xor, xor, 0) GEN_ATOMIC_HELPER(fetch_smin, smin, 0) GEN_ATOMIC_HELPER(fetch_umin, umin, 0) @@ -1266,6 +1356,7 @@ static void tcg_gen_mov2_i64(TCGv_i64 r, TCGv_i64 a, TCGv_i64 b) tcg_gen_mov_i64(r, b); } -GEN_ATOMIC_HELPER(xchg, mov2, 0) +GEN_ATOMIC_HELPER128(xchg, mov2, 0) #undef GEN_ATOMIC_HELPER +#undef GEN_ATOMIC_HELPER128 From 905c2c34fe3432879f31ef0aa64ce478e573276b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 30 Aug 2025 14:50:06 +1000 Subject: [PATCH 0256/2396] target/arm: Rename isar_feature_aa64_atomics This is FEAT_LSE -- rename the predicate to match. Signed-off-by: Richard Henderson Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell Message-id: 20250830045006.380393-1-richard.henderson@linaro.org Message-id: 20250815122653.701782-6-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- bsd-user/aarch64/target_arch_elf.h | 2 +- linux-user/aarch64/elfload.c | 2 +- target/arm/cpu-features.h | 2 +- target/arm/tcg/translate-a64.c | 24 ++++++++++++------------ 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/bsd-user/aarch64/target_arch_elf.h b/bsd-user/aarch64/target_arch_elf.h index cc87f475b3..cec254f88b 100644 --- a/bsd-user/aarch64/target_arch_elf.h +++ b/bsd-user/aarch64/target_arch_elf.h @@ -114,7 +114,7 @@ static uint32_t get_elf_hwcap(void) GET_FEATURE_ID(aa64_sm3, ARM_HWCAP_A64_SM3); GET_FEATURE_ID(aa64_sm4, ARM_HWCAP_A64_SM4); GET_FEATURE_ID(aa64_fp16, ARM_HWCAP_A64_FPHP | ARM_HWCAP_A64_ASIMDHP); - GET_FEATURE_ID(aa64_atomics, ARM_HWCAP_A64_ATOMICS); + GET_FEATURE_ID(aa64_lse, ARM_HWCAP_A64_ATOMICS); GET_FEATURE_ID(aa64_rdm, ARM_HWCAP_A64_ASIMDRDM); GET_FEATURE_ID(aa64_dp, ARM_HWCAP_A64_ASIMDDP); GET_FEATURE_ID(aa64_fcma, ARM_HWCAP_A64_FCMA); diff --git a/linux-user/aarch64/elfload.c b/linux-user/aarch64/elfload.c index dd5f34398a..8bf39c4730 100644 --- a/linux-user/aarch64/elfload.c +++ b/linux-user/aarch64/elfload.c @@ -154,7 +154,7 @@ abi_ulong get_elf_hwcap(CPUState *cs) GET_FEATURE_ID(aa64_sm3, ARM_HWCAP_A64_SM3); GET_FEATURE_ID(aa64_sm4, ARM_HWCAP_A64_SM4); GET_FEATURE_ID(aa64_fp16, ARM_HWCAP_A64_FPHP | ARM_HWCAP_A64_ASIMDHP); - GET_FEATURE_ID(aa64_atomics, ARM_HWCAP_A64_ATOMICS); + GET_FEATURE_ID(aa64_lse, ARM_HWCAP_A64_ATOMICS); GET_FEATURE_ID(aa64_lse2, ARM_HWCAP_A64_USCAT); GET_FEATURE_ID(aa64_rdm, ARM_HWCAP_A64_ASIMDRDM); GET_FEATURE_ID(aa64_dp, ARM_HWCAP_A64_ASIMDDP); diff --git a/target/arm/cpu-features.h b/target/arm/cpu-features.h index d48754bcf2..451b37b5b3 100644 --- a/target/arm/cpu-features.h +++ b/target/arm/cpu-features.h @@ -406,7 +406,7 @@ static inline bool isar_feature_aa64_crc32(const ARMISARegisters *id) return FIELD_EX64_IDREG(id, ID_AA64ISAR0, CRC32) != 0; } -static inline bool isar_feature_aa64_atomics(const ARMISARegisters *id) +static inline bool isar_feature_aa64_lse(const ARMISARegisters *id) { return FIELD_EX64_IDREG(id, ID_AA64ISAR0, ATOMIC) >= 2; } diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 259aa70a36..0ba537268c 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -3237,7 +3237,7 @@ static bool trans_LDXP(DisasContext *s, arg_stxr *a) static bool trans_CASP(DisasContext *s, arg_CASP *a) { - if (!dc_isar_feature(aa64_atomics, s)) { + if (!dc_isar_feature(aa64_lse, s)) { return false; } if (((a->rt | a->rs) & 1) != 0) { @@ -3250,7 +3250,7 @@ static bool trans_CASP(DisasContext *s, arg_CASP *a) static bool trans_CAS(DisasContext *s, arg_CAS *a) { - if (!dc_isar_feature(aa64_atomics, s)) { + if (!dc_isar_feature(aa64_lse, s)) { return false; } gen_compare_and_swap(s, a->rs, a->rt, a->rn, a->sz); @@ -3743,15 +3743,15 @@ static bool do_atomic_ld(DisasContext *s, arg_atomic *a, AtomicThreeOpFn *fn, return true; } -TRANS_FEAT(LDADD, aa64_atomics, do_atomic_ld, a, tcg_gen_atomic_fetch_add_i64, 0, false) -TRANS_FEAT(LDCLR, aa64_atomics, do_atomic_ld, a, tcg_gen_atomic_fetch_and_i64, 0, true) -TRANS_FEAT(LDEOR, aa64_atomics, do_atomic_ld, a, tcg_gen_atomic_fetch_xor_i64, 0, false) -TRANS_FEAT(LDSET, aa64_atomics, do_atomic_ld, a, tcg_gen_atomic_fetch_or_i64, 0, false) -TRANS_FEAT(LDSMAX, aa64_atomics, do_atomic_ld, a, tcg_gen_atomic_fetch_smax_i64, MO_SIGN, false) -TRANS_FEAT(LDSMIN, aa64_atomics, do_atomic_ld, a, tcg_gen_atomic_fetch_smin_i64, MO_SIGN, false) -TRANS_FEAT(LDUMAX, aa64_atomics, do_atomic_ld, a, tcg_gen_atomic_fetch_umax_i64, 0, false) -TRANS_FEAT(LDUMIN, aa64_atomics, do_atomic_ld, a, tcg_gen_atomic_fetch_umin_i64, 0, false) -TRANS_FEAT(SWP, aa64_atomics, do_atomic_ld, a, tcg_gen_atomic_xchg_i64, 0, false) +TRANS_FEAT(LDADD, aa64_lse, do_atomic_ld, a, tcg_gen_atomic_fetch_add_i64, 0, false) +TRANS_FEAT(LDCLR, aa64_lse, do_atomic_ld, a, tcg_gen_atomic_fetch_and_i64, 0, true) +TRANS_FEAT(LDEOR, aa64_lse, do_atomic_ld, a, tcg_gen_atomic_fetch_xor_i64, 0, false) +TRANS_FEAT(LDSET, aa64_lse, do_atomic_ld, a, tcg_gen_atomic_fetch_or_i64, 0, false) +TRANS_FEAT(LDSMAX, aa64_lse, do_atomic_ld, a, tcg_gen_atomic_fetch_smax_i64, MO_SIGN, false) +TRANS_FEAT(LDSMIN, aa64_lse, do_atomic_ld, a, tcg_gen_atomic_fetch_smin_i64, MO_SIGN, false) +TRANS_FEAT(LDUMAX, aa64_lse, do_atomic_ld, a, tcg_gen_atomic_fetch_umax_i64, 0, false) +TRANS_FEAT(LDUMIN, aa64_lse, do_atomic_ld, a, tcg_gen_atomic_fetch_umin_i64, 0, false) +TRANS_FEAT(SWP, aa64_lse, do_atomic_ld, a, tcg_gen_atomic_xchg_i64, 0, false) static bool trans_LDAPR(DisasContext *s, arg_LDAPR *a) { @@ -3759,7 +3759,7 @@ static bool trans_LDAPR(DisasContext *s, arg_LDAPR *a) TCGv_i64 clean_addr; MemOp mop; - if (!dc_isar_feature(aa64_atomics, s) || + if (!dc_isar_feature(aa64_lse, s) || !dc_isar_feature(aa64_rcpc_8_3, s)) { return false; } From 99e441107efb34a8af990cedbfbda0567d0bb387 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 15 Aug 2025 22:26:52 +1000 Subject: [PATCH 0257/2396] target/arm: Implement FEAT_LSE128 This feature contains the LDCLRP, LDSETP, and SWPP instructions. Signed-off-by: Richard Henderson Reviewed-by: Peter Maydell Message-id: 20250815122653.701782-7-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/cpu-features.h | 5 ++++ target/arm/tcg/a64.decode | 7 +++++ target/arm/tcg/translate-a64.c | 49 ++++++++++++++++++++++++++++++++++ 3 files changed, 61 insertions(+) diff --git a/target/arm/cpu-features.h b/target/arm/cpu-features.h index 451b37b5b3..e49e0ae3af 100644 --- a/target/arm/cpu-features.h +++ b/target/arm/cpu-features.h @@ -411,6 +411,11 @@ static inline bool isar_feature_aa64_lse(const ARMISARegisters *id) return FIELD_EX64_IDREG(id, ID_AA64ISAR0, ATOMIC) >= 2; } +static inline bool isar_feature_aa64_lse128(const ARMISARegisters *id) +{ + return FIELD_EX64_IDREG(id, ID_AA64ISAR0, ATOMIC) >= 3; +} + static inline bool isar_feature_aa64_rdm(const ARMISARegisters *id) { return FIELD_EX64_IDREG(id, ID_AA64ISAR0, RDM) != 0; diff --git a/target/arm/tcg/a64.decode b/target/arm/tcg/a64.decode index 766c610c01..55ff6c504f 100644 --- a/target/arm/tcg/a64.decode +++ b/target/arm/tcg/a64.decode @@ -546,6 +546,13 @@ SWP .. 111 0 00 . . 1 ..... 1000 00 ..... ..... @atomic LDAPR sz:2 111 0 00 1 0 1 11111 1100 00 rn:5 rt:5 +# Atomic 128-bit memory operations +&atomic128 rn rt rt2 a r +@atomic128 ........ a:1 r:1 . rt2:5 ...... rn:5 rt:5 &atomic128 +LDCLRP 00011001 . . 1 ..... 000100 ..... ..... @atomic128 +LDSETP 00011001 . . 1 ..... 001100 ..... ..... @atomic128 +SWPP 00011001 . . 1 ..... 100000 ..... ..... @atomic128 + # Load/store register (pointer authentication) # LDRA immediate is 10 bits signed and scaled, but the bits aren't all contiguous diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 0ba537268c..37bedc3780 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -3753,6 +3753,55 @@ TRANS_FEAT(LDUMAX, aa64_lse, do_atomic_ld, a, tcg_gen_atomic_fetch_umax_i64, 0, TRANS_FEAT(LDUMIN, aa64_lse, do_atomic_ld, a, tcg_gen_atomic_fetch_umin_i64, 0, false) TRANS_FEAT(SWP, aa64_lse, do_atomic_ld, a, tcg_gen_atomic_xchg_i64, 0, false) +typedef void Atomic128ThreeOpFn(TCGv_i128, TCGv_i64, TCGv_i128, TCGArg, MemOp); + +static bool do_atomic128_ld(DisasContext *s, arg_atomic128 *a, + Atomic128ThreeOpFn *fn, bool invert) +{ + MemOp mop; + int rlo, rhi; + TCGv_i64 clean_addr, tlo, thi; + TCGv_i128 t16; + + if (a->rt == 31 || a->rt2 == 31 || a->rt == a->rt2) { + return false; + } + if (a->rn == 31) { + gen_check_sp_alignment(s); + } + mop = check_atomic_align(s, a->rn, MO_128); + clean_addr = gen_mte_check1(s, cpu_reg_sp(s, a->rn), false, + a->rn != 31, mop); + + rlo = (s->be_data == MO_LE ? a->rt : a->rt2); + rhi = (s->be_data == MO_LE ? a->rt2 : a->rt); + + tlo = read_cpu_reg(s, rlo, true); + thi = read_cpu_reg(s, rhi, true); + if (invert) { + tcg_gen_not_i64(tlo, tlo); + tcg_gen_not_i64(thi, thi); + } + /* + * The tcg atomic primitives are all full barriers. Therefore we + * can ignore the Acquire and Release bits of this instruction. + */ + t16 = tcg_temp_new_i128(); + tcg_gen_concat_i64_i128(t16, tlo, thi); + + fn(t16, clean_addr, t16, get_mem_index(s), mop); + + tcg_gen_extr_i128_i64(cpu_reg(s, rlo), cpu_reg(s, rhi), t16); + return true; +} + +TRANS_FEAT(LDCLRP, aa64_lse128, do_atomic128_ld, + a, tcg_gen_atomic_fetch_and_i128, true) +TRANS_FEAT(LDSETP, aa64_lse128, do_atomic128_ld, + a, tcg_gen_atomic_fetch_or_i128, false) +TRANS_FEAT(SWPP, aa64_lse128, do_atomic128_ld, + a, tcg_gen_atomic_xchg_i128, false) + static bool trans_LDAPR(DisasContext *s, arg_LDAPR *a) { bool iss_sf = ldst_iss_sf(a->sz, false, false); From 23f5b02447503d9e28f6fad690ccce850c7bb656 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 15 Aug 2025 22:26:53 +1000 Subject: [PATCH 0258/2396] target/arm: Enable FEAT_LSE128 for -cpu max Signed-off-by: Richard Henderson Reviewed-by: Peter Maydell Message-id: 20250815122653.701782-8-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- docs/system/arm/emulation.rst | 1 + linux-user/aarch64/elfload.c | 1 + target/arm/tcg/cpu64.c | 2 +- 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/system/arm/emulation.rst b/docs/system/arm/emulation.rst index b12f013b4f..4e8aca8b5d 100644 --- a/docs/system/arm/emulation.rst +++ b/docs/system/arm/emulation.rst @@ -89,6 +89,7 @@ the following architecture extensions: - FEAT_LRCPC2 (Load-acquire RCpc instructions v2) - FEAT_LSE (Large System Extensions) - FEAT_LSE2 (Large System Extensions v2) +- FEAT_LSE128 (128-bit Atomics) - FEAT_LVA (Large Virtual Address space) - FEAT_MixedEnd (Mixed-endian support) - FEAT_MixedEndEL0 (Mixed-endian support at EL0) diff --git a/linux-user/aarch64/elfload.c b/linux-user/aarch64/elfload.c index 8bf39c4730..77d03b50e1 100644 --- a/linux-user/aarch64/elfload.c +++ b/linux-user/aarch64/elfload.c @@ -216,6 +216,7 @@ abi_ulong get_elf_hwcap2(CPUState *cs) GET_FEATURE_ID(aa64_sme_f16f16, ARM_HWCAP2_A64_SME_F16F16); GET_FEATURE_ID(aa64_sve_b16b16, ARM_HWCAP2_A64_SVE_B16B16); GET_FEATURE_ID(aa64_cssc, ARM_HWCAP2_A64_CSSC); + GET_FEATURE_ID(aa64_lse128, ARM_HWCAP2_A64_LSE128); return hwcaps; } diff --git a/target/arm/tcg/cpu64.c b/target/arm/tcg/cpu64.c index eaf8846a6a..b8b1981e70 100644 --- a/target/arm/tcg/cpu64.c +++ b/target/arm/tcg/cpu64.c @@ -1145,7 +1145,7 @@ void aarch64_max_tcg_initfn(Object *obj) t = FIELD_DP64(t, ID_AA64ISAR0, SHA1, 1); /* FEAT_SHA1 */ t = FIELD_DP64(t, ID_AA64ISAR0, SHA2, 2); /* FEAT_SHA512 */ t = FIELD_DP64(t, ID_AA64ISAR0, CRC32, 1); /* FEAT_CRC32 */ - t = FIELD_DP64(t, ID_AA64ISAR0, ATOMIC, 2); /* FEAT_LSE */ + t = FIELD_DP64(t, ID_AA64ISAR0, ATOMIC, 3); /* FEAT_LSE, FEAT_LSE128 */ t = FIELD_DP64(t, ID_AA64ISAR0, RDM, 1); /* FEAT_RDM */ t = FIELD_DP64(t, ID_AA64ISAR0, SHA3, 1); /* FEAT_SHA3 */ t = FIELD_DP64(t, ID_AA64ISAR0, SM3, 1); /* FEAT_SM3 */ From 2e27650bddd35477d994a795a3b1cb57c8ed5c76 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 21 Aug 2025 16:42:29 +0100 Subject: [PATCH 0259/2396] hw/arm/stm32f205_soc: Don't leak TYPE_OR_IRQ objects MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In stm32f250_soc_initfn() we mostly use the standard pattern for child objects of calling object_initialize_child(). However for s->adc_irqs we call object_new() and then later qdev_realize(), and we never unref the object on deinit. This causes a leak, detected by ASAN on the device-introspect-test: Indirect leak of 10 byte(s) in 1 object(s) allocated from: #0 0x5b9fc4789de3 in malloc (/mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/arm-asan/qemu-system-arm+0x21f1de3) (BuildId: 267a2619a026ed91c78a07b1eb2ef15381538efe) #1 0x740de3f28b09 in g_malloc (/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x62b09) (BuildId: 1eb6131419edb83b2178b682829a6913cf682d75) #2 0x740de3f3e4d8 in g_strdup (/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x784d8) (BuildId: 1eb6131419edb83b2178b682829a6913cf682d75) #3 0x5b9fc70159e1 in g_strdup_inline /usr/include/glib-2.0/glib/gstrfuncs.h:321:10 #4 0x5b9fc70159e1 in object_property_try_add /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/arm-asan/../../qom/object.c:1276:18 #5 0x5b9fc7015f94 in object_property_add /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/arm-asan/../../qom/object.c:1294:12 #6 0x5b9fc701b900 in object_add_link_prop /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/arm-asan/../../qom/object.c:2021:10 #7 0x5b9fc701b3fc in object_property_add_link /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/arm-asan/../../qom/object.c:2037:12 #8 0x5b9fc4c299fb in qdev_init_gpio_out_named /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/arm-asan/../../hw/core/gpio.c:90:9 #9 0x5b9fc4c29b26 in qdev_init_gpio_out /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/arm-asan/../../hw/core/gpio.c:101:5 #10 0x5b9fc4c0f77a in or_irq_init /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/arm-asan/../../hw/core/or-irq.c:70:5 #11 0x5b9fc70257e1 in object_init_with_type /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/arm-asan/../../qom/object.c:428:9 #12 0x5b9fc700cd4b in object_initialize_with_type /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/arm-asan/../../qom/object.c:570:5 #13 0x5b9fc700e66d in object_new_with_type /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/arm-asan/../../qom/object.c:774:5 #14 0x5b9fc700e750 in object_new /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/arm-asan/../../qom/object.c:789:12 #15 0x5b9fc68b2162 in stm32f205_soc_initfn /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/arm-asan/../../hw/arm/stm32f205_soc.c:69:26 Switch to using object_initialize_child() like all our other child objects for this SoC object. Cc: qemu-stable@nongnu.org Fixes: b63041c8f6b ("STM32F205: Connect the ADC devices") Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Message-id: 20250821154229.2417453-1-peter.maydell@linaro.org --- hw/arm/stm32f205_soc.c | 10 +++++----- include/hw/arm/stm32f205_soc.h | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/hw/arm/stm32f205_soc.c b/hw/arm/stm32f205_soc.c index 229af7fb10..e3c7203c6e 100644 --- a/hw/arm/stm32f205_soc.c +++ b/hw/arm/stm32f205_soc.c @@ -66,7 +66,7 @@ static void stm32f205_soc_initfn(Object *obj) TYPE_STM32F2XX_TIMER); } - s->adc_irqs = OR_IRQ(object_new(TYPE_OR_IRQ)); + object_initialize_child(obj, "adc-irq-orgate", &s->adc_irqs, TYPE_OR_IRQ); for (i = 0; i < STM_NUM_ADCS; i++) { object_initialize_child(obj, "adc[*]", &s->adc[i], TYPE_STM32F2XX_ADC); @@ -171,12 +171,12 @@ static void stm32f205_soc_realize(DeviceState *dev_soc, Error **errp) } /* ADC 1 to 3 */ - object_property_set_int(OBJECT(s->adc_irqs), "num-lines", STM_NUM_ADCS, + object_property_set_int(OBJECT(&s->adc_irqs), "num-lines", STM_NUM_ADCS, &error_abort); - if (!qdev_realize(DEVICE(s->adc_irqs), NULL, errp)) { + if (!qdev_realize(DEVICE(&s->adc_irqs), NULL, errp)) { return; } - qdev_connect_gpio_out(DEVICE(s->adc_irqs), 0, + qdev_connect_gpio_out(DEVICE(&s->adc_irqs), 0, qdev_get_gpio_in(armv7m, ADC_IRQ)); for (i = 0; i < STM_NUM_ADCS; i++) { @@ -187,7 +187,7 @@ static void stm32f205_soc_realize(DeviceState *dev_soc, Error **errp) busdev = SYS_BUS_DEVICE(dev); sysbus_mmio_map(busdev, 0, adc_addr[i]); sysbus_connect_irq(busdev, 0, - qdev_get_gpio_in(DEVICE(s->adc_irqs), i)); + qdev_get_gpio_in(DEVICE(&s->adc_irqs), i)); } /* SPI 1 and 2 */ diff --git a/include/hw/arm/stm32f205_soc.h b/include/hw/arm/stm32f205_soc.h index 4f4c8bbebc..46eda3403a 100644 --- a/include/hw/arm/stm32f205_soc.h +++ b/include/hw/arm/stm32f205_soc.h @@ -59,7 +59,7 @@ struct STM32F205State { STM32F2XXADCState adc[STM_NUM_ADCS]; STM32F2XXSPIState spi[STM_NUM_SPIS]; - OrIRQState *adc_irqs; + OrIRQState adc_irqs; MemoryRegion sram; MemoryRegion flash; From a12ff7f807e3ae2e254ac1de45c3892831479a0f Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Wed, 23 Jul 2025 15:15:04 +0200 Subject: [PATCH 0260/2396] ui/keymaps: Avoid trace crash and improve error messages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit parse_keyboard_layout() passes a possibly null @filename to trace_keymap_parse(). Trace backend log then formats it with %s, which crashes on some systems. Fix by moving the null check before the trace_keymap_parse(). While there, improve the error messages a bit. Fixes: d3b787fa7dde (keymaps: add tracing) Signed-off-by: Markus Armbruster Message-ID: <20250723131504.1482657-1-armbru@redhat.com> Reviewed-by: Marc-André Lureau Reviewed-by: Philippe Mathieu-Daudé --- ui/keymaps.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/ui/keymaps.c b/ui/keymaps.c index 6ceaa97085..2359dbfe7e 100644 --- a/ui/keymaps.c +++ b/ui/keymaps.c @@ -86,19 +86,25 @@ static int parse_keyboard_layout(kbd_layout_t *k, const name2keysym_t *table, const char *language, Error **errp) { + g_autofree char *filename = NULL; int ret; FILE *f; - char * filename; char line[1024]; char keyname[64]; int len; filename = qemu_find_file(QEMU_FILE_TYPE_KEYMAP, language); + if (!filename) { + error_setg(errp, "could not find keymap file for language '%s'", + language); + return -1; + } + trace_keymap_parse(filename); - f = filename ? fopen(filename, "r") : NULL; - g_free(filename); + + f = fopen(filename, "r"); if (!f) { - error_setg(errp, "could not read keymap file: '%s'", language); + error_setg_file_open(errp, errno, filename); return -1; } From 83f6dceb8f5c0a1efe806a9a4905cbc743a8a378 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Wed, 30 Jul 2025 09:27:09 +0200 Subject: [PATCH 0261/2396] qga: Fix ubsan warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When compiling QEMU with --enable-ubsan there is a undefined behavior warning when running "make check": .../qga/commands-linux.c:452:15: runtime error: applying non-zero offset 5 to null pointer #0 0x55ea7b89450c in build_guest_fsinfo_for_pci_dev ..../qga/commands-linux.c:452:15 Fix it by avoiding the additional pointer variable here and use an "offset" integer variable instead. Signed-off-by: Thomas Huth Reviewed-by: Daniel P. Berrangé Reviewed-by: Kostiantyn Kostiuk Link: https://lore.kernel.org/qemu-devel/20250730072709.27077-1-thuth@redhat.com Signed-off-by: Kostiantyn Kostiuk --- qga/commands-linux.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/qga/commands-linux.c b/qga/commands-linux.c index 9dc0c82503..4a09ddc760 100644 --- a/qga/commands-linux.c +++ b/qga/commands-linux.c @@ -400,10 +400,10 @@ static bool build_guest_fsinfo_for_pci_dev(char const *syspath, Error **errp) { unsigned int pci[4], host, hosts[8], tgt[3]; - int i, nhosts = 0, pcilen; + int i, offset, nhosts = 0, pcilen; GuestPCIAddress *pciaddr = disk->pci_controller; bool has_ata = false, has_host = false, has_tgt = false; - char *p, *q, *driver = NULL; + char *p, *driver = NULL; bool ret = false; p = strstr(syspath, "/devices/pci"); @@ -445,13 +445,13 @@ static bool build_guest_fsinfo_for_pci_dev(char const *syspath, p = strstr(syspath, "/ata"); if (p) { - q = p + 4; + offset = 4; has_ata = true; } else { p = strstr(syspath, "/host"); - q = p + 5; + offset = 5; } - if (p && sscanf(q, "%u", &host) == 1) { + if (p && sscanf(p + offset, "%u", &host) == 1) { has_host = true; nhosts = build_hosts(syspath, p, has_ata, hosts, ARRAY_SIZE(hosts), errp); From 42bdb911c22f9449f7a310efc73b70548ca42b24 Mon Sep 17 00:00:00 2001 From: "Denis V. Lunev" Date: Thu, 7 Aug 2025 15:32:21 +0200 Subject: [PATCH 0262/2396] qga: fix potentially not initialized nr_volumes in qga_vss_fsfreeze() In this function we could have this variable not initialized. If this could be acceptable on error, the variable could be left not initialized f.e. as follows: void requester_freeze(int *num_vols, void *mountpoints, ErrorSet *errset) { ... if (mountpoints) { ... if (num_mount_points == 0) { /* If there is no valid mount points, just exit. */ goto out; } } ... if (!mountpoints) { ... if (num_fixed_drives == 0) { goto out; /* If there is no fixed drive, just exit. */ } } ... } Stay on safe side, initialize the variable at the beginning. Signed-off-by: Denis V. Lunev CC: Kostiantyn Kostiuk CC: Michael Roth Reviewed-by: Kostiantyn Kostiuk Link: https://lore.kernel.org/qemu-devel/20250807133221.1135453-1-den@openvz.org Signed-off-by: Kostiantyn Kostiuk --- qga/vss-win32.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/qga/vss-win32.c b/qga/vss-win32.c index f444a25a70..b272bfc782 100644 --- a/qga/vss-win32.c +++ b/qga/vss-win32.c @@ -157,6 +157,8 @@ void qga_vss_fsfreeze(int *nr_volume, bool freeze, .errp = errp, }; + *nr_volume = 0; + g_assert(errp); /* requester.cpp requires it */ func = (QGAVSSRequesterFunc)GetProcAddress(provider_lib, func_name); if (!func) { From 9646155bb01c076f982601b1ebdead73efcb58a1 Mon Sep 17 00:00:00 2001 From: Kostiantyn Kostiuk Date: Mon, 25 Aug 2025 17:52:40 +0300 Subject: [PATCH 0263/2396] qga-vss: Replace asserts with condition and report error Reviewed-by: Yan Vugenfirer Link: https://lore.kernel.org/qemu-devel/20250825145241.170717-2-kkostiuk@redhat.com Signed-off-by: Kostiantyn Kostiuk --- qga/vss-win32/requester.cpp | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/qga/vss-win32/requester.cpp b/qga/vss-win32/requester.cpp index 4401d55e3a..bc260abb96 100644 --- a/qga/vss-win32/requester.cpp +++ b/qga/vss-win32/requester.cpp @@ -347,7 +347,12 @@ void requester_freeze(int *num_vols, void *mountpoints, ErrorSet *errset) goto out; } - assert(pCreateVssBackupComponents != NULL); + if (!pCreateVssBackupComponents) { + err_set(errset, (HRESULT)ERROR_PROC_NOT_FOUND, + "CreateVssBackupComponents proc address absent. Did you call requester_init()?"); + goto out; + } + hr = pCreateVssBackupComponents(&vss_ctx.pVssbc); if (FAILED(hr)) { err_set(errset, hr, "failed to create VSS backup components"); @@ -579,8 +584,16 @@ void requester_thaw(int *num_vols, void *mountpints, ErrorSet *errset) /* Tell the provider that the snapshot is finished. */ SetEvent(vss_ctx.hEventThaw); - assert(vss_ctx.pVssbc); - assert(vss_ctx.pAsyncSnapshot); + if (!vss_ctx.pVssbc) { + err_set(errset, (HRESULT)VSS_E_BAD_STATE, + "CreateVssBackupComponents is missing. Did you freeze the volumes?"); + return; + } + if (!vss_ctx.pAsyncSnapshot) { + err_set(errset, (HRESULT)VSS_E_BAD_STATE, + "AsyncSnapshot set is missing. Did you freeze the volumes?"); + return; + } HRESULT hr = WaitForAsync(vss_ctx.pAsyncSnapshot); switch (hr) { From ed42682a66ef9f5e892abb7bcc3f211e1180f075 Mon Sep 17 00:00:00 2001 From: Kostiantyn Kostiuk Date: Mon, 25 Aug 2025 17:52:41 +0300 Subject: [PATCH 0264/2396] qga-vss: Remove unused dependencies Reviewed-by: Yan Vugenfirer Link: https://lore.kernel.org/qemu-devel/20250825145241.170717-3-kkostiuk@redhat.com Signed-off-by: Kostiantyn Kostiuk --- qga/vss-win32/meson.build | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/qga/vss-win32/meson.build b/qga/vss-win32/meson.build index 0ac918910b..a6b810f12a 100644 --- a/qga/vss-win32/meson.build +++ b/qga/vss-win32/meson.build @@ -13,13 +13,11 @@ qga_vss = shared_module( link_args: link_args, vs_module_defs: 'qga-vss.def', dependencies: [ - glib, socket, cc.find_library('ole32'), cc.find_library('oleaut32'), cc.find_library('shlwapi'), - cc.find_library('uuid'), - cc.find_library('intl') + cc.find_library('uuid') ] ) From 3b0ba59762380fff9c91a031301f6f661abaac96 Mon Sep 17 00:00:00 2001 From: Kostiantyn Kostiuk Date: Mon, 25 Aug 2025 17:05:48 +0300 Subject: [PATCH 0265/2396] qga: Fix channel initialization check in run_agent_once Reviewed-by: Yan Vugenfirer Reviewed-by: Michal Privoznik Link: https://lore.kernel.org/qemu-devel/20250825140549.146617-2-kkostiuk@redhat.com Signed-off-by: Kostiantyn Kostiuk --- qga/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qga/main.c b/qga/main.c index 6c02f3ec38..a1bf8f53ac 100644 --- a/qga/main.c +++ b/qga/main.c @@ -1563,7 +1563,7 @@ static void cleanup_agent(GAState *s) static int run_agent_once(GAState *s) { if (!s->channel && - channel_init(s, s->config->method, s->config->channel_path, + !channel_init(s, s->config->method, s->config->channel_path, s->socket_activation ? FIRST_SOCKET_ACTIVATION_FD : -1)) { g_critical("failed to initialize guest agent channel"); return EXIT_FAILURE; From b44c8a6d837ed4e082dd03d79095a4e9141eff5b Mon Sep 17 00:00:00 2001 From: Kostiantyn Kostiuk Date: Mon, 25 Aug 2025 17:05:49 +0300 Subject: [PATCH 0266/2396] qga: ignore channel_init() fail if 'retry_path' is set On Windows, we run QGA with `-d --retry-path` options by default, and expect that QGA will start even without the vioserial driver and will wait for communication forever. Reviewed-by: Yan Vugenfirer Reviewed-by: Michal Privoznik Link: https://lore.kernel.org/qemu-devel/20250825140549.146617-3-kkostiuk@redhat.com Signed-off-by: Kostiantyn Kostiuk --- qga/main.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/qga/main.c b/qga/main.c index a1bf8f53ac..dd1c216f9a 100644 --- a/qga/main.c +++ b/qga/main.c @@ -1512,8 +1512,12 @@ static GAState *initialize_agent(GAConfig *config, int socket_activation) if (!channel_init(s, s->config->method, s->config->channel_path, s->socket_activation ? FIRST_SOCKET_ACTIVATION_FD : -1)) { - g_critical("failed to initialize guest agent channel"); - return NULL; + if (s->config->retry_path) { + g_info("failed to initialize guest agent channel, will retry"); + } else { + g_critical("failed to initialize guest agent channel"); + return NULL; + } } if (config->daemonize) { From edf3780a7dad4658ab7b72ea37e310a2be9b16d3 Mon Sep 17 00:00:00 2001 From: Kostiantyn Kostiuk Date: Mon, 25 Aug 2025 16:53:11 +0300 Subject: [PATCH 0267/2396] qga-vss: Write hex value of error in log QGA-VSS writes error using error_setg_win32_internal, which call g_win32_error_message. g_win32_error_message - translate a Win32 error code (as returned by GetLastError()) into the corresponding message. In the same time, we call error_setg_win32_internal with error codes from different Windows componets like VSS or Performance monitor that provides different codes and can't be converted with g_win32_error_message. In this case, the empty suffix will be returned so error will be masked. This commit directly add hex value of error code. Reproduce: - Run QGA command: {"execute": "guest-fsfreeze-freeze-list", "arguments": {"mountpoints": ["D:"]}} QGA error example: - before changes: {"error": {"class": "GenericError", "desc": "failed to add D: to snapshot set: "}} - after changes: {"error": {"class": "GenericError", "desc": "failed to add D: to snapshot set: Windows error 0x8004230e: "}} Reviewed-by: Yan Vugenfirer Link: https://lore.kernel.org/qemu-devel/20250825135311.138330-1-kkostiuk@redhat.com Signed-off-by: Kostiantyn Kostiuk --- qga/vss-win32/requester.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/qga/vss-win32/requester.cpp b/qga/vss-win32/requester.cpp index bc260abb96..5615955b6f 100644 --- a/qga/vss-win32/requester.cpp +++ b/qga/vss-win32/requester.cpp @@ -28,8 +28,9 @@ #define err_set(e, err, fmt, ...) { \ (e)->error_setg_win32_wrapper((e)->errp, __FILE__, __LINE__, __func__, \ - err, fmt, ## __VA_ARGS__); \ - qga_debug(fmt, ## __VA_ARGS__); \ + err, fmt ": Windows error 0x%lx", \ + ## __VA_ARGS__, err); \ + qga_debug(fmt ": Windows error 0x%lx", ## __VA_ARGS__, err); \ } /* Bad idea, works only when (e)->errp != NULL: */ #define err_is_set(e) ((e)->errp && *(e)->errp) From 85ff0e956bf26a93c92e4dca8f6257613269a0cf Mon Sep 17 00:00:00 2001 From: Kostiantyn Kostiuk Date: Mon, 25 Aug 2025 17:31:55 +0300 Subject: [PATCH 0268/2396] qga/installer: Remove QGA VSS if QGA installation failed When QGA Installer failed to install QGA service but install QGA VSS provider, provider should be removed before installer exits. Otherwise QGA VSS will has broken infomation and prevent QGA installation in next run. Reviewed-by: Yan Vugenfirer Link: https://lore.kernel.org/qemu-devel/20250825143155.160913-1-kkostiuk@redhat.com Signed-off-by: Kostiantyn Kostiuk --- qga/installer/qemu-ga.wxs | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/qga/installer/qemu-ga.wxs b/qga/installer/qemu-ga.wxs index df572adb4a..32b8308728 100644 --- a/qga/installer/qemu-ga.wxs +++ b/qga/installer/qemu-ga.wxs @@ -151,6 +151,14 @@ Return="check" > + + @@ -174,8 +182,19 @@ - Installed - NOT REMOVE + + + + + + + NOT REMOVE + + + NOT REMOVE + + + Installed From 28c5d27dd4dc4100a96ff4c9e5871dd23c6b02ec Mon Sep 17 00:00:00 2001 From: "minglei.liu" Date: Fri, 11 Jul 2025 10:17:14 +0800 Subject: [PATCH 0269/2396] qga: Fix truncated output handling in guest-exec status reporting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: minglei.liu Fixes: a1853dca743 Reviewed-by: Daniel P. Berrangé Reviewed-by: Kostiantyn Kostiuk Link: https://lore.kernel.org/qemu-devel/20250711021714.91258-1-minglei.liu@smartx.com Signed-off-by: Kostiantyn Kostiuk --- qga/commands.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/qga/commands.c b/qga/commands.c index 5a5fad31f8..5f20af25d3 100644 --- a/qga/commands.c +++ b/qga/commands.c @@ -205,13 +205,15 @@ GuestExecStatus *qmp_guest_exec_status(int64_t pid, Error **errp) #endif if (gei->out.length > 0) { ges->out_data = g_base64_encode(gei->out.data, gei->out.length); - ges->has_out_truncated = gei->out.truncated; + ges->has_out_truncated = true; + ges->out_truncated = gei->out.truncated; } g_free(gei->out.data); if (gei->err.length > 0) { ges->err_data = g_base64_encode(gei->err.data, gei->err.length); - ges->has_err_truncated = gei->err.truncated; + ges->has_err_truncated = true; + ges->err_truncated = gei->err.truncated; } g_free(gei->err.data); From b2e4534a2c9ce3d20ba44d855f1e2b71cc53c3a3 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Wed, 23 Jul 2025 15:32:56 +0200 Subject: [PATCH 0270/2396] i386/kvm/vmsr_energy: Plug memory leak on failure to connect socket vmsr_open_socket() leaks the Error set by qio_channel_socket_connect_sync(). Plug the leak by not creating the Error. Fixes: 0418f90809ae (Add support for RAPL MSRs in KVM/Qemu) Signed-off-by: Markus Armbruster Message-ID: <20250723133257.1497640-2-armbru@redhat.com> Reviewed-by: Zhao Liu --- target/i386/kvm/vmsr_energy.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/target/i386/kvm/vmsr_energy.c b/target/i386/kvm/vmsr_energy.c index 58ce3df53a..890322ae37 100644 --- a/target/i386/kvm/vmsr_energy.c +++ b/target/i386/kvm/vmsr_energy.c @@ -57,13 +57,9 @@ QIOChannelSocket *vmsr_open_socket(const char *path) }; QIOChannelSocket *sioc = qio_channel_socket_new(); - Error *local_err = NULL; qio_channel_set_name(QIO_CHANNEL(sioc), "vmsr-helper"); - qio_channel_socket_connect_sync(sioc, - &saddr, - &local_err); - if (local_err) { + if (qio_channel_socket_connect_sync(sioc, &saddr, NULL) < 0) { /* Close socket. */ qio_channel_close(QIO_CHANNEL(sioc), NULL); object_unref(OBJECT(sioc)); From ec14a3de622ae30a8afa78b6f564bc743b753ee1 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Wed, 23 Jul 2025 15:32:57 +0200 Subject: [PATCH 0271/2396] vfio scsi ui: Error-check qio_channel_socket_connect_sync() the same way qio_channel_socket_connect_sync() returns 0 on success, and -1 on failure, with errp set. Some callers check the return value, and some check whether errp was set. For consistency, always check the return value, and always check it's negative. Signed-off-by: Markus Armbruster Message-ID: <20250723133257.1497640-3-armbru@redhat.com> Reviewed-by: Zhao Liu --- hw/vfio-user/proxy.c | 2 +- scsi/pr-manager-helper.c | 9 ++------- ui/input-barrier.c | 5 +---- 3 files changed, 4 insertions(+), 12 deletions(-) diff --git a/hw/vfio-user/proxy.c b/hw/vfio-user/proxy.c index 2275d3fe39..2c03d49f97 100644 --- a/hw/vfio-user/proxy.c +++ b/hw/vfio-user/proxy.c @@ -885,7 +885,7 @@ VFIOUserProxy *vfio_user_connect_dev(SocketAddress *addr, Error **errp) sioc = qio_channel_socket_new(); ioc = QIO_CHANNEL(sioc); - if (qio_channel_socket_connect_sync(sioc, addr, errp)) { + if (qio_channel_socket_connect_sync(sioc, addr, errp) < 0) { object_unref(OBJECT(ioc)); return NULL; } diff --git a/scsi/pr-manager-helper.c b/scsi/pr-manager-helper.c index 6b86f01b01..aea751fb04 100644 --- a/scsi/pr-manager-helper.c +++ b/scsi/pr-manager-helper.c @@ -105,20 +105,15 @@ static int pr_manager_helper_initialize(PRManagerHelper *pr_mgr, .u.q_unix.path = path }; QIOChannelSocket *sioc = qio_channel_socket_new(); - Error *local_err = NULL; - uint32_t flags; int r; assert(!pr_mgr->ioc); qio_channel_set_name(QIO_CHANNEL(sioc), "pr-manager-helper"); - qio_channel_socket_connect_sync(sioc, - &saddr, - &local_err); + r = qio_channel_socket_connect_sync(sioc, &saddr, errp); g_free(path); - if (local_err) { + if (r < 0) { object_unref(OBJECT(sioc)); - error_propagate(errp, local_err); return -ENOTCONN; } diff --git a/ui/input-barrier.c b/ui/input-barrier.c index 9793258aac..0a2198ca50 100644 --- a/ui/input-barrier.c +++ b/ui/input-barrier.c @@ -490,7 +490,6 @@ static gboolean input_barrier_event(QIOChannel *ioc G_GNUC_UNUSED, static void input_barrier_complete(UserCreatable *uc, Error **errp) { InputBarrier *ib = INPUT_BARRIER(uc); - Error *local_err = NULL; if (!ib->name) { error_setg(errp, QERR_MISSING_PARAMETER, "name"); @@ -506,9 +505,7 @@ static void input_barrier_complete(UserCreatable *uc, Error **errp) ib->sioc = qio_channel_socket_new(); qio_channel_set_name(QIO_CHANNEL(ib->sioc), "barrier-client"); - qio_channel_socket_connect_sync(ib->sioc, &ib->saddr, &local_err); - if (local_err) { - error_propagate(errp, local_err); + if (qio_channel_socket_connect_sync(ib->sioc, &ib->saddr, errp) < 0) { return; } From a60e1544b515e5205489141fc76cdf26de3a5f1b Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Fri, 25 Jul 2025 15:50:31 +0200 Subject: [PATCH 0272/2396] qtest/qom-test: Shallow testing of qom-list / qom-get This test traverses the QOM sub-tree rooted at /machine with a combination of qom-list and qom-get. In my x86_64 testing, it runs almost 12000 QMP commands in 34 seconds. With -m slow, we test more machines, and it takes almost 84000 commands in almost four minutes. Since commit 3dd93992ffb (tests/qtest/qom-test: unit test for qom-list-get), the test traverses this tree a second time, with qom-list-get. In my x86_64 testing, this takes some 200 QMP commands and around two seconds, and some 1100 in just under 12s with -m slow. Traversing the entire tree is useful, because it exercise the QOM property getters. Traversing it twice not so much. Make the qom-list / qom-get test shallow unless -m slow is given: don't recurse. Cuts the number of commands to around 600, and run time to under 5s for me. Signed-off-by: Markus Armbruster Message-ID: <20250725135034.2280477-3-armbru@redhat.com> Reviewed-by: Steve Sistare --- tests/qtest/qom-test.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/qtest/qom-test.c b/tests/qtest/qom-test.c index 4ade1c728c..7dea0d802d 100644 --- a/tests/qtest/qom-test.c +++ b/tests/qtest/qom-test.c @@ -180,7 +180,7 @@ static void test_properties(QTestState *qts, const char *path, bool recurse) links = g_slist_delete_link(links, links); } while (children) { - test_properties(qts, children->data, true); + test_properties(qts, children->data, g_test_slow()); g_free(children->data); children = g_slist_delete_link(children, children); } From 67a392f7cf2b1662c21fb609b25bd745d25a2d05 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Fri, 25 Jul 2025 15:50:32 +0200 Subject: [PATCH 0273/2396] qtest/qom-test: Traverse entire QOM tree This test traverses the QOM sub-tree rooted at /machine. Traverse the entire tree instead. The x86_64 test runs some 40 additional QMP commands, and stays under 5s for me. Signed-off-by: Markus Armbruster Message-ID: <20250725135034.2280477-4-armbru@redhat.com> Reviewed-by: Steve Sistare --- tests/qtest/qom-test.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/qtest/qom-test.c b/tests/qtest/qom-test.c index 7dea0d802d..a2db56bf22 100644 --- a/tests/qtest/qom-test.c +++ b/tests/qtest/qom-test.c @@ -211,7 +211,7 @@ static void test_machine(gconstpointer data) test_properties(qts, "/machine", true); - qlist_append_str(paths, "/machine"); + qlist_append_str(paths, "/"); test_list_get(qts, paths); test_list_get_value(qts); From c4c3ee8c1de5f04b4a34b53c2f3d6e85f5a2fe6f Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Fri, 25 Jul 2025 15:50:33 +0200 Subject: [PATCH 0274/2396] qtest/qom-test: Don't bother to execute QMP command quit Signed-off-by: Markus Armbruster Message-ID: <20250725135034.2280477-5-armbru@redhat.com> Reviewed-by: Steve Sistare --- tests/qtest/qom-test.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/tests/qtest/qom-test.c b/tests/qtest/qom-test.c index a2db56bf22..2da9918e16 100644 --- a/tests/qtest/qom-test.c +++ b/tests/qtest/qom-test.c @@ -215,10 +215,6 @@ static void test_machine(gconstpointer data) test_list_get(qts, paths); test_list_get_value(qts); - response = qtest_qmp(qts, "{ 'execute': 'quit' }"); - g_assert(qdict_haskey(response, "return")); - qobject_unref(response); - qtest_quit(qts); g_free((void *)machine); } From f0f682675c9b95ef762cec8dd1b868171a3dda67 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Fri, 25 Jul 2025 15:50:34 +0200 Subject: [PATCH 0275/2396] MAINTAINERS: Cover tests/qtest/qom-test.c Signed-off-by: Markus Armbruster Message-ID: <20250725135034.2280477-6-armbru@redhat.com> --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 8147fff352..0207946537 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3454,6 +3454,7 @@ F: qom/ F: tests/unit/check-qom-interface.c F: tests/unit/check-qom-proplist.c F: tests/unit/test-qdev-global-props.c +F: tests/qtest/qom-test.c QOM boilerplate conversion script M: Eduardo Habkost From c9a1ea9c52e6462ad5c7814f3abd65baa69dc4ce Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Mon, 28 Jul 2025 16:57:47 +0200 Subject: [PATCH 0276/2396] Revert "tests/qtest: use qos_printf instead of g_test_message" This reverts commit 30ea13e9d97dcbd4ea541ddf9e8857fa1d5cb30f. Also rewrites qos_printf() calls added later. "make check" prints many lines like stdout: 138: UNKNOWN: # # qos_test running single test in subprocess stdout: 139: UNKNOWN: # # set_protocol_features: 0x42 stdout: 140: UNKNOWN: # # set_owner: start of session stdout: 141: UNKNOWN: # # vhost-user: un-handled message: 14 stdout: 142: UNKNOWN: # # vhost-user: un-handled message: 14 stdout: 143: UNKNOWN: # # set_vring(0)=enabled stdout: 144: UNKNOWN: # # set_vring(1)=enabled stdout: 145: UNKNOWN: # # set_vring(0)=enabled stdout: 146: UNKNOWN: # # set_vring(1)=enabled stdout: 147: UNKNOWN: # # set_vring(0)=enabled stdout: 148: UNKNOWN: # # set_vring(1)=enabled stdout: 149: UNKNOWN: # # set_vring(0)=enabled stdout: 150: UNKNOWN: # # set_vring(1)=enabled stdout: 151: UNKNOWN: # # set_vring(0)=enabled stdout: 152: UNKNOWN: # # set_vring(1)=enabled stdout: 153: UNKNOWN: # # set_vring_num: 0/256 stdout: 154: UNKNOWN: # # set_vring_addr: 0x7f9060000000/0x7f905ffff000/0x7f9060001000 Turns out this is qos-test, and the culprit is a commit meant to ease debugging. Revert it until a better solution is found. Signed-off-by: Markus Armbruster Message-ID: <20250728145747.3165315-1-armbru@redhat.com> [Commit message clarified] --- tests/qtest/qos-test.c | 5 ----- tests/qtest/vhost-user-test.c | 27 +++++++++++++-------------- 2 files changed, 13 insertions(+), 19 deletions(-) diff --git a/tests/qtest/qos-test.c b/tests/qtest/qos-test.c index abfd4b9512..00f39f33f6 100644 --- a/tests/qtest/qos-test.c +++ b/tests/qtest/qos-test.c @@ -328,11 +328,6 @@ static void walk_path(QOSGraphNode *orig_path, int len) int main(int argc, char **argv, char** envp) { g_test_init(&argc, &argv, NULL); - - if (g_test_subprocess()) { - qos_printf("qos_test running single test in subprocess\n"); - } - if (g_test_verbose()) { qos_printf("ENVIRONMENT VARIABLES: {\n"); for (char **env = envp; *env != 0; env++) { diff --git a/tests/qtest/vhost-user-test.c b/tests/qtest/vhost-user-test.c index 75cb3e44b2..56472ca709 100644 --- a/tests/qtest/vhost-user-test.c +++ b/tests/qtest/vhost-user-test.c @@ -26,7 +26,6 @@ #include "libqos/virtio-pci.h" #include "libqos/malloc-pc.h" -#include "libqos/qgraph_internal.h" #include "hw/virtio/virtio-net.h" #include "standard-headers/linux/vhost_types.h" @@ -345,7 +344,7 @@ static void chr_read(void *opaque, const uint8_t *buf, int size) } if (size != VHOST_USER_HDR_SIZE) { - qos_printf("%s: Wrong message size received %d\n", __func__, size); + g_test_message("Wrong message size received %d", size); return; } @@ -356,8 +355,8 @@ static void chr_read(void *opaque, const uint8_t *buf, int size) p += VHOST_USER_HDR_SIZE; size = qemu_chr_fe_read_all(chr, p, msg.size); if (size != msg.size) { - qos_printf("%s: Wrong message size received %d != %d\n", - __func__, size, msg.size); + g_test_message("Wrong message size received %d != %d", + size, msg.size); goto out; } } @@ -393,7 +392,7 @@ static void chr_read(void *opaque, const uint8_t *buf, int size) * We don't need to do anything here, the remote is just * letting us know it is in charge. Just log it. */ - qos_printf("set_owner: start of session\n"); + g_test_message("set_owner: start of session\n"); break; case VHOST_USER_GET_PROTOCOL_FEATURES: @@ -419,7 +418,7 @@ static void chr_read(void *opaque, const uint8_t *buf, int size) * the remote end to send this. There is no handshake reply so * just log the details for debugging. */ - qos_printf("set_protocol_features: 0x%"PRIx64 "\n", msg.payload.u64); + g_test_message("set_protocol_features: 0x%"PRIx64 "\n", msg.payload.u64); break; /* @@ -427,11 +426,11 @@ static void chr_read(void *opaque, const uint8_t *buf, int size) * address of the vrings but we can simply report them. */ case VHOST_USER_SET_VRING_NUM: - qos_printf("set_vring_num: %d/%d\n", + g_test_message("set_vring_num: %d/%d\n", msg.payload.state.index, msg.payload.state.num); break; case VHOST_USER_SET_VRING_ADDR: - qos_printf("set_vring_addr: 0x%"PRIx64"/0x%"PRIx64"/0x%"PRIx64"\n", + g_test_message("set_vring_addr: 0x%"PRIx64"/0x%"PRIx64"/0x%"PRIx64"\n", msg.payload.addr.avail_user_addr, msg.payload.addr.desc_user_addr, msg.payload.addr.used_user_addr); @@ -464,7 +463,7 @@ static void chr_read(void *opaque, const uint8_t *buf, int size) case VHOST_USER_SET_VRING_CALL: /* consume the fd */ if (!qemu_chr_fe_get_msgfds(chr, &fd, 1) && fd < 0) { - qos_printf("call fd: %d, do not set non-blocking\n", fd); + g_test_message("call fd: %d, do not set non-blocking\n", fd); break; } /* @@ -510,12 +509,12 @@ static void chr_read(void *opaque, const uint8_t *buf, int size) * fully functioning vhost-user we would enable/disable the * vring monitoring. */ - qos_printf("set_vring(%d)=%s\n", msg.payload.state.index, + g_test_message("set_vring(%d)=%s\n", msg.payload.state.index, msg.payload.state.num ? "enabled" : "disabled"); break; default: - qos_printf("vhost-user: un-handled message: %d\n", msg.request); + g_test_message("vhost-user: un-handled message: %d\n", msg.request); break; } @@ -539,7 +538,7 @@ static const char *init_hugepagefs(void) } if (access(path, R_OK | W_OK | X_OK)) { - qos_printf("access on path (%s): %s", path, strerror(errno)); + g_test_message("access on path (%s): %s", path, strerror(errno)); g_test_fail(); return NULL; } @@ -549,13 +548,13 @@ static const char *init_hugepagefs(void) } while (ret != 0 && errno == EINTR); if (ret != 0) { - qos_printf("statfs on path (%s): %s", path, strerror(errno)); + g_test_message("statfs on path (%s): %s", path, strerror(errno)); g_test_fail(); return NULL; } if (fs.f_type != HUGETLBFS_MAGIC) { - qos_printf("Warning: path not on HugeTLBFS: %s", path); + g_test_message("Warning: path not on HugeTLBFS: %s", path); g_test_fail(); return NULL; } From a80151c9da1a848e5d3ad7153080beaf0745e4cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 27 Jun 2024 09:10:39 +0200 Subject: [PATCH 0277/2396] hw/sd/sdcard: Remove support for spec v1.10 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Support for spec v1.10 was deprecated in QEMU v9.1. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Message-ID: <20240627071040.36190-4-philmd@linaro.org> --- docs/about/deprecated.rst | 6 ------ docs/about/removed-features.rst | 5 +++++ hw/sd/sd.c | 12 ++---------- include/hw/sd/sd.h | 1 - 4 files changed, 7 insertions(+), 17 deletions(-) diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst index 5d1579dcf8..6ae6920681 100644 --- a/docs/about/deprecated.rst +++ b/docs/about/deprecated.rst @@ -434,12 +434,6 @@ recommending to switch to their stable counterparts: - "Zve64f" should be replaced with "zve64f" - "Zve64d" should be replaced with "zve64d" -``-device sd-card,spec_version=1`` (since 9.1) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -SD physical layer specification v2.00 supersedes the v1.10 one. -v2.00 is the default since QEMU 3.0.0. - Block device options '''''''''''''''''''' diff --git a/docs/about/removed-features.rst b/docs/about/removed-features.rst index 25a904032c..332d07e2b1 100644 --- a/docs/about/removed-features.rst +++ b/docs/about/removed-features.rst @@ -1186,6 +1186,11 @@ by using ``-machine graphics=off``. The 'pvrdma' device and the whole RDMA subsystem have been removed. +``-device sd-card,spec_version=1`` (since 10.2) +''''''''''''''''''''''''''''''''''''''''''''''' + +SD physical layer specification v2.00 supersedes the v1.10 one. + Related binaries ---------------- diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 8c290595f0..8b142e4796 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -195,7 +195,6 @@ static bool sd_is_emmc(SDState *sd) static const char *sd_version_str(enum SDPhySpecificationVersion version) { static const char *sdphy_version[] = { - [SD_PHY_SPECv1_10_VERS] = "v1.10", [SD_PHY_SPECv2_00_VERS] = "v2.00", [SD_PHY_SPECv3_01_VERS] = "v3.01", }; @@ -407,11 +406,7 @@ static void sd_set_ocr(SDState *sd) static void sd_set_scr(SDState *sd) { sd->scr[0] = 0 << 4; /* SCR structure version 1.0 */ - if (sd->spec_version == SD_PHY_SPECv1_10_VERS) { - sd->scr[0] |= 1; /* Spec Version 1.10 */ - } else { - sd->scr[0] |= 2; /* Spec Version 2.00 or Version 3.0X */ - } + sd->scr[0] |= 2; /* Spec Version 2.00 or Version 3.0X */ sd->scr[1] = (2 << 4) /* SDSC Card (Security Version 1.01) */ | 0b0101; /* 1-bit or 4-bit width bus modes */ sd->scr[2] = 0x00; /* Extended Security is not supported. */ @@ -1555,9 +1550,6 @@ static sd_rsp_type_t sd_cmd_DE_SELECT_CARD(SDState *sd, SDRequest req) /* CMD8 */ static sd_rsp_type_t sd_cmd_SEND_IF_COND(SDState *sd, SDRequest req) { - if (sd->spec_version < SD_PHY_SPECv2_00_VERS) { - return sd_cmd_illegal(sd, req); - } if (sd->state != sd_idle_state) { return sd_invalid_state_for_cmd(sd, req); } @@ -2773,7 +2765,7 @@ static void sd_realize(DeviceState *dev, Error **errp) int ret; switch (sd->spec_version) { - case SD_PHY_SPECv1_10_VERS + case SD_PHY_SPECv2_00_VERS ... SD_PHY_SPECv3_01_VERS: break; default: diff --git a/include/hw/sd/sd.h b/include/hw/sd/sd.h index 55d363f58f..91b5c40a5f 100644 --- a/include/hw/sd/sd.h +++ b/include/hw/sd/sd.h @@ -56,7 +56,6 @@ #define AKE_SEQ_ERROR (1 << 3) enum SDPhySpecificationVersion { - SD_PHY_SPECv1_10_VERS = 1, SD_PHY_SPECv2_00_VERS = 2, SD_PHY_SPECv3_01_VERS = 3, }; From b8d6e05f16b77231d11b96659072b302290b3396 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 10 Jun 2025 11:19:34 +0200 Subject: [PATCH 0278/2396] target/ppc/kvm: Avoid using alloca() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit kvmppc_load_htab_chunk() is used for migration, thus is not a hot path. Use the heap instead of the stack, removing the alloca() call. Reported-by: Peter Maydell Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Peter Maydell Reviewed-by: Manos Pitsidianakis Reviewed-by: Harsh Prateek Bora Reviewed-by: Stefan Hajnoczi Message-Id: <20250901132626.28639-2-philmd@linaro.org> --- target/ppc/kvm.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/target/ppc/kvm.c b/target/ppc/kvm.c index d145774b09..2521ff65c6 100644 --- a/target/ppc/kvm.c +++ b/target/ppc/kvm.c @@ -2760,11 +2760,11 @@ int kvmppc_save_htab(QEMUFile *f, int fd, size_t bufsize, int64_t max_ns) int kvmppc_load_htab_chunk(QEMUFile *f, int fd, uint32_t index, uint16_t n_valid, uint16_t n_invalid, Error **errp) { - struct kvm_get_htab_header *buf; - size_t chunksize = sizeof(*buf) + n_valid * HASH_PTE_SIZE_64; + size_t chunksize = sizeof(struct kvm_get_htab_header) + + n_valid * HASH_PTE_SIZE_64; + g_autofree struct kvm_get_htab_header *buf = g_malloc(chunksize); ssize_t rc; - buf = alloca(chunksize); buf->index = index; buf->n_valid = n_valid; buf->n_invalid = n_invalid; From 32ee080ccdde8a90d5ee3b56e28f95ada35dee4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 10 Jun 2025 11:25:39 +0200 Subject: [PATCH 0279/2396] docs/devel/style: Mention alloca() family API is forbidden MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Suggested-by: Alex Bennée Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Manos Pitsidianakis Reviewed-by: Stefan Hajnoczi Message-Id: <20250901132626.28639-4-philmd@linaro.org> --- docs/devel/style.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/devel/style.rst b/docs/devel/style.rst index d025933808..941fe14bfd 100644 --- a/docs/devel/style.rst +++ b/docs/devel/style.rst @@ -446,8 +446,8 @@ Low level memory management =========================== Use of the ``malloc/free/realloc/calloc/valloc/memalign/posix_memalign`` -APIs is not allowed in the QEMU codebase. Instead of these routines, -use the GLib memory allocation routines +or ``alloca/g_alloca/g_newa/g_newa0`` APIs is not allowed in the QEMU codebase. +Instead of these routines, use the GLib memory allocation routines ``g_malloc/g_malloc0/g_new/g_new0/g_realloc/g_free`` or QEMU's ``qemu_memalign/qemu_blockalign/qemu_vfree`` APIs. From e74416713fe166a6f21cc5ee2000cfd0c248e1a7 Mon Sep 17 00:00:00 2001 From: Djordje Todorovic Date: Wed, 18 Jun 2025 12:27:49 +0000 Subject: [PATCH 0280/2396] hw/pci: Allow explicit function numbers in pci MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since there is no pch_gbe emulation, we could be using func other than 0 when adding new devices to specific boards. Signed-off-by: Chao-ying Fu Signed-off-by: Djordje Todorovic Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250901102850.1172983-13-djordje.todorovic@htecgroup.com> [PMD: Compare with null character ('\0'), not '0'] Signed-off-by: Philippe Mathieu-Daudé --- hw/pci/pci.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/hw/pci/pci.c b/hw/pci/pci.c index c70b5ceeba..297196b242 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -984,14 +984,15 @@ static int pci_parse_devaddr(const char *addr, int *domp, int *busp, slot = val; - if (funcp != NULL) { - if (*e != '.') + if (funcp != NULL && *e != '\0') { + if (*e != '.') { return -1; - + } p = e + 1; val = strtoul(p, &e, 16); - if (e == p) + if (e == p) { return -1; + } func = val; } @@ -2054,13 +2055,15 @@ bool pci_init_nic_in_slot(PCIBus *rootbus, const char *model, int dom, busnr, devfn; PCIDevice *pci_dev; unsigned slot; + unsigned func; + PCIBus *bus; if (!nd) { return false; } - if (!devaddr || pci_parse_devaddr(devaddr, &dom, &busnr, &slot, NULL) < 0) { + if (!devaddr || pci_parse_devaddr(devaddr, &dom, &busnr, &slot, &func) < 0) { error_report("Invalid PCI device address %s for device %s", devaddr, model); exit(1); @@ -2071,7 +2074,7 @@ bool pci_init_nic_in_slot(PCIBus *rootbus, const char *model, exit(1); } - devfn = PCI_DEVFN(slot, 0); + devfn = PCI_DEVFN(slot, func); bus = pci_find_bus_nr(rootbus, busnr); if (!bus) { From 7ef54b7bf38104c59523a0d7559ae964676178b2 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Wed, 30 Jul 2025 15:04:34 -0700 Subject: [PATCH 0281/2396] migration: compile migration/ram.c once MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Acked-by: Fabiano Rosas Reviewed-by: Peter Xu Signed-off-by: Pierrick Bouvier Reviewed-by: Richard Henderson Message-ID: <20250730220435.1139101-2-pierrick.bouvier@linaro.org> Signed-off-by: Philippe Mathieu-Daudé --- migration/meson.build | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/migration/meson.build b/migration/meson.build index 276da3be5a..45e9445f97 100644 --- a/migration/meson.build +++ b/migration/meson.build @@ -31,6 +31,7 @@ system_ss.add(files( 'multifd-zero-page.c', 'options.c', 'postcopy-ram.c', + 'ram.c', 'savevm.c', 'socket.c', 'tls.c', @@ -50,5 +51,4 @@ system_ss.add(when: uadk, if_true: files('multifd-uadk.c')) system_ss.add(when: qatzip, if_true: files('multifd-qatzip.c')) specific_ss.add(when: 'CONFIG_SYSTEM_ONLY', - if_true: files('ram.c', - 'vfio.c')) + if_true: files('vfio.c')) From 01b6fb37056bf5c6a734e77b386f4e9c830b2ce0 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Wed, 30 Jul 2025 15:04:35 -0700 Subject: [PATCH 0282/2396] migration/vfio: compile only once MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Acked-by: Fabiano Rosas Reviewed-by: Peter Xu Signed-off-by: Pierrick Bouvier Reviewed-by: Richard Henderson Message-ID: <20250730220435.1139101-3-pierrick.bouvier@linaro.org> [PMD: Cover vfio-stub.c in MAINTAINERS] Signed-off-by: Philippe Mathieu-Daudé --- MAINTAINERS | 1 + migration/meson.build | 6 +++--- migration/vfio-stub.c | 16 ++++++++++++++++ migration/vfio.c | 14 -------------- 4 files changed, 20 insertions(+), 17 deletions(-) create mode 100644 migration/vfio-stub.c diff --git a/MAINTAINERS b/MAINTAINERS index 0207946537..040ef06494 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2268,6 +2268,7 @@ F: util/vfio-helpers.c F: include/hw/vfio/ F: docs/devel/migration/vfio.rst F: qapi/vfio.json +F: migration/vfio-stub.c F: tests/functional/aarch64/test_device_passthrough.py vfio-igd diff --git a/migration/meson.build b/migration/meson.build index 45e9445f97..0f71544a82 100644 --- a/migration/meson.build +++ b/migration/meson.build @@ -49,6 +49,6 @@ system_ss.add(when: zstd, if_true: files('multifd-zstd.c')) system_ss.add(when: qpl, if_true: files('multifd-qpl.c')) system_ss.add(when: uadk, if_true: files('multifd-uadk.c')) system_ss.add(when: qatzip, if_true: files('multifd-qatzip.c')) - -specific_ss.add(when: 'CONFIG_SYSTEM_ONLY', - if_true: files('vfio.c')) +system_ss.add(when: 'CONFIG_VFIO', + if_true: files('vfio.c'), + if_false: files('vfio-stub.c')) diff --git a/migration/vfio-stub.c b/migration/vfio-stub.c new file mode 100644 index 0000000000..f59ebe075d --- /dev/null +++ b/migration/vfio-stub.c @@ -0,0 +1,16 @@ +/* + * QEMU live migration - stubs for VFIO + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "migration.h" + +void migration_populate_vfio_info(MigrationInfo *info) +{ +} + +void migration_reset_vfio_bytes_transferred(void) +{ +} diff --git a/migration/vfio.c b/migration/vfio.c index 0b64e49ef0..af6ae2c1e1 100644 --- a/migration/vfio.c +++ b/migration/vfio.c @@ -8,13 +8,8 @@ #include "qemu/osdep.h" #include "qapi/qapi-types-migration.h" #include "migration.h" -#include CONFIG_DEVICES - -#ifdef CONFIG_VFIO #include "hw/vfio/vfio-migration.h" -#endif -#ifdef CONFIG_VFIO void migration_populate_vfio_info(MigrationInfo *info) { if (vfio_migration_active()) { @@ -27,12 +22,3 @@ void migration_reset_vfio_bytes_transferred(void) { vfio_migration_reset_bytes_transferred(); } -#else -void migration_populate_vfio_info(MigrationInfo *info) -{ -} - -void migration_reset_vfio_bytes_transferred(void) -{ -} -#endif From 38838f0837a3fcfadf000d885329216195a5f9ae Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Wed, 30 Jul 2025 15:05:17 -0700 Subject: [PATCH 0283/2396] cpu-target: build compilation unit once for user/system MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250730220519.1140447-2-pierrick.bouvier@linaro.org> Signed-off-by: Philippe Mathieu-Daudé --- cpu-target.c | 5 ----- meson.build | 3 ++- target-info-stub.c | 4 ++++ 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/cpu-target.c b/cpu-target.c index 772e35495b..f030e2c642 100644 --- a/cpu-target.c +++ b/cpu-target.c @@ -18,7 +18,6 @@ */ #include "qemu/osdep.h" -#include "cpu.h" #include "accel/accel-cpu-ops.h" #include "system/cpus.h" #include "exec/cpu-common.h" @@ -27,10 +26,6 @@ #include "hw/core/cpu.h" #include "trace/trace-root.h" -/* Validate correct placement of CPUArchState. */ -QEMU_BUILD_BUG_ON(offsetof(ArchCPU, parent_obj) != 0); -QEMU_BUILD_BUG_ON(offsetof(ArchCPU, env) != sizeof(CPUState)); - /* enable or disable single step mode. EXCP_DEBUG is returned by the CPU loop after each instruction */ void cpu_single_step(CPUState *cpu, int enabled) diff --git a/meson.build b/meson.build index 0d42de61ae..7ff84787cf 100644 --- a/meson.build +++ b/meson.build @@ -3876,7 +3876,8 @@ if have_block endif common_ss.add(files('cpu-common.c')) -specific_ss.add(files('cpu-target.c')) +user_ss.add(files('cpu-target.c')) +system_ss.add(files('cpu-target.c')) subdir('system') diff --git a/target-info-stub.c b/target-info-stub.c index ca0caa3686..d96d8249c1 100644 --- a/target-info-stub.c +++ b/target-info-stub.c @@ -12,6 +12,10 @@ #include "hw/boards.h" #include "cpu.h" +/* Validate correct placement of CPUArchState. */ +QEMU_BUILD_BUG_ON(offsetof(ArchCPU, parent_obj) != 0); +QEMU_BUILD_BUG_ON(offsetof(ArchCPU, env) != sizeof(CPUState)); + static const TargetInfo target_info_stub = { .target_name = TARGET_NAME, .target_arch = SYS_EMU_TARGET__MAX, From 9dbb61bb2cfea836bfdb10260a5ebe4d0678463a Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Wed, 30 Jul 2025 15:05:18 -0700 Subject: [PATCH 0284/2396] include/exec/target_page.h: move page-target.c to header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250730220519.1140447-3-pierrick.bouvier@linaro.org> Signed-off-by: Philippe Mathieu-Daudé --- include/exec/target_page.h | 11 ++++++++++- meson.build | 2 +- page-target.c | 21 --------------------- 3 files changed, 11 insertions(+), 23 deletions(-) delete mode 100644 page-target.c diff --git a/include/exec/target_page.h b/include/exec/target_page.h index ca0ebbc8bb..813591c9b5 100644 --- a/include/exec/target_page.h +++ b/include/exec/target_page.h @@ -62,6 +62,15 @@ static inline int qemu_target_page_bits(void) return TARGET_PAGE_BITS; } -size_t qemu_target_pages_to_MiB(size_t pages); +/* Convert target pages to MiB (2**20). */ +static inline size_t qemu_target_pages_to_MiB(size_t pages) +{ + int page_bits = TARGET_PAGE_BITS; + + /* So far, the largest (non-huge) page size is 64k, i.e. 16 bits. */ + g_assert(page_bits < 20); + + return pages >> (20 - page_bits); +} #endif diff --git a/meson.build b/meson.build index 7ff84787cf..fa6186db33 100644 --- a/meson.build +++ b/meson.build @@ -3899,7 +3899,7 @@ if get_option('b_lto') pagevary = declare_dependency(link_with: pagevary) endif common_ss.add(pagevary) -specific_ss.add(files('page-target.c', 'page-vary-target.c')) +specific_ss.add(files('page-vary-target.c')) common_ss.add(files('target-info.c')) specific_ss.add(files('target-info-stub.c')) diff --git a/page-target.c b/page-target.c deleted file mode 100644 index 8fcd5443b5..0000000000 --- a/page-target.c +++ /dev/null @@ -1,21 +0,0 @@ -/* - * QEMU page values getters (target independent) - * - * Copyright (c) 2003 Fabrice Bellard - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#include "qemu/osdep.h" -#include "exec/target_page.h" - -/* Convert target pages to MiB (2**20). */ -size_t qemu_target_pages_to_MiB(size_t pages) -{ - int page_bits = TARGET_PAGE_BITS; - - /* So far, the largest (non-huge) page size is 64k, i.e. 16 bits. */ - g_assert(page_bits < 20); - - return pages >> (20 - page_bits); -} From 0df57e00d22412320873c2a7548f49c72b247e42 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Fri, 1 Aug 2025 10:40:05 -0700 Subject: [PATCH 0285/2396] hw/meson: enter target hw first MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We can reuse target source sets for "generic" devices that are related to a single architecture (like interrupt controllers). Signed-off-by: Pierrick Bouvier Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Message-ID: <20250801174006.2466508-2-pierrick.bouvier@linaro.org> Signed-off-by: Philippe Mathieu-Daudé --- hw/meson.build | 45 +++++++++++++++++++++++---------------------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/hw/meson.build b/hw/meson.build index 791ce21ab4..1022bdb806 100644 --- a/hw/meson.build +++ b/hw/meson.build @@ -1,3 +1,26 @@ +# Enter target code first to reuse variables associated +subdir('alpha') +subdir('arm') +subdir('avr') +subdir('hppa') +subdir('xenpv') # i386 uses it +subdir('i386') +subdir('loongarch') +subdir('m68k') +subdir('microblaze') +subdir('mips') +subdir('openrisc') +subdir('ppc') +subdir('remote') +subdir('riscv') +subdir('rx') +subdir('s390x') +subdir('sh4') +subdir('sparc') +subdir('sparc64') +subdir('tricore') +subdir('xtensa') + subdir('9pfs') subdir('acpi') subdir('adc') @@ -44,26 +67,4 @@ subdir('virtio') subdir('vmapple') subdir('watchdog') subdir('xen') -subdir('xenpv') subdir('fsi') - -subdir('alpha') -subdir('arm') -subdir('avr') -subdir('hppa') -subdir('i386') -subdir('loongarch') -subdir('m68k') -subdir('microblaze') -subdir('mips') -subdir('openrisc') -subdir('ppc') -subdir('remote') -subdir('riscv') -subdir('rx') -subdir('s390x') -subdir('sh4') -subdir('sparc') -subdir('sparc64') -subdir('tricore') -subdir('xtensa') From c0a3bdf62c260aa647491b4906c55177a2c08d23 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Fri, 1 Aug 2025 10:40:06 -0700 Subject: [PATCH 0286/2396] hw/intc: compile some arm related source once MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Let kvm related gic file out for now, as they are compiled only on aarch64 hosts. Signed-off-by: Pierrick Bouvier Tested-by: Philippe Mathieu-Daudé Message-ID: <20250801174006.2466508-3-pierrick.bouvier@linaro.org> Signed-off-by: Philippe Mathieu-Daudé --- hw/intc/meson.build | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hw/intc/meson.build b/hw/intc/meson.build index 3137521a4a..3efb276b6e 100644 --- a/hw/intc/meson.build +++ b/hw/intc/meson.build @@ -38,11 +38,11 @@ if config_all_devices.has_key('CONFIG_APIC') or \ endif specific_ss.add(when: 'CONFIG_APIC', if_true: files('apic.c', 'apic_common.c')) -specific_ss.add(when: 'CONFIG_ARM_GIC', if_true: files('arm_gicv3_cpuif_common.c')) -specific_ss.add(when: 'CONFIG_ARM_GICV3', if_true: files('arm_gicv3_cpuif.c')) +arm_common_ss.add(when: 'CONFIG_ARM_GIC', if_true: files('arm_gicv3_cpuif_common.c')) +arm_common_ss.add(when: 'CONFIG_ARM_GICV3', if_true: files('arm_gicv3_cpuif.c')) specific_ss.add(when: 'CONFIG_ARM_GIC_KVM', if_true: files('arm_gic_kvm.c')) specific_ss.add(when: ['CONFIG_ARM_GIC_KVM', 'TARGET_AARCH64'], if_true: files('arm_gicv3_kvm.c', 'arm_gicv3_its_kvm.c')) -specific_ss.add(when: 'CONFIG_ARM_V7M', if_true: files('armv7m_nvic.c')) +arm_common_ss.add(when: 'CONFIG_ARM_V7M', if_true: files('armv7m_nvic.c')) specific_ss.add(when: 'CONFIG_GRLIB', if_true: files('grlib_irqmp.c')) specific_ss.add(when: 'CONFIG_IOAPIC', if_true: files('ioapic.c')) specific_ss.add(when: 'CONFIG_LOONGSON_LIOINTC', if_true: files('loongson_liointc.c')) From 8e4649cac9bcddc050d2df07908075e9e69bccc7 Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Thu, 7 Aug 2025 13:08:06 +0200 Subject: [PATCH 0287/2396] e1000e: Prevent crash from legacy interrupt firing after MSI-X enable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A race condition between guest driver actions and QEMU timers can lead to an assertion failure when the guest switches the e1000e from legacy interrupt mode to MSI-X. If a legacy interrupt delay timer (TIDV or RDTR) is active, but the guest enables MSI-X before the timer fires, the pending interrupt cause can trigger an assert in e1000e_intmgr_collect_delayed_causes(). This patch removes the assertion and executes the code that clears the pending legacy causes. This change is safe and introduces no unintended behavioral side effects, as it only alters a state that previously led to termination. - when core->delayed_causes == 0 the function was already a no-op and remains so. - when core->delayed_causes != 0 the function would previously crash due to the assertion failure. The patch now defines a safe outcome by clearing the cause and returning. Since behavior after the assertion never existed, this simply corrects the crash. Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1863 Suggested-by: Akihiko Odaki Signed-off-by: Laurent Vivier Acked-by: Jason Wang Reviewed-by: Akihiko Odaki Message-ID: <20250807110806.409065-1-lvivier@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/net/e1000e_core.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/hw/net/e1000e_core.c b/hw/net/e1000e_core.c index 2413858790..06657bb3ac 100644 --- a/hw/net/e1000e_core.c +++ b/hw/net/e1000e_core.c @@ -341,11 +341,6 @@ e1000e_intmgr_collect_delayed_causes(E1000ECore *core) { uint32_t res; - if (msix_enabled(core->owner)) { - assert(core->delayed_causes == 0); - return 0; - } - res = core->delayed_causes; core->delayed_causes = 0; From c5ade4f9d289fed61df7a04950f8f607e26353e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 11 Aug 2025 11:36:00 +0200 Subject: [PATCH 0288/2396] scripts/coverity-scan/COMPONENTS.md: Add a 'plugins' category MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cover the TCG plugins files under their own Coverity category. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Peter Maydell Acked-by: Alex Bennée Message-Id: <20250811094341.91597-1-philmd@linaro.org> --- scripts/coverity-scan/COMPONENTS.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/scripts/coverity-scan/COMPONENTS.md b/scripts/coverity-scan/COMPONENTS.md index 72995903ff..95805b536b 100644 --- a/scripts/coverity-scan/COMPONENTS.md +++ b/scripts/coverity-scan/COMPONENTS.md @@ -147,6 +147,9 @@ tcg system ~ .*/qemu(/system/.*|/accel/.*) +plugins + ~ .*/qemu(/contrib|/tests/tcg)?/plugins/.* + (headers) ~ .*/qemu(/include/.*) From 7baa9c39fc3d525216f0cedcfda5374c26d50e80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 11 Aug 2025 11:31:08 +0200 Subject: [PATCH 0289/2396] hw/scsi/mptsas: Avoid silent integer truncation in MPI_FUNC_IOC_INIT MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For the MaxDevices 8-bit field of the request / response structures of the MPI_FUNCTION_IOC_INIT command, the 0x00 value means "max 256 devices". This is not a problem because when max_devices=256, its value (0x100), being casted to a uint8_t, is truncated to 0x00. However Coverity complains for an "Overflowed constant". Fix by re-using the request fields in the response, since they are not modified and use the same types. Fix: Coverity 1547736 (Overflowed constant) Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Peter Maydell Message-Id: <20250811095550.93655-1-philmd@linaro.org> --- hw/scsi/mptsas.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hw/scsi/mptsas.c b/hw/scsi/mptsas.c index 1ebe0b82a7..4ada35b7ec 100644 --- a/hw/scsi/mptsas.c +++ b/hw/scsi/mptsas.c @@ -579,11 +579,11 @@ static void mptsas_process_ioc_init(MPTSASState *s, MPIMsgIOCInit *req) } memset(&reply, 0, sizeof(reply)); - reply.WhoInit = s->who_init; + reply.WhoInit = req->WhoInit; reply.MsgLength = sizeof(reply) / 4; reply.Function = req->Function; - reply.MaxDevices = s->max_devices; - reply.MaxBuses = s->max_buses; + reply.MaxDevices = req->MaxDevices; + reply.MaxBuses = req->MaxBuses; reply.MsgContext = req->MsgContext; mptsas_fix_ioc_init_reply_endianness(&reply); From 831d75fd735dbd116703d3a1ca5e271dc930ebae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 12 Aug 2025 15:36:05 +0200 Subject: [PATCH 0290/2396] hw/ssi: Document ssi_transfer() method MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A SPI transaction consists of shifting bit in sync with the CLK line, writing on the MOSI (output) line / and reading MISO (input) line. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Gustavo Romero Reviewed-by: Alex Bennée --- include/hw/ssi/ssi.h | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/include/hw/ssi/ssi.h b/include/hw/ssi/ssi.h index 3cdcbd5390..2ad8033d8f 100644 --- a/include/hw/ssi/ssi.h +++ b/include/hw/ssi/ssi.h @@ -38,6 +38,7 @@ struct SSIPeripheralClass { /* if you have standard or no CS behaviour, just override transfer. * This is called when the device cs is active (true by default). + * See ssi_transfer(). */ uint32_t (*transfer)(SSIPeripheral *dev, uint32_t val); /* called when the CS line changes. Optional, devices only need to implement @@ -52,6 +53,7 @@ struct SSIPeripheralClass { * of the CS behaviour at the device level. transfer, set_cs, and * cs_polarity are unused if this is overwritten. Transfer_raw will * always be called for the device for every txrx access to the parent bus + * See ssi_transfer(). */ uint32_t (*transfer_raw)(SSIPeripheral *dev, uint32_t val); }; @@ -110,6 +112,18 @@ bool ssi_realize_and_unref(DeviceState *dev, SSIBus *bus, Error **errp); /* Master interface. */ SSIBus *ssi_create_bus(DeviceState *parent, const char *name); +/** + * Transfer a word on a SSI bus + * @bus: SSI bus + * @val: word to transmit + * + * At the same time, read a word and write the @val one on the SSI bus. + * + * SSI words might vary between 8 and 32 bits. The same number of bits + * written is received. + * + * Return: word value received + */ uint32_t ssi_transfer(SSIBus *bus, uint32_t val); DeviceState *ssi_get_cs(SSIBus *bus, uint8_t cs_index); From 14ab44b96d5bf761af81cc723314ef5ecf73ed17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 14 Aug 2025 08:40:38 +0200 Subject: [PATCH 0291/2396] elf: Add EF_MIPS_ARCH_ASE definitions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Include MIPS ASE ELF definitions from binutils: https://sourceware.org/git/?p=binutils-gdb.git;a=blob;f=include/elf/mips.h;h=4fc190f404d828ded84e621bfcece5fa9f9c23c8;hb=HEAD#l210 Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250814070650.78657-2-philmd@linaro.org> --- include/elf.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/include/elf.h b/include/elf.h index e7259ec366..bbfac055de 100644 --- a/include/elf.h +++ b/include/elf.h @@ -56,6 +56,13 @@ typedef int64_t Elf64_Sxword; #define EF_MIPS_ARCH_32R6 0x90000000 /* MIPS32r6 code. */ #define EF_MIPS_ARCH_64R6 0xa0000000 /* MIPS64r6 code. */ +/* MIPS Architectural Extensions. */ +#define EF_MIPS_ARCH_ASE 0x0f000000 + +#define EF_MIPS_ARCH_ASE_MICROMIPS 0x02000000 +#define EF_MIPS_ARCH_ASE_M16 0x04000000 +#define EF_MIPS_ARCH_ASE_MDMX 0x08000000 + /* The ABI of a file. */ #define EF_MIPS_ABI_O32 0x00001000 /* O32 ABI. */ #define EF_MIPS_ABI_O64 0x00002000 /* O32 extended for 64 bit. */ From 7a09b3cc70ab6d717b18dec5c5995f7a06af4593 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 14 Aug 2025 08:40:49 +0200 Subject: [PATCH 0292/2396] linux-user/mips: Select 74Kf CPU to run MIPS16e binaries MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 74Kf is our latest CPU supporting MIPS16e ASE. Note, currently QEMU doesn't have 64-bit CPU supporting MIPS16e ASE. Cc: qemu-stable@nongnu.org Fixes: 6ea219d0196..d19954f46df ("target-mips: MIPS16 support") Resolves: https://gitlab.com/qemu-project/qemu/-/issues/3054 Reported-by: Justin Applegate Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250814070650.78657-3-philmd@linaro.org> --- linux-user/mips/elfload.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/linux-user/mips/elfload.c b/linux-user/mips/elfload.c index e0c50f50ed..6f1880befc 100644 --- a/linux-user/mips/elfload.c +++ b/linux-user/mips/elfload.c @@ -37,6 +37,9 @@ const char *get_elf_cpu_model(uint32_t eflags) if ((eflags & EF_MIPS_ARCH) == EF_MIPS_ARCH_32R6) { return "mips32r6-generic"; } + if ((eflags & EF_MIPS_ARCH_ASE) == EF_MIPS_ARCH_ASE_M16) { + return "74Kf"; + } if (eflags & EF_MIPS_NAN2008) { return "P5600"; } From 51c3aebfda6489b49cebef593a1ceb597cb97a7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 14 Aug 2025 08:41:26 +0200 Subject: [PATCH 0293/2396] linux-user/mips: Select M14Kc CPU to run microMIPS binaries MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The M14Kc is our latest CPU supporting the microMIPS ASE. Note, currently QEMU doesn't have 64-bit CPU supporting microMIPS ASE. Cc: qemu-stable@nongnu.org Fixes: 3c824109da0 ("target-mips: microMIPS ASE support") Resolves: https://gitlab.com/qemu-project/qemu/-/issues/3054 Reported-by: Justin Applegate Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250814070650.78657-4-philmd@linaro.org> --- linux-user/mips/elfload.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/linux-user/mips/elfload.c b/linux-user/mips/elfload.c index 6f1880befc..cc5bbf05ab 100644 --- a/linux-user/mips/elfload.c +++ b/linux-user/mips/elfload.c @@ -37,6 +37,9 @@ const char *get_elf_cpu_model(uint32_t eflags) if ((eflags & EF_MIPS_ARCH) == EF_MIPS_ARCH_32R6) { return "mips32r6-generic"; } + if ((eflags & EF_MIPS_ARCH_ASE) == EF_MIPS_ARCH_ASE_MICROMIPS) { + return "M14Kc"; + } if ((eflags & EF_MIPS_ARCH_ASE) == EF_MIPS_ARCH_ASE_M16) { return "74Kf"; } From 1f82ca723478f44823a18e7151e487d58da03659 Mon Sep 17 00:00:00 2001 From: Denis Rastyogin Date: Thu, 14 Aug 2025 13:48:32 +0300 Subject: [PATCH 0294/2396] target/mips: fix TLB huge page check to use 64-bit shift MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use extract64(entry, psn, 1) instead of (entry & (1 << psn)) to avoid undefined behavior for shifts by 32–63 and to make bit extraction intent explicit. Found by Linux Verification Center (linuxtesting.org) with SVACE. Signed-off-by: Denis Rastyogin Message-ID: <20250814104914.13101-1-gerben@altlinux.org> Signed-off-by: Philippe Mathieu-Daudé --- target/mips/tcg/system/tlb_helper.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/mips/tcg/system/tlb_helper.c b/target/mips/tcg/system/tlb_helper.c index eccaf3624c..1e8901556d 100644 --- a/target/mips/tcg/system/tlb_helper.c +++ b/target/mips/tcg/system/tlb_helper.c @@ -652,7 +652,7 @@ static int walk_directory(CPUMIPSState *env, uint64_t *vaddr, return 0; } - if ((entry & (1 << psn)) && hugepg) { + if (extract64(entry, psn, 1) && hugepg) { *huge_page = true; *hgpg_directory_hit = true; entry = get_tlb_entry_layout(env, entry, leaf_mop, pf_ptew); From 46d03bb23dde86513465724760d85f42eb17539e Mon Sep 17 00:00:00 2001 From: Aditya Gupta Date: Wed, 20 Aug 2025 17:55:17 +0530 Subject: [PATCH 0295/2396] hw/ppc: Fix build error with CONFIG_POWERNV disabled MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently when CONFIG_POWERNV is not enabled, the build fails, such as with --without-default-devices: $ ./configure --without-default-devices $ make [281/283] Linking target qemu-system-ppc64 FAILED: qemu-system-ppc64 cc -m64 @qemu-system-ppc64.rsp /usr/bin/ld: libqemu-ppc64-softmmu.a.p/target_ppc_misc_helper.c.o: in function `helper_load_sprd': .../target/ppc/misc_helper.c:335:(.text+0xcdc): undefined reference to `pnv_chip_find_core' /usr/bin/ld: libqemu-ppc64-softmmu.a.p/target_ppc_misc_helper.c.o: in function `helper_store_sprd': .../target/ppc/misc_helper.c:375:(.text+0xdf4): undefined reference to `pnv_chip_find_core' collect2: error: ld returned 1 exit status ... This is since target/ppc/misc_helper.c references PowerNV specific 'pnv_chip_find_core' call. Split the PowerNV specific SPRD code out of the generic PowerPC code, by moving the SPRD code to pnv.c Fixes: 9808ce6d5cb ("target/ppc: Big-core scratch register fix") Cc: Philippe Mathieu-Daudé Reported-by: Thomas Huth Suggested-by: Cédric Le Goater Signed-off-by: Aditya Gupta Acked-by: Cédric Le Goater Message-ID: <20250820122516.949766-2-adityag@linux.ibm.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/ppc/pnv.c | 86 ++++++++++++++++++++++++++++++++++++++++ target/ppc/cpu.h | 4 ++ target/ppc/misc_helper.c | 59 +++------------------------ 3 files changed, 96 insertions(+), 53 deletions(-) diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index d84c9067ed..9c74f46091 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -21,6 +21,7 @@ #include "qemu/osdep.h" #include "qemu/datadir.h" +#include "qemu/log.h" #include "qemu/units.h" #include "qemu/cutils.h" #include "qapi/error.h" @@ -1794,12 +1795,83 @@ static void pnv_chip_power9_pec_realize(PnvChip *chip, Error **errp) } } +static uint64_t pnv_handle_sprd_load(CPUPPCState *env) +{ + PowerPCCPU *cpu = env_archcpu(env); + PnvCore *pc = pnv_cpu_state(cpu)->pnv_core; + uint64_t sprc = env->spr[SPR_POWER_SPRC]; + + if (pc->big_core) { + pc = pnv_chip_find_core(pc->chip, CPU_CORE(pc)->core_id & ~0x1); + } + + switch (sprc & 0x3e0) { + case 0: /* SCRATCH0-3 */ + case 1: /* SCRATCH4-7 */ + return pc->scratch[(sprc >> 3) & 0x7]; + + case 0x1e0: /* core thread state */ + if (env->excp_model == POWERPC_EXCP_POWER9) { + /* + * Only implement for POWER9 because skiboot uses it to check + * big-core mode. Other bits are unimplemented so we would + * prefer to get unimplemented message on POWER10 if it were + * used anywhere. + */ + if (pc->big_core) { + return PPC_BIT(63); + } else { + return 0; + } + } + /* fallthru */ + + default: + qemu_log_mask(LOG_UNIMP, "mfSPRD: Unimplemented SPRC:0x" + TARGET_FMT_lx"\n", sprc); + break; + } + return 0; +} + +static void pnv_handle_sprd_store(CPUPPCState *env, uint64_t val) +{ + PowerPCCPU *cpu = env_archcpu(env); + uint64_t sprc = env->spr[SPR_POWER_SPRC]; + PnvCore *pc = pnv_cpu_state(cpu)->pnv_core; + int nr; + + if (pc->big_core) { + pc = pnv_chip_find_core(pc->chip, CPU_CORE(pc)->core_id & ~0x1); + } + + switch (sprc & 0x3e0) { + case 0: /* SCRATCH0-3 */ + case 1: /* SCRATCH4-7 */ + /* + * Log stores to SCRATCH, because some firmware uses these for + * debugging and logging, but they would normally be read by the BMC, + * which is not implemented in QEMU yet. This gives a way to get at the + * information. Could also dump these upon checkstop. + */ + nr = (sprc >> 3) & 0x7; + pc->scratch[nr] = val; + break; + default: + qemu_log_mask(LOG_UNIMP, "mtSPRD: Unimplemented SPRC:0x" + TARGET_FMT_lx"\n", sprc); + break; + } +} + static void pnv_chip_power9_realize(DeviceState *dev, Error **errp) { PnvChipClass *pcc = PNV_CHIP_GET_CLASS(dev); Pnv9Chip *chip9 = PNV9_CHIP(dev); PnvChip *chip = PNV_CHIP(dev); Pnv9Psi *psi9 = &chip9->psi; + PowerPCCPU *cpu; + PowerPCCPUClass *cpu_class; Error *local_err = NULL; int i; @@ -1827,6 +1899,12 @@ static void pnv_chip_power9_realize(DeviceState *dev, Error **errp) return; } + /* Set handlers for Special registers, such as SPRD */ + cpu = chip->cores[0]->threads[0]; + cpu_class = POWERPC_CPU_GET_CLASS(cpu); + cpu_class->load_sprd = pnv_handle_sprd_load; + cpu_class->store_sprd = pnv_handle_sprd_store; + /* XIVE interrupt controller (POWER9) */ object_property_set_int(OBJECT(&chip9->xive), "ic-bar", PNV9_XIVE_IC_BASE(chip), &error_fatal); @@ -2078,6 +2156,8 @@ static void pnv_chip_power10_realize(DeviceState *dev, Error **errp) PnvChipClass *pcc = PNV_CHIP_GET_CLASS(dev); PnvChip *chip = PNV_CHIP(dev); Pnv10Chip *chip10 = PNV10_CHIP(dev); + PowerPCCPU *cpu; + PowerPCCPUClass *cpu_class; Error *local_err = NULL; int i; @@ -2105,6 +2185,12 @@ static void pnv_chip_power10_realize(DeviceState *dev, Error **errp) return; } + /* Set handlers for Special registers, such as SPRD */ + cpu = chip->cores[0]->threads[0]; + cpu_class = POWERPC_CPU_GET_CLASS(cpu); + cpu_class->load_sprd = pnv_handle_sprd_load; + cpu_class->store_sprd = pnv_handle_sprd_store; + /* XIVE2 interrupt controller (POWER10) */ object_property_set_int(OBJECT(&chip10->xive), "ic-bar", PNV10_XIVE2_IC_BASE(chip), &error_fatal); diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h index 6b90543811..0e26e4343d 100644 --- a/target/ppc/cpu.h +++ b/target/ppc/cpu.h @@ -1522,6 +1522,10 @@ struct PowerPCCPUClass { void (*init_proc)(CPUPPCState *env); int (*check_pow)(CPUPPCState *env); int (*check_attn)(CPUPPCState *env); + + /* Handlers to be set by the machine initialising the chips */ + uint64_t (*load_sprd)(CPUPPCState *env); + void (*store_sprd)(CPUPPCState *env, uint64_t val); }; static inline bool ppc_cpu_core_single_threaded(CPUState *cs) diff --git a/target/ppc/misc_helper.c b/target/ppc/misc_helper.c index e7d9462518..0e625cbb70 100644 --- a/target/ppc/misc_helper.c +++ b/target/ppc/misc_helper.c @@ -328,69 +328,22 @@ target_ulong helper_load_sprd(CPUPPCState *env) * accessed by powernv machines. */ PowerPCCPU *cpu = env_archcpu(env); - PnvCore *pc = pnv_cpu_state(cpu)->pnv_core; - target_ulong sprc = env->spr[SPR_POWER_SPRC]; + PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu); - if (pc->big_core) { - pc = pnv_chip_find_core(pc->chip, CPU_CORE(pc)->core_id & ~0x1); + if (pcc->load_sprd) { + return pcc->load_sprd(env); } - switch (sprc & 0x3e0) { - case 0: /* SCRATCH0-3 */ - case 1: /* SCRATCH4-7 */ - return pc->scratch[(sprc >> 3) & 0x7]; - - case 0x1e0: /* core thread state */ - if (env->excp_model == POWERPC_EXCP_POWER9) { - /* - * Only implement for POWER9 because skiboot uses it to check - * big-core mode. Other bits are unimplemented so we would - * prefer to get unimplemented message on POWER10 if it were - * used anywhere. - */ - if (pc->big_core) { - return PPC_BIT(63); - } else { - return 0; - } - } - /* fallthru */ - - default: - qemu_log_mask(LOG_UNIMP, "mfSPRD: Unimplemented SPRC:0x" - TARGET_FMT_lx"\n", sprc); - break; - } return 0; } void helper_store_sprd(CPUPPCState *env, target_ulong val) { - target_ulong sprc = env->spr[SPR_POWER_SPRC]; PowerPCCPU *cpu = env_archcpu(env); - PnvCore *pc = pnv_cpu_state(cpu)->pnv_core; - int nr; + PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu); - if (pc->big_core) { - pc = pnv_chip_find_core(pc->chip, CPU_CORE(pc)->core_id & ~0x1); - } - - switch (sprc & 0x3e0) { - case 0: /* SCRATCH0-3 */ - case 1: /* SCRATCH4-7 */ - /* - * Log stores to SCRATCH, because some firmware uses these for - * debugging and logging, but they would normally be read by the BMC, - * which is not implemented in QEMU yet. This gives a way to get at the - * information. Could also dump these upon checkstop. - */ - nr = (sprc >> 3) & 0x7; - pc->scratch[nr] = val; - break; - default: - qemu_log_mask(LOG_UNIMP, "mtSPRD: Unimplemented SPRC:0x" - TARGET_FMT_lx"\n", sprc); - break; + if (pcc->store_sprd) { + return pcc->store_sprd(env, val); } } From 01941107ebda4756e63a841ff5c457bc6a77c6ce Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 21 Aug 2025 16:40:51 +0100 Subject: [PATCH 0296/2396] hw/irq: New qemu_init_irq_child() function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The qemu_init_irq() function initializes a TYPE_IRQ QOM object. The caller is therefore responsible for eventually calling qemu_free_irq() to unref (and thus free) it. In many places where we want to initialize an IRQ we are in the init/realize of some other QOM object; if we have a variant of this function that calls object_initialize_child() then the IRQ will be automatically cleaned up when its parent object is destroyed, and we don't need to remember to manually free it. Implement qemu_init_irq_child(), which is to qemu_init_irq() what object_initialize_child() is to object_initialize(). Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250821154053.2417090-2-peter.maydell@linaro.org> Signed-off-by: Philippe Mathieu-Daudé --- hw/core/irq.c | 8 ++++++++ include/hw/irq.h | 23 ++++++++++++++++++++++- 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/hw/core/irq.c b/hw/core/irq.c index 6dd8d47bd6..0c768f7704 100644 --- a/hw/core/irq.c +++ b/hw/core/irq.c @@ -49,6 +49,14 @@ void qemu_init_irq(IRQState *irq, qemu_irq_handler handler, void *opaque, init_irq_fields(irq, handler, opaque, n); } +void qemu_init_irq_child(Object *parent, const char *propname, + IRQState *irq, qemu_irq_handler handler, + void *opaque, int n) +{ + object_initialize_child(parent, propname, irq, TYPE_IRQ); + init_irq_fields(irq, handler, opaque, n); +} + void qemu_init_irqs(IRQState irq[], size_t count, qemu_irq_handler handler, void *opaque) { diff --git a/include/hw/irq.h b/include/hw/irq.h index b3012237ac..291fdd67df 100644 --- a/include/hw/irq.h +++ b/include/hw/irq.h @@ -36,11 +36,32 @@ static inline void qemu_irq_pulse(qemu_irq irq) /* * Init a single IRQ. The irq is assigned with a handler, an opaque data - * and the interrupt number. + * and the interrupt number. The caller must free this with qemu_free_irq(). + * If you are using this inside a device's init or realize method, then + * qemu_init_irq_child() is probably a better choice to avoid the need + * to manually clean up the IRQ. */ void qemu_init_irq(IRQState *irq, qemu_irq_handler handler, void *opaque, int n); +/** + * qemu_init_irq_child: Initialize IRQ and make it a QOM child + * @parent: QOM object which owns this IRQ + * @propname: child property name + * @irq: pointer to IRQState to initialize + * @handler: handler function for incoming interrupts + * @opaque: opaque data to pass to @handler + * @n: interrupt number to pass to @handler + * + * Init a single IRQ and make the IRQ object a child of @parent with + * the child-property name @propname. The IRQ object will thus be + * automatically freed when @parent is destroyed. + */ +void qemu_init_irq_child(Object *parent, const char *propname, + IRQState *irq, qemu_irq_handler handler, + void *opaque, int n); + + /** * qemu_init_irqs: Initialize an array of IRQs. * From d1c9061b97d57d194e44023eb7e52fedde155e61 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 21 Aug 2025 16:40:52 +0100 Subject: [PATCH 0297/2396] hw/char/serial-pci-multi: Use qemu_init_irq_child() to avoid leak MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The serial-pci-multi device initializes an IRQ with qemu_init_irq() in its instance_init function; however it never calls qemu_free_irq(), so the init/deinit cycle has a memory leak, which ASAN catches in the device-introspect-test: Direct leak of 576 byte(s) in 6 object(s) allocated from: #0 0x626306ddade3 in malloc (/mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/arm-asan/qem u-system-arm+0x21f1de3) (BuildId: 52ece17287eba2d68e5be980e1856cd1f6be932f) #1 0x7756ade79b09 in g_malloc (/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x62b09) (BuildId: 1 eb6131419edb83b2178b682829a6913cf682d75) #2 0x7756ade5b45a in g_hash_table_new_full (/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x4445a ) (BuildId: 1eb6131419edb83b2178b682829a6913cf682d75) #3 0x62630965da37 in object_initialize_with_type /mnt/nvmedisk/linaro/qemu-from-laptop/qem u/build/arm-asan/../../qom/object.c:568:23 #4 0x62630965d440 in object_initialize /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/ar m-asan/../../qom/object.c:578:5 #5 0x626309653eeb in qemu_init_irq /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/arm-as an/../../hw/core/irq.c:48:5 #6 0x6263072370bb in multi_serial_init /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/arm-asan/../../hw/char/serial-pci-multi.c:183:9 Use the new qemu_init_irq_child() function instead, so that the IRQ object is automatically unreffed when the serial-pci device is deinited. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250821154053.2417090-3-peter.maydell@linaro.org> [PMD: Use "irq[*]" as child property name] Signed-off-by: Philippe Mathieu-Daudé --- hw/char/serial-pci-multi.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hw/char/serial-pci-multi.c b/hw/char/serial-pci-multi.c index 13df272691..34f30fb70b 100644 --- a/hw/char/serial-pci-multi.c +++ b/hw/char/serial-pci-multi.c @@ -180,7 +180,8 @@ static void multi_serial_init(Object *o) size_t i, nports = multi_serial_get_port_count(PCI_DEVICE_GET_CLASS(dev)); for (i = 0; i < nports; i++) { - qemu_init_irq(&pms->irqs[i], multi_serial_irq_mux, pms, i); + qemu_init_irq_child(o, "irq[*]", &pms->irqs[i], + multi_serial_irq_mux, pms, i); object_initialize_child(o, "serial[*]", &pms->state[i], TYPE_SERIAL); } } From f905be62379aab0c5874756e1a73b33581d7011d Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 21 Aug 2025 16:40:53 +0100 Subject: [PATCH 0298/2396] hw/ide/ich.c: Use qemu_init_irq_child() to avoid memory leak MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ICH9 PCI device uses qemu_init_irq() in its instance_init method, but fails to clean it up in its uninit. This results in a leak, detected by ASAN when running the device-introspect-test: Direct leak of 96 byte(s) in 1 object(s) allocated from: #0 0x58f3b53ecde3 in malloc (/mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/arm-asan/qem u-system-arm+0x21f1de3) (BuildId: 8dcd38b1d76bd7bd44f905c38200f4cceafd7ca4) #1 0x72e446dd5b09 in g_malloc (/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x62b09) (BuildId: 1 eb6131419edb83b2178b682829a6913cf682d75) #2 0x72e446db745a in g_hash_table_new_full (/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x4445a ) (BuildId: 1eb6131419edb83b2178b682829a6913cf682d75) #3 0x58f3b7c6fc67 in object_initialize_with_type /mnt/nvmedisk/linaro/qemu-from-laptop/qem u/build/arm-asan/../../qom/object.c:568:23 #4 0x58f3b7c6f670 in object_initialize /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/ar m-asan/../../qom/object.c:578:5 #5 0x58f3b7c6611b in qemu_init_irq /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/arm-asan/../../hw/core/irq.c:48:5 #6 0x58f3b5c6e931 in pci_ich9_ahci_init /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/arm-asan/../../hw/ide/ich.c:117:5 We could call qemu_free_irq() in pci_ich9_uninit(), but since we have a method of initializing the IRQ that doesn't need manual freeing, use that instead: qemu_init_irq_child(). Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250821154053.2417090-4-peter.maydell@linaro.org> Signed-off-by: Philippe Mathieu-Daudé --- hw/ide/ich.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hw/ide/ich.c b/hw/ide/ich.c index 4cade0d121..b00987f08d 100644 --- a/hw/ide/ich.c +++ b/hw/ide/ich.c @@ -114,7 +114,8 @@ static void pci_ich9_ahci_init(Object *obj) { AHCIPCIState *d = ICH9_AHCI(obj); - qemu_init_irq(&d->irq, pci_ich9_ahci_update_irq, d, 0); + qemu_init_irq_child(obj, "update-irq", &d->irq, + pci_ich9_ahci_update_irq, d, 0); ahci_init(&d->ahci, DEVICE(obj)); d->ahci.irq = &d->irq; } From 3284d1c07cfd8d42aa27d1cf83d3e65fcd62e35e Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 21 Aug 2025 16:44:59 +0100 Subject: [PATCH 0299/2396] hw/gpio/pca9554: Avoid leak in pca9554_set_pin() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In pca9554_set_pin() we have a string property which we parse in order to set some non-string fields in the device state. So we call visit_type_str(), passing it the address of the local variable state_str. visit_type_str() will allocate a new copy of the string; we never free this string, so the result is a memory leak, detected by ASAN during a "make check" run: Direct leak of 5 byte(s) in 1 object(s) allocated from: #0 0x5d605212ede3 in malloc (/mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/arm-asan/qemu-system-arm+0x21f1de3) ( BuildId: 3d5373c89317f58bfcd191a33988c7347714be14) #1 0x7f7edea57b09 in g_malloc (/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x62b09) (BuildId: 1eb6131419edb83b2178b68282 9a6913cf682d75) #2 0x7f7edea6d4d8 in g_strdup (/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x784d8) (BuildId: 1eb6131419edb83b2178b68282 9a6913cf682d75) #3 0x5d6055289a91 in g_strdup_inline /usr/include/glib-2.0/glib/gstrfuncs.h:321:10 #4 0x5d6055289a91 in qobject_input_type_str /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/arm-asan/../../qapi/qo bject-input-visitor.c:542:12 #5 0x5d605528479c in visit_type_str /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/arm-asan/../../qapi/qapi-visit -core.c:349:10 #6 0x5d60528bdd87 in pca9554_set_pin /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/arm-asan/../../hw/gpio/pca9554.c:179:10 #7 0x5d60549bcbbb in object_property_set /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/arm-asan/../../qom/object.c:1450:5 #8 0x5d60549d2055 in object_property_set_qobject /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/arm-asan/../../qom/qom-qobject.c:28:10 #9 0x5d60549bcdf1 in object_property_set_str /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/arm-asan/../../qom/object.c:1458:15 #10 0x5d605439d077 in gb200nvl_bmc_i2c_init /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/arm-asan/../../hw/arm/aspeed.c:1267:5 #11 0x5d60543a3bbc in aspeed_machine_init /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/arm-asan/../../hw/arm/aspeed.c:493:9 Make the state_str g_autofree, so that we will always free it, on both error-exit and success codepaths. Cc: qemu-stable@nongnu.org Fixes: de0c7d543bca ("misc: Add a pca9554 GPIO device model") Signed-off-by: Peter Maydell Reviewed-by: Glenn Miles Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250821154459.2417976-1-peter.maydell@linaro.org> Signed-off-by: Philippe Mathieu-Daudé --- hw/gpio/pca9554.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/gpio/pca9554.c b/hw/gpio/pca9554.c index de3f883aee..eac0d23be3 100644 --- a/hw/gpio/pca9554.c +++ b/hw/gpio/pca9554.c @@ -174,7 +174,7 @@ static void pca9554_set_pin(Object *obj, Visitor *v, const char *name, PCA9554State *s = PCA9554(obj); int pin, rc, val; uint8_t state, mask; - char *state_str; + g_autofree char *state_str = NULL; if (!visit_type_str(v, name, &state_str, errp)) { return; From ac6b124180f7698084ef2a59282e8fa65a45f23b Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 21 Aug 2025 16:43:58 +0100 Subject: [PATCH 0300/2396] hw/char/max78000_uart: Destroy FIFO on deinit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In the max78000_uart we create a FIFO in the instance_init function, but we don't destroy it on deinit, so ASAN reports a leak in the device-introspect-test: #0 0x561cc92d5de3 in malloc (/mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/arm-asan/qemu-system-arm+0x21f1de3) (BuildId: 98fdf9fc85c3beaeca8eda0be8412f1e11b9c6ad) #1 0x70cbf2afab09 in g_malloc (/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x62b09) (BuildId: 1eb6131419edb83b2178b682829a6913cf682d75) #2 0x561ccc4c884d in fifo8_create /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/arm-asan/../../util/fifo8.c:27:18 #3 0x561cc9744ec9 in max78000_uart_init /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/arm-asan/../../hw/char/max78000_uart.c:241:5 Add an instance_finalize method to destroy the FIFO. Cc: qemu-stable@nongnu.org Fixes: d447e4b70295 ("MAX78000: UART Implementation") Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250821154358.2417744-1-peter.maydell@linaro.org> Signed-off-by: Philippe Mathieu-Daudé --- hw/char/max78000_uart.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/hw/char/max78000_uart.c b/hw/char/max78000_uart.c index 19506d52ef..c76c0e759b 100644 --- a/hw/char/max78000_uart.c +++ b/hw/char/max78000_uart.c @@ -247,6 +247,12 @@ static void max78000_uart_init(Object *obj) sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio); } +static void max78000_uart_finalize(Object *obj) +{ + Max78000UartState *s = MAX78000_UART(obj); + fifo8_destroy(&s->rx_fifo); +} + static void max78000_uart_realize(DeviceState *dev, Error **errp) { Max78000UartState *s = MAX78000_UART(dev); @@ -274,6 +280,7 @@ static const TypeInfo max78000_uart_info = { .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(Max78000UartState), .instance_init = max78000_uart_init, + .instance_finalize = max78000_uart_finalize, .class_init = max78000_uart_class_init, }; From 4dec497264c2e03b32fc82d6f24a694661b14d64 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 26 Aug 2025 18:49:55 +0100 Subject: [PATCH 0301/2396] hw/misc/xlnx-versal-cframe-reg: Free FIFO, g_tree on deinit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In the xlnx-versal-cframe-reg device we create a FIFO in instance_init but don't destroy it on deinit, causing ASAN to report a leak in the device-introspect-test: Direct leak of 400 byte(s) in 1 object(s) allocated from: #0 0x5aded4d54e23 in malloc (/mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/arm-asan/qemu-system-aarch64+0x24ffe23) (BuildId: 9f1e6c53fecd904ba5fc1f521d7da080a0e4103b) #1 0x71fbfac9bb09 in g_malloc (/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x62b09) (BuildId: 1eb6131419edb83b2178b682829a6913cf682d75) #2 0x5aded850059d in fifo8_create /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/arm-asan/../../util/fifo8.c:27:18 #3 0x5aded582b9e4 in fifo32_create /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/include/qemu/fifo32.h:35:5 #4 0x5aded582b326 in cframe_reg_init /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/arm-asan/../../hw/misc/xlnx-versal-cframe-reg.c:693:5 Similarly, we don't clean up the g_tree we create: Direct leak of 48 byte(s) in 1 object(s) allocated from: #0 0x5aded4d54e23 in malloc (/mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/arm-asan/qemu-system-aarch64+0x24ffe23) (BuildId: 9f1e6c5 3fecd904ba5fc1f521d7da080a0e4103b) #1 0x71fbfac9bb09 in g_malloc (/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x62b09) (BuildId: 1eb6131419edb83b2178b682829a6913cf682d75) #2 0x71fbfaccc799 in g_tree_new_full (/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x93799) (BuildId: 1eb6131419edb83b2178b682829a6913cf682d7 5) #3 0x5aded582b21a in cframe_reg_init /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/arm-asan/../../hw/misc/xlnx-versal-cframe-reg.c:691:18 Add an instance_finalize method to clean up what we allocated in instance_init. Signed-off-by: Peter Maydell Reviewed-by: Edgar E. Iglesias Reviewed-by: Manos Pitsidianakis Reviewed-by: Francisco Iglesias Message-ID: <20250826174956.3010274-2-peter.maydell@linaro.org> Signed-off-by: Philippe Mathieu-Daudé --- hw/misc/xlnx-versal-cframe-reg.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/hw/misc/xlnx-versal-cframe-reg.c b/hw/misc/xlnx-versal-cframe-reg.c index 1ce083e240..95e167b921 100644 --- a/hw/misc/xlnx-versal-cframe-reg.c +++ b/hw/misc/xlnx-versal-cframe-reg.c @@ -693,6 +693,14 @@ static void cframe_reg_init(Object *obj) fifo32_create(&s->new_f_data, FRAME_NUM_WORDS); } +static void cframe_reg_finalize(Object *obj) +{ + XlnxVersalCFrameReg *s = XLNX_VERSAL_CFRAME_REG(obj); + + fifo32_destroy(&s->new_f_data); + g_tree_destroy(s->cframes); +} + static const VMStateDescription vmstate_cframe = { .name = "cframe", .version_id = 1, @@ -833,6 +841,7 @@ static const TypeInfo cframe_reg_info = { .instance_size = sizeof(XlnxVersalCFrameReg), .class_init = cframe_reg_class_init, .instance_init = cframe_reg_init, + .instance_finalize = cframe_reg_finalize, .interfaces = (const InterfaceInfo[]) { { TYPE_XLNX_CFI_IF }, { } From 6592f710e4e1890a8a71e157266060bceacef6dd Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 26 Aug 2025 18:49:56 +0100 Subject: [PATCH 0302/2396] hw/display/xlnx_dp: Don't leak dpcd and edid objects MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In the xnlx_dp_init() function we create the s->dpcd and s->edid objects with qdev_new(); then in xlnx_dp_realize() we realize the dpcd with qdev_realize() and the edid with qdev_realize_and_unref(). This is inconsistent, and both ways result in a memory leak for the instance_init -> deinit lifecycle tested by device-introspect-test: Indirect leak of 1968 byte(s) in 1 object(s) allocated from: #0 0x5aded4d54e23 in malloc (/mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/arm-asan/qemu-system-aarch64+0x24ffe23) (BuildId: 9f1e6c5 3fecd904ba5fc1f521d7da080a0e4103b) #1 0x71fbfac9bb09 in g_malloc (/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x62b09) (BuildId: 1eb6131419edb83b2178b682829a6913cf682d75) #2 0x5aded7b9211c in object_new_with_type /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/arm-asan/../../qom/object.c:767:15 #3 0x5aded7b92240 in object_new /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/arm-asan/../../qom/object.c:789:12 #4 0x5aded7b773e4 in qdev_new /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/arm-asan/../../hw/core/qdev.c:149:19 #5 0x5aded54458be in xlnx_dp_init /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/arm-asan/../../hw/display/xlnx_dp.c:1272:20 Direct leak of 344 byte(s) in 1 object(s) allocated from: #0 0x5aded4d54e23 in malloc (/mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/arm-asan/qemu-system-aarch64+0x24ffe23) (BuildId: 9f1e6c53fecd904ba5fc1f521d7da080a0e4103b) #1 0x71fbfac9bb09 in g_malloc (/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x62b09) (BuildId: 1eb6131419edb83b2178b682829a6913cf682d75) #2 0x5aded7b9211c in object_new_with_type /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/arm-asan/../../qom/object.c:767:15 #3 0x5aded7b92240 in object_new /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/arm-asan/../../qom/object.c:789:12 #4 0x5aded7b773e4 in qdev_new /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/arm-asan/../../hw/core/qdev.c:149:19 #5 0x5aded5445a56 in xlnx_dp_init /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/arm-asan/../../hw/display/xlnx_dp.c:1275:22 Instead, explicitly object_unref() after we have added the objects as child properties of the device. This means they will automatically be freed when this device is deinited. When we do this, qdev_realize() is the correct way to realize them in xlnx_dp_realize(). Signed-off-by: Peter Maydell Reviewed-by: Francisco Iglesias Reviewed-by: Manos Pitsidianakis Reviewed-by: Edgar E. Iglesias Message-ID: <20250826174956.3010274-3-peter.maydell@linaro.org> Signed-off-by: Philippe Mathieu-Daudé --- hw/display/xlnx_dp.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/hw/display/xlnx_dp.c b/hw/display/xlnx_dp.c index 7c980ee642..ef73e1815f 100644 --- a/hw/display/xlnx_dp.c +++ b/hw/display/xlnx_dp.c @@ -1267,14 +1267,18 @@ static void xlnx_dp_init(Object *obj) s->aux_bus = aux_bus_init(DEVICE(obj), "aux"); /* - * Initialize DPCD and EDID.. + * Initialize DPCD and EDID. Once we have added the objects as + * child properties of this device, we can drop the reference we + * hold to them, leaving the child-property as the only reference. */ s->dpcd = DPCD(qdev_new("dpcd")); object_property_add_child(OBJECT(s), "dpcd", OBJECT(s->dpcd)); + object_unref(s->dpcd); s->edid = I2CDDC(qdev_new("i2c-ddc")); i2c_slave_set_address(I2C_SLAVE(s->edid), 0x50); object_property_add_child(OBJECT(s), "edid", OBJECT(s->edid)); + object_unref(s->edid); fifo8_create(&s->rx_fifo, 16); fifo8_create(&s->tx_fifo, 16); @@ -1311,8 +1315,8 @@ static void xlnx_dp_realize(DeviceState *dev, Error **errp) qdev_realize(DEVICE(s->dpcd), BUS(s->aux_bus), &error_fatal); aux_map_slave(AUX_SLAVE(s->dpcd), 0x0000); - qdev_realize_and_unref(DEVICE(s->edid), BUS(aux_get_i2c_bus(s->aux_bus)), - &error_fatal); + qdev_realize(DEVICE(s->edid), BUS(aux_get_i2c_bus(s->aux_bus)), + &error_fatal); s->console = graphic_console_init(dev, 0, &xlnx_dp_gfx_ops, s); surface = qemu_console_surface(s->console); From acba1ebcad9a0dd8c08495edaf5b8ce6a748bb01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 28 Aug 2025 16:24:16 +0200 Subject: [PATCH 0303/2396] hw/mips: Remove mipssim machine MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The "mipssim" machine is deprecated since commit facfc943cb9 ("hw/mips: Mark the "mipssim" machine as deprecated"), released in v10.0; time to remove. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Ján Tomko Reviewed-by: Richard Henderson Reviewed-by: Jiaxun Yang Message-Id: <20250828143800.49842-2-philmd@linaro.org> --- MAINTAINERS | 1 - configs/devices/mips-softmmu/common.mak | 1 - docs/about/deprecated.rst | 12 -- docs/about/removed-features.rst | 5 + docs/system/target-mips.rst | 11 -- hw/mips/Kconfig | 7 - hw/mips/meson.build | 1 - hw/mips/mipssim.c | 249 ------------------------ 8 files changed, 5 insertions(+), 282 deletions(-) delete mode 100644 hw/mips/mipssim.c diff --git a/MAINTAINERS b/MAINTAINERS index 040ef06494..3d3bbc0026 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1414,7 +1414,6 @@ F: tests/functional/mips*/test_tuxrun.py Mipssim R: Aleksandar Rikalo S: Orphan -F: hw/mips/mipssim.c F: hw/net/mipsnet.c Fuloong 2E diff --git a/configs/devices/mips-softmmu/common.mak b/configs/devices/mips-softmmu/common.mak index b50107feaf..cdeae7ce45 100644 --- a/configs/devices/mips-softmmu/common.mak +++ b/configs/devices/mips-softmmu/common.mak @@ -6,4 +6,3 @@ # Boards are selected by default, uncomment to keep out of the build. # CONFIG_MALTA=n -# CONFIG_MIPSSIM=n diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst index 6ae6920681..b2420732e1 100644 --- a/docs/about/deprecated.rst +++ b/docs/about/deprecated.rst @@ -313,18 +313,6 @@ and serves as the initial engineering sample rather than a production version. A newer revision, A1, is now supported, and the ``ast2700a1-evb`` should replace the older A0 version. -Mips ``mipssim`` machine (since 10.0) -''''''''''''''''''''''''''''''''''''' - -Linux dropped support for this virtual machine type in kernel v3.7, and -there does not seem to be anybody around who is still using this board -in QEMU: Most former MIPS-related people are working on other architectures -in their everyday job nowadays, and we are also not aware of anybody still -using old binaries with this board (i.e. there is also no binary available -online to check that this board did not completely bitrot yet). It is -recommended to use another MIPS machine for future MIPS code development -instead. - RISC-V default machine option (since 10.0) '''''''''''''''''''''''''''''''''''''''''' diff --git a/docs/about/removed-features.rst b/docs/about/removed-features.rst index 332d07e2b1..dc3d4eaa2d 100644 --- a/docs/about/removed-features.rst +++ b/docs/about/removed-features.rst @@ -1107,6 +1107,11 @@ were added for little endian CPUs. Big endian support was never tested and likely never worked. Starting with QEMU v10.1, the machines are now only available as little-endian machines. +Mips ``mipssim`` machine (removed in 10.2) +'''''''''''''''''''''''''''''''''''''''''' + +Linux dropped support for this virtual machine type in kernel v3.7, and +there was also no binary available online to use with that board. linux-user mode CPUs -------------------- diff --git a/docs/system/target-mips.rst b/docs/system/target-mips.rst index 9028c3b304..2a152e1338 100644 --- a/docs/system/target-mips.rst +++ b/docs/system/target-mips.rst @@ -12,8 +12,6 @@ machine types are emulated: - An ACER Pica \"pica61\". This machine needs the 64-bit emulator. -- MIPS emulator pseudo board \"mipssim\" - - A MIPS Magnum R4000 machine \"magnum\". This machine needs the 64-bit emulator. @@ -80,15 +78,6 @@ The Loongson-3 virtual platform emulation supports: - Both KVM and TCG supported -The mipssim pseudo board emulation provides an environment similar to -what the proprietary MIPS emulator uses for running Linux. It supports: - -- A range of MIPS CPUs, default is the 24Kf - -- PC style serial port - -- MIPSnet network emulation - .. include:: cpu-models-mips.rst.inc .. _nanoMIPS-System-emulator: diff --git a/hw/mips/Kconfig b/hw/mips/Kconfig index f84fffcd32..b59cb2f111 100644 --- a/hw/mips/Kconfig +++ b/hw/mips/Kconfig @@ -13,13 +13,6 @@ config MALTA select SERIAL_MM select SMBUS_EEPROM -config MIPSSIM - bool - default y - depends on MIPS - select SERIAL_MM - select MIPSNET - config JAZZ bool default y diff --git a/hw/mips/meson.build b/hw/mips/meson.build index 31dbd2bf4d..390f0fd7f9 100644 --- a/hw/mips/meson.build +++ b/hw/mips/meson.build @@ -8,7 +8,6 @@ mips_ss.add(when: 'CONFIG_MIPS_CPS', if_true: files('cps.c')) if 'CONFIG_TCG' in config_all_accel mips_ss.add(when: 'CONFIG_JAZZ', if_true: files('jazz.c')) -mips_ss.add(when: 'CONFIG_MIPSSIM', if_true: files('mipssim.c')) mips_ss.add(when: 'CONFIG_FULOONG', if_true: files('fuloong2e.c')) mips_ss.add(when: 'CONFIG_MIPS_BOSTON', if_true: files('boston.c')) endif diff --git a/hw/mips/mipssim.c b/hw/mips/mipssim.c deleted file mode 100644 index e843307b9b..0000000000 --- a/hw/mips/mipssim.c +++ /dev/null @@ -1,249 +0,0 @@ -/* - * QEMU/mipssim emulation - * - * Emulates a very simple machine model similar to the one used by the - * proprietary MIPS emulator. - * - * Copyright (c) 2007 Thiemo Seufer - * - * 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 "qapi/error.h" -#include "qemu/datadir.h" -#include "system/address-spaces.h" -#include "hw/clock.h" -#include "hw/mips/mips.h" -#include "hw/char/serial-mm.h" -#include "net/net.h" -#include "system/system.h" -#include "hw/boards.h" -#include "hw/loader.h" -#include "elf.h" -#include "hw/sysbus.h" -#include "hw/qdev-properties.h" -#include "qemu/error-report.h" -#include "system/qtest.h" -#include "system/reset.h" -#include "cpu.h" - -#define BIOS_SIZE (4 * MiB) - -static struct _loaderparams { - int ram_size; - const char *kernel_filename; - const char *kernel_cmdline; - const char *initrd_filename; -} loaderparams; - -typedef struct ResetData { - MIPSCPU *cpu; - uint64_t vector; -} ResetData; - -static uint64_t load_kernel(void) -{ - uint64_t entry, kernel_high, initrd_size; - long kernel_size; - ram_addr_t initrd_offset; - - kernel_size = load_elf(loaderparams.kernel_filename, NULL, - cpu_mips_kseg0_to_phys, NULL, - &entry, NULL, - &kernel_high, NULL, - TARGET_BIG_ENDIAN ? ELFDATA2MSB : ELFDATA2LSB, - EM_MIPS, 1, 0); - if (kernel_size < 0) { - error_report("could not load kernel '%s': %s", - loaderparams.kernel_filename, - load_elf_strerror(kernel_size)); - exit(1); - } - - /* load initrd */ - initrd_size = 0; - initrd_offset = 0; - if (loaderparams.initrd_filename) { - initrd_size = get_image_size(loaderparams.initrd_filename); - if (initrd_size > 0) { - initrd_offset = ROUND_UP(kernel_high, INITRD_PAGE_SIZE); - if (initrd_offset + initrd_size > loaderparams.ram_size) { - error_report("memory too small for initial ram disk '%s'", - loaderparams.initrd_filename); - exit(1); - } - initrd_size = load_image_targphys(loaderparams.initrd_filename, - initrd_offset, loaderparams.ram_size - initrd_offset); - } - if (initrd_size == (target_ulong) -1) { - error_report("could not load initial ram disk '%s'", - loaderparams.initrd_filename); - exit(1); - } - } - return entry; -} - -static void main_cpu_reset(void *opaque) -{ - ResetData *s = (ResetData *)opaque; - CPUMIPSState *env = &s->cpu->env; - - cpu_reset(CPU(s->cpu)); - env->active_tc.PC = s->vector & ~(target_ulong)1; - if (s->vector & 1) { - env->hflags |= MIPS_HFLAG_M16; - } -} - -static void mipsnet_init(int base, qemu_irq irq) -{ - DeviceState *dev; - SysBusDevice *s; - - dev = qemu_create_nic_device("mipsnet", true, NULL); - if (!dev) { - return; - } - - s = SYS_BUS_DEVICE(dev); - sysbus_realize_and_unref(s, &error_fatal); - sysbus_connect_irq(s, 0, irq); - memory_region_add_subregion(get_system_io(), - base, - sysbus_mmio_get_region(s, 0)); -} - -static void -mips_mipssim_init(MachineState *machine) -{ - const char *kernel_filename = machine->kernel_filename; - const char *kernel_cmdline = machine->kernel_cmdline; - const char *initrd_filename = machine->initrd_filename; - const char *bios_name = TARGET_BIG_ENDIAN ? "mips_bios.bin" - : "mipsel_bios.bin"; - char *filename; - MemoryRegion *address_space_mem = get_system_memory(); - MemoryRegion *isa = g_new(MemoryRegion, 1); - MemoryRegion *bios = g_new(MemoryRegion, 1); - Clock *cpuclk; - MIPSCPU *cpu; - CPUMIPSState *env; - ResetData *reset_info; - int bios_size; - - cpuclk = clock_new(OBJECT(machine), "cpu-refclk"); -#ifdef TARGET_MIPS64 - clock_set_hz(cpuclk, 6000000); /* 6 MHz */ -#else - clock_set_hz(cpuclk, 12000000); /* 12 MHz */ -#endif - - /* Init CPUs. */ - cpu = mips_cpu_create_with_clock(machine->cpu_type, cpuclk, - TARGET_BIG_ENDIAN); - env = &cpu->env; - - reset_info = g_new0(ResetData, 1); - reset_info->cpu = cpu; - reset_info->vector = env->active_tc.PC; - qemu_register_reset(main_cpu_reset, reset_info); - - /* Allocate RAM. */ - memory_region_init_rom(bios, NULL, "mips_mipssim.bios", BIOS_SIZE, - &error_fatal); - - memory_region_add_subregion(address_space_mem, 0, machine->ram); - - /* Map the BIOS / boot exception handler. */ - memory_region_add_subregion(address_space_mem, 0x1fc00000LL, bios); - /* Load a BIOS / boot exception handler image. */ - filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, - machine->firmware ?: bios_name); - if (filename) { - bios_size = load_image_targphys(filename, 0x1fc00000LL, BIOS_SIZE); - g_free(filename); - } else { - bios_size = -1; - } - if ((bios_size < 0 || bios_size > BIOS_SIZE) && - machine->firmware && !qtest_enabled()) { - /* Bail out if we have neither a kernel image nor boot vector code. */ - error_report("Could not load MIPS bios '%s'", machine->firmware); - exit(1); - } else { - /* We have a boot vector start address. */ - env->active_tc.PC = (target_long)(int32_t)0xbfc00000; - } - - if (kernel_filename) { - loaderparams.ram_size = machine->ram_size; - loaderparams.kernel_filename = kernel_filename; - loaderparams.kernel_cmdline = kernel_cmdline; - loaderparams.initrd_filename = initrd_filename; - reset_info->vector = load_kernel(); - } - - /* Init CPU internal devices. */ - cpu_mips_irq_init_cpu(cpu); - cpu_mips_clock_init(cpu); - - /* - * Register 64 KB of ISA IO space at 0x1fd00000. But without interrupts - * (except for the hardcoded serial port interrupt) -device cannot work, - * so do not expose the ISA bus to the user. - */ - memory_region_init_alias(isa, NULL, "isa_mmio", - get_system_io(), 0, 0x00010000); - memory_region_add_subregion(get_system_memory(), 0x1fd00000, isa); - - /* - * A single 16450 sits at offset 0x3f8. It is attached to - * MIPS CPU INT2, which is interrupt 4. - */ - if (serial_hd(0)) { - DeviceState *dev = qdev_new(TYPE_SERIAL_MM); - - qdev_prop_set_chr(dev, "chardev", serial_hd(0)); - qdev_prop_set_uint8(dev, "regshift", 0); - qdev_prop_set_uint8(dev, "endianness", DEVICE_LITTLE_ENDIAN); - sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); - sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, env->irq[4]); - memory_region_add_subregion(get_system_io(), 0x3f8, - sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0)); - } - - /* MIPSnet uses the MIPS CPU INT0, which is interrupt 2. */ - mipsnet_init(0x4200, env->irq[2]); -} - -static void mips_mipssim_machine_init(MachineClass *mc) -{ - mc->desc = "MIPS MIPSsim platform"; - mc->init = mips_mipssim_init; -#ifdef TARGET_MIPS64 - mc->default_cpu_type = MIPS_CPU_TYPE_NAME("5Kf"); -#else - mc->default_cpu_type = MIPS_CPU_TYPE_NAME("24Kf"); -#endif - mc->default_ram_id = "mips_mipssim.ram"; -} - -DEFINE_MACHINE("mipssim", mips_mipssim_machine_init) From 60c8ee1a6d6ad89dd55f3066062dc788f4a419dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 28 Aug 2025 16:25:56 +0200 Subject: [PATCH 0304/2396] hw/net: Remove mipsnet device model MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The mipsnet device model was only used by the mipssim machine, which just got removed. Remove as now dead code. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Ján Tomko Reviewed-by: Richard Henderson Reviewed-by: Jiaxun Yang Message-Id: <20250828143800.49842-3-philmd@linaro.org> --- MAINTAINERS | 5 - hw/net/Kconfig | 3 - hw/net/meson.build | 1 - hw/net/mipsnet.c | 297 -------------------------------------------- hw/net/trace-events | 7 -- 5 files changed, 313 deletions(-) delete mode 100644 hw/net/mipsnet.c diff --git a/MAINTAINERS b/MAINTAINERS index 3d3bbc0026..1ae28e8804 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1411,11 +1411,6 @@ F: include/hw/southbridge/piix.h F: tests/functional/mips*/test_malta.py F: tests/functional/mips*/test_tuxrun.py -Mipssim -R: Aleksandar Rikalo -S: Orphan -F: hw/net/mipsnet.c - Fuloong 2E M: Huacai Chen M: Philippe Mathieu-Daudé diff --git a/hw/net/Kconfig b/hw/net/Kconfig index 7f80218d10..2b513d6895 100644 --- a/hw/net/Kconfig +++ b/hw/net/Kconfig @@ -82,9 +82,6 @@ config OPENCORES_ETH config XGMAC bool -config MIPSNET - bool - config ALLWINNER_EMAC bool diff --git a/hw/net/meson.build b/hw/net/meson.build index e6759e26ca..913eaedbc5 100644 --- a/hw/net/meson.build +++ b/hw/net/meson.build @@ -23,7 +23,6 @@ system_ss.add(when: 'CONFIG_LAN9118_PHY', if_true: files('lan9118_phy.c')) system_ss.add(when: 'CONFIG_NE2000_ISA', if_true: files('ne2000-isa.c')) system_ss.add(when: 'CONFIG_OPENCORES_ETH', if_true: files('opencores_eth.c')) system_ss.add(when: 'CONFIG_XGMAC', if_true: files('xgmac.c')) -system_ss.add(when: 'CONFIG_MIPSNET', if_true: files('mipsnet.c')) system_ss.add(when: 'CONFIG_XILINX_AXI', if_true: files('xilinx_axienet.c')) system_ss.add(when: 'CONFIG_ALLWINNER_EMAC', if_true: files('allwinner_emac.c')) system_ss.add(when: 'CONFIG_ALLWINNER_SUN8I_EMAC', if_true: files('allwinner-sun8i-emac.c')) diff --git a/hw/net/mipsnet.c b/hw/net/mipsnet.c deleted file mode 100644 index 583aa1c7de..0000000000 --- a/hw/net/mipsnet.c +++ /dev/null @@ -1,297 +0,0 @@ -#include "qemu/osdep.h" -#include "hw/irq.h" -#include "hw/qdev-properties.h" -#include "net/net.h" -#include "qemu/module.h" -#include "trace.h" -#include "hw/sysbus.h" -#include "migration/vmstate.h" -#include "qom/object.h" - -/* MIPSnet register offsets */ - -#define MIPSNET_DEV_ID 0x00 -#define MIPSNET_BUSY 0x08 -#define MIPSNET_RX_DATA_COUNT 0x0c -#define MIPSNET_TX_DATA_COUNT 0x10 -#define MIPSNET_INT_CTL 0x14 -# define MIPSNET_INTCTL_TXDONE 0x00000001 -# define MIPSNET_INTCTL_RXDONE 0x00000002 -# define MIPSNET_INTCTL_TESTBIT 0x80000000 -#define MIPSNET_INTERRUPT_INFO 0x18 -#define MIPSNET_RX_DATA_BUFFER 0x1c -#define MIPSNET_TX_DATA_BUFFER 0x20 - -#define MAX_ETH_FRAME_SIZE 1514 - -#define TYPE_MIPS_NET "mipsnet" -OBJECT_DECLARE_SIMPLE_TYPE(MIPSnetState, MIPS_NET) - -struct MIPSnetState { - SysBusDevice parent_obj; - - uint32_t busy; - uint32_t rx_count; - uint32_t rx_read; - uint32_t tx_count; - uint32_t tx_written; - uint32_t intctl; - uint8_t rx_buffer[MAX_ETH_FRAME_SIZE]; - uint8_t tx_buffer[MAX_ETH_FRAME_SIZE]; - MemoryRegion io; - qemu_irq irq; - NICState *nic; - NICConf conf; -}; - -static void mipsnet_reset(MIPSnetState *s) -{ - s->busy = 1; - s->rx_count = 0; - s->rx_read = 0; - s->tx_count = 0; - s->tx_written = 0; - s->intctl = 0; - memset(s->rx_buffer, 0, MAX_ETH_FRAME_SIZE); - memset(s->tx_buffer, 0, MAX_ETH_FRAME_SIZE); -} - -static void mipsnet_update_irq(MIPSnetState *s) -{ - int isr = !!s->intctl; - trace_mipsnet_irq(isr, s->intctl); - qemu_set_irq(s->irq, isr); -} - -static int mipsnet_buffer_full(MIPSnetState *s) -{ - if (s->rx_count >= MAX_ETH_FRAME_SIZE) { - return 1; - } - return 0; -} - -static int mipsnet_can_receive(NetClientState *nc) -{ - MIPSnetState *s = qemu_get_nic_opaque(nc); - - if (s->busy) { - return 0; - } - return !mipsnet_buffer_full(s); -} - -static ssize_t mipsnet_receive(NetClientState *nc, - const uint8_t *buf, size_t size) -{ - MIPSnetState *s = qemu_get_nic_opaque(nc); - - trace_mipsnet_receive(size); - if (!mipsnet_can_receive(nc)) { - return 0; - } - - if (size >= sizeof(s->rx_buffer)) { - return 0; - } - s->busy = 1; - - /* Just accept everything. */ - - /* Write packet data. */ - memcpy(s->rx_buffer, buf, size); - - s->rx_count = size; - s->rx_read = 0; - - /* Now we can signal we have received something. */ - s->intctl |= MIPSNET_INTCTL_RXDONE; - mipsnet_update_irq(s); - - return size; -} - -static uint64_t mipsnet_ioport_read(void *opaque, hwaddr addr, - unsigned int size) -{ - MIPSnetState *s = opaque; - int ret = 0; - - addr &= 0x3f; - switch (addr) { - case MIPSNET_DEV_ID: - ret = be32_to_cpu(0x4d495053); /* MIPS */ - break; - case MIPSNET_DEV_ID + 4: - ret = be32_to_cpu(0x4e455430); /* NET0 */ - break; - case MIPSNET_BUSY: - ret = s->busy; - break; - case MIPSNET_RX_DATA_COUNT: - ret = s->rx_count; - break; - case MIPSNET_TX_DATA_COUNT: - ret = s->tx_count; - break; - case MIPSNET_INT_CTL: - ret = s->intctl; - s->intctl &= ~MIPSNET_INTCTL_TESTBIT; - break; - case MIPSNET_INTERRUPT_INFO: - /* XXX: This seems to be a per-VPE interrupt number. */ - ret = 0; - break; - case MIPSNET_RX_DATA_BUFFER: - if (s->rx_count) { - s->rx_count--; - ret = s->rx_buffer[s->rx_read++]; - if (mipsnet_can_receive(s->nic->ncs)) { - qemu_flush_queued_packets(qemu_get_queue(s->nic)); - } - } - break; - /* Reads as zero. */ - case MIPSNET_TX_DATA_BUFFER: - default: - break; - } - trace_mipsnet_read(addr, ret); - return ret; -} - -static void mipsnet_ioport_write(void *opaque, hwaddr addr, - uint64_t val, unsigned int size) -{ - MIPSnetState *s = opaque; - - addr &= 0x3f; - trace_mipsnet_write(addr, val); - switch (addr) { - case MIPSNET_TX_DATA_COUNT: - s->tx_count = (val <= MAX_ETH_FRAME_SIZE) ? val : 0; - s->tx_written = 0; - break; - case MIPSNET_INT_CTL: - if (val & MIPSNET_INTCTL_TXDONE) { - s->intctl &= ~MIPSNET_INTCTL_TXDONE; - } else if (val & MIPSNET_INTCTL_RXDONE) { - s->intctl &= ~MIPSNET_INTCTL_RXDONE; - } else if (val & MIPSNET_INTCTL_TESTBIT) { - mipsnet_reset(s); - s->intctl |= MIPSNET_INTCTL_TESTBIT; - } else if (!val) { - /* ACK testbit interrupt, flag was cleared on read. */ - } - s->busy = !!s->intctl; - mipsnet_update_irq(s); - if (mipsnet_can_receive(s->nic->ncs)) { - qemu_flush_queued_packets(qemu_get_queue(s->nic)); - } - break; - case MIPSNET_TX_DATA_BUFFER: - s->tx_buffer[s->tx_written++] = val; - if ((s->tx_written >= MAX_ETH_FRAME_SIZE) - || (s->tx_written == s->tx_count)) { - /* Send buffer. */ - trace_mipsnet_send(s->tx_written); - qemu_send_packet(qemu_get_queue(s->nic), - s->tx_buffer, s->tx_written); - s->tx_count = s->tx_written = 0; - s->intctl |= MIPSNET_INTCTL_TXDONE; - s->busy = 1; - mipsnet_update_irq(s); - } - break; - /* Read-only registers */ - case MIPSNET_DEV_ID: - case MIPSNET_BUSY: - case MIPSNET_RX_DATA_COUNT: - case MIPSNET_INTERRUPT_INFO: - case MIPSNET_RX_DATA_BUFFER: - default: - break; - } -} - -static const VMStateDescription vmstate_mipsnet = { - .name = "mipsnet", - .version_id = 0, - .minimum_version_id = 0, - .fields = (const VMStateField[]) { - VMSTATE_UINT32(busy, MIPSnetState), - VMSTATE_UINT32(rx_count, MIPSnetState), - VMSTATE_UINT32(rx_read, MIPSnetState), - VMSTATE_UINT32(tx_count, MIPSnetState), - VMSTATE_UINT32(tx_written, MIPSnetState), - VMSTATE_UINT32(intctl, MIPSnetState), - VMSTATE_BUFFER(rx_buffer, MIPSnetState), - VMSTATE_BUFFER(tx_buffer, MIPSnetState), - VMSTATE_END_OF_LIST() - } -}; - -static NetClientInfo net_mipsnet_info = { - .type = NET_CLIENT_DRIVER_NIC, - .size = sizeof(NICState), - .receive = mipsnet_receive, -}; - -static const MemoryRegionOps mipsnet_ioport_ops = { - .read = mipsnet_ioport_read, - .write = mipsnet_ioport_write, - .impl.min_access_size = 1, - .impl.max_access_size = 4, -}; - -static void mipsnet_realize(DeviceState *dev, Error **errp) -{ - SysBusDevice *sbd = SYS_BUS_DEVICE(dev); - MIPSnetState *s = MIPS_NET(dev); - - memory_region_init_io(&s->io, OBJECT(dev), &mipsnet_ioport_ops, s, - "mipsnet-io", 36); - sysbus_init_mmio(sbd, &s->io); - sysbus_init_irq(sbd, &s->irq); - - s->nic = qemu_new_nic(&net_mipsnet_info, &s->conf, - object_get_typename(OBJECT(dev)), dev->id, - &dev->mem_reentrancy_guard, s); - qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); -} - -static void mipsnet_sysbus_reset(DeviceState *dev) -{ - MIPSnetState *s = MIPS_NET(dev); - mipsnet_reset(s); -} - -static const Property mipsnet_properties[] = { - DEFINE_NIC_PROPERTIES(MIPSnetState, conf), -}; - -static void mipsnet_class_init(ObjectClass *klass, const void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->realize = mipsnet_realize; - set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); - dc->desc = "MIPS Simulator network device"; - device_class_set_legacy_reset(dc, mipsnet_sysbus_reset); - dc->vmsd = &vmstate_mipsnet; - device_class_set_props(dc, mipsnet_properties); -} - -static const TypeInfo mipsnet_info = { - .name = TYPE_MIPS_NET, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(MIPSnetState), - .class_init = mipsnet_class_init, -}; - -static void mipsnet_register_types(void) -{ - type_register_static(&mipsnet_info); -} - -type_init(mipsnet_register_types) diff --git a/hw/net/trace-events b/hw/net/trace-events index 72b69c4a8b..e82d7490c3 100644 --- a/hw/net/trace-events +++ b/hw/net/trace-events @@ -20,13 +20,6 @@ lan9118_phy_reset(void) "" lance_mem_readw(uint64_t addr, uint32_t ret) "addr=0x%"PRIx64"val=0x%04x" lance_mem_writew(uint64_t addr, uint32_t val) "addr=0x%"PRIx64"val=0x%04x" -# mipsnet.c -mipsnet_send(uint32_t size) "sending len=%u" -mipsnet_receive(uint32_t size) "receiving len=%u" -mipsnet_read(uint64_t addr, uint32_t val) "read addr=0x%" PRIx64 " val=0x%x" -mipsnet_write(uint64_t addr, uint64_t val) "write addr=0x%" PRIx64 " val=0x%" PRIx64 -mipsnet_irq(uint32_t isr, uint32_t intctl) "set irq to %d (0x%02x)" - # ne2000.c ne2000_read(uint64_t addr, uint64_t val) "read addr=0x%" PRIx64 " val=0x%" PRIx64 ne2000_write(uint64_t addr, uint64_t val) "write addr=0x%" PRIx64 " val=0x%" PRIx64 From 79d472a51015f9c9ab341a5f56b8c450870c006b Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Mon, 1 Sep 2025 07:56:22 +0200 Subject: [PATCH 0305/2396] hw/sd/sdcard: Add validation for boot-partition-size MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make sure we are not silently rounding down or even wrapping around, causing inconsistencies with the provided image. Signed-off-by: Jan Kiszka Reviewed-by: Alex Bennée [PMD: Use g_autofree, suggested by Alex] Message-ID: <1fff448da042bdf8cff7733ce67cadff4c540f1d.1756706188.git.jan.kiszka@siemens.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/sd/sd.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 8b142e4796..5603b391bf 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -2810,6 +2810,15 @@ static void sd_realize(DeviceState *dev, Error **errp) } blk_set_dev_ops(sd->blk, &sd_block_ops, sd); } + if (sd->boot_part_size % (128 * KiB) || + sd->boot_part_size > 255 * 128 * KiB) { + g_autofree char *size_str = size_to_str(sd->boot_part_size); + + error_setg(errp, "Invalid boot partition size: %s", size_str); + error_append_hint(errp, + "The boot partition size must be multiples of 128K" + "and not larger than 32640K.\n"); + } } static void emmc_realize(DeviceState *dev, Error **errp) From e2d7c1a3cdc46d6b2e8afa8db8a7ef4c2740a2fe Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Mon, 1 Sep 2025 07:56:24 +0200 Subject: [PATCH 0306/2396] hw/sd/sdcard: Refactor sd_bootpart_offset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This function provides the offset for any partition in the block image, not only the boot partitions, therefore rename it. Align the constant names with the numbering scheme in the standard and use constants for both boot partitions for consistency reasons. There is also no reason to return early if boot_part_size is zero because the existing code will provide the right value in that case as well. Signed-off-by: Jan Kiszka Reviewed-by: Philippe Mathieu-Daudé Message-ID: <66e9b07476aad61820c4f42f4f984cc90752ba5e.1756706188.git.jan.kiszka@siemens.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/sd/sd.c | 16 ++++++++-------- hw/sd/sdmmc-internal.h | 3 ++- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 5603b391bf..d7a496d77c 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -833,14 +833,14 @@ static uint32_t sd_blk_len(SDState *sd) /* * This requires a disk image that has two boot partitions inserted at the - * beginning of it. The size of the boot partitions is the "boot-size" - * property. + * beginning of it, followed by an RPMB partition. The size of the boot + * partitions is the "boot-partition-size" property. */ -static uint32_t sd_bootpart_offset(SDState *sd) +static uint32_t sd_part_offset(SDState *sd) { unsigned partition_access; - if (!sd->boot_part_size || !sd_is_emmc(sd)) { + if (!sd_is_emmc(sd)) { return 0; } @@ -849,9 +849,9 @@ static uint32_t sd_bootpart_offset(SDState *sd) switch (partition_access) { case EXT_CSD_PART_CONFIG_ACC_DEFAULT: return sd->boot_part_size * 2; - case EXT_CSD_PART_CONFIG_ACC_BOOT0: + case EXT_CSD_PART_CONFIG_ACC_BOOT1: return 0; - case EXT_CSD_PART_CONFIG_ACC_BOOT0 + 1: + case EXT_CSD_PART_CONFIG_ACC_BOOT2: return sd->boot_part_size * 1; default: g_assert_not_reached(); @@ -1052,7 +1052,7 @@ static const VMStateDescription sd_vmstate = { static void sd_blk_read(SDState *sd, uint64_t addr, uint32_t len) { trace_sdcard_read_block(addr, len); - addr += sd_bootpart_offset(sd); + addr += sd_part_offset(sd); if (!sd->blk || blk_pread(sd->blk, addr, len, sd->data, 0) < 0) { fprintf(stderr, "sd_blk_read: read error on host side\n"); } @@ -1061,7 +1061,7 @@ static void sd_blk_read(SDState *sd, uint64_t addr, uint32_t len) static void sd_blk_write(SDState *sd, uint64_t addr, uint32_t len) { trace_sdcard_write_block(addr, len); - addr += sd_bootpart_offset(sd); + addr += sd_part_offset(sd); if (!sd->blk || blk_pwrite(sd->blk, addr, len, sd->data, 0) < 0) { fprintf(stderr, "sd_blk_write: write error on host side\n"); } diff --git a/hw/sd/sdmmc-internal.h b/hw/sd/sdmmc-internal.h index 91eb5b6b2f..ce6bc4e6ec 100644 --- a/hw/sd/sdmmc-internal.h +++ b/hw/sd/sdmmc-internal.h @@ -116,7 +116,8 @@ DECLARE_OBJ_CHECKERS(SDState, SDCardClass, SDMMC_COMMON, TYPE_SDMMC_COMMON) #define EXT_CSD_PART_CONFIG_ACC_MASK (0x7) #define EXT_CSD_PART_CONFIG_ACC_DEFAULT (0x0) -#define EXT_CSD_PART_CONFIG_ACC_BOOT0 (0x1) +#define EXT_CSD_PART_CONFIG_ACC_BOOT1 (0x1) +#define EXT_CSD_PART_CONFIG_ACC_BOOT2 (0x2) #define EXT_CSD_PART_CONFIG_EN_MASK (0x7 << 3) #define EXT_CSD_PART_CONFIG_EN_BOOT0 (0x1 << 3) From 22ece1a6ebf5d9e0b0a3e376dcfbfe9d96d209b9 Mon Sep 17 00:00:00 2001 From: Jan Kiszka Date: Mon, 1 Sep 2025 07:56:26 +0200 Subject: [PATCH 0307/2396] crypto/hmac: Allow to build hmac over multiple qcrypto_gnutls_hmac_bytes[v] calls MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If the buffers that should be considered for building the hmac are not available at the same time, the current API is unsuitable. Extend it so that passing a NULL pointer as result_len is used as indicator that further buffers will be passed in succeeding calls to qcrypto_gnutls_hmac_bytes[v]. Signed-off-by: Jan Kiszka Reviewed-by: Philippe Mathieu-Daudé Message-ID: <2d3539c247a6c323491a3821f0e5b6fc382a4686.1756706188.git.jan.kiszka@siemens.com> Signed-off-by: Philippe Mathieu-Daudé --- crypto/hmac-gcrypt.c | 4 +++- crypto/hmac-glib.c | 4 +++- crypto/hmac-gnutls.c | 4 +++- crypto/hmac-nettle.c | 4 +++- include/crypto/hmac.h | 12 ++++++++++++ 5 files changed, 24 insertions(+), 4 deletions(-) diff --git a/crypto/hmac-gcrypt.c b/crypto/hmac-gcrypt.c index 5273086eb9..e428d17479 100644 --- a/crypto/hmac-gcrypt.c +++ b/crypto/hmac-gcrypt.c @@ -121,7 +121,9 @@ qcrypto_gcrypt_hmac_bytesv(QCryptoHmac *hmac, return -1; } - if (*resultlen == 0) { + if (resultlen == NULL) { + return 0; + } else if (*resultlen == 0) { *resultlen = ret; *result = g_new0(uint8_t, *resultlen); } else if (*resultlen != ret) { diff --git a/crypto/hmac-glib.c b/crypto/hmac-glib.c index ea80c8d1b2..b845133a05 100644 --- a/crypto/hmac-glib.c +++ b/crypto/hmac-glib.c @@ -104,7 +104,9 @@ qcrypto_glib_hmac_bytesv(QCryptoHmac *hmac, return -1; } - if (*resultlen == 0) { + if (resultlen == NULL) { + return 0; + } else if (*resultlen == 0) { *resultlen = ret; *result = g_new0(uint8_t, *resultlen); } else if (*resultlen != ret) { diff --git a/crypto/hmac-gnutls.c b/crypto/hmac-gnutls.c index 822995505c..3c5bcbe80b 100644 --- a/crypto/hmac-gnutls.c +++ b/crypto/hmac-gnutls.c @@ -119,7 +119,9 @@ qcrypto_gnutls_hmac_bytesv(QCryptoHmac *hmac, return -1; } - if (*resultlen == 0) { + if (resultlen == NULL) { + return 0; + } else if (*resultlen == 0) { *resultlen = ret; *result = g_new0(uint8_t, *resultlen); } else if (*resultlen != ret) { diff --git a/crypto/hmac-nettle.c b/crypto/hmac-nettle.c index dd5b2ab7a1..2cff7931e1 100644 --- a/crypto/hmac-nettle.c +++ b/crypto/hmac-nettle.c @@ -164,7 +164,9 @@ qcrypto_nettle_hmac_bytesv(QCryptoHmac *hmac, } } - if (*resultlen == 0) { + if (resultlen == NULL) { + return 0; + } else if (*resultlen == 0) { *resultlen = qcrypto_hmac_alg_map[hmac->alg].len; *result = g_new0(uint8_t, *resultlen); } else if (*resultlen != qcrypto_hmac_alg_map[hmac->alg].len) { diff --git a/include/crypto/hmac.h b/include/crypto/hmac.h index da8a1e3ceb..af3d5f8feb 100644 --- a/include/crypto/hmac.h +++ b/include/crypto/hmac.h @@ -90,6 +90,12 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC(QCryptoHmac, qcrypto_hmac_free) * The memory referenced in @result must be released with a call * to g_free() when no longer required by the caller. * + * If @result_len is set to a NULL pointer, no result will be returned, and + * the hmac object can be used for further invocations of qcrypto_hmac_bytes() + * or qcrypto_hmac_bytesv() until a non-NULL pointer is provided. This allows + * to build the hmac across memory regions that are not available at the same + * time. + * * Returns: * 0 on success, -1 on error */ @@ -123,6 +129,12 @@ int qcrypto_hmac_bytesv(QCryptoHmac *hmac, * The memory referenced in @result must be released with a call * to g_free() when no longer required by the caller. * + * If @result_len is set to a NULL pointer, no result will be returned, and + * the hmac object can be used for further invocations of qcrypto_hmac_bytes() + * or qcrypto_hmac_bytesv() until a non-NULL pointer is provided. This allows + * to build the hmac across memory regions that are not available at the same + * time. + * * Returns: * 0 on success, -1 on error */ From 319ca84949fc3134774342d50790592680c3b9b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Mon, 1 Sep 2025 08:46:24 +0200 Subject: [PATCH 0308/2396] hw/arm/virt: Include 'system/system.h' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit hw/arm/virt.c should include 'system/system.h' for : serial_hd() qemu_add_machine_init_done_notifier() Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Peter Maydell Link: https://lore.kernel.org/qemu-devel/20250731144019.1403591-1-clg@redhat.com Signed-off-by: Cédric Le Goater Message-ID: <20250901064631.530723-2-clg@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/arm/virt.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 1e63f40fbe..e5c4142e82 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -50,6 +50,7 @@ #include "system/kvm.h" #include "system/hvf.h" #include "system/qtest.h" +#include "system/system.h" #include "hw/loader.h" #include "qapi/error.h" #include "qemu/bitops.h" From 02423bc9d329b7ff274aa2cf7da544dc339d9724 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Mon, 1 Sep 2025 08:46:25 +0200 Subject: [PATCH 0309/2396] hw/isa/superio: Include 'system/system.h' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Files using serial_hd() should include 'system/system.h'. Fix that. Cc: Michael S. Tsirkin Cc: Paolo Bonzini Signed-off-by: Cédric Le Goater Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250901064631.530723-3-clg@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/isa/isa-superio.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/isa/isa-superio.c b/hw/isa/isa-superio.c index 2853485977..941b0f91d7 100644 --- a/hw/isa/isa-superio.c +++ b/hw/isa/isa-superio.c @@ -15,6 +15,7 @@ #include "qemu/module.h" #include "qapi/error.h" #include "system/blockdev.h" +#include "system/system.h" #include "chardev/char.h" #include "hw/char/parallel.h" #include "hw/block/fdc.h" From f4e39e06d1c8cfc0cfd4d2f839d85f568072435d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Mon, 1 Sep 2025 08:46:26 +0200 Subject: [PATCH 0310/2396] hw/mips/loongson3_virt: Include 'system/system.h' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Files using serial_hd() should include 'system/system.h'. Fix that. Cc: Philippe Mathieu-Daudé Cc: Huacai Chen Cc: Jiaxun Yang Signed-off-by: Cédric Le Goater Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250901064631.530723-4-clg@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/mips/loongson3_virt.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/mips/loongson3_virt.c b/hw/mips/loongson3_virt.c index de6fbcc0cb..672083dec9 100644 --- a/hw/mips/loongson3_virt.c +++ b/hw/mips/loongson3_virt.c @@ -49,6 +49,7 @@ #include "system/qtest.h" #include "system/reset.h" #include "system/runstate.h" +#include "system/system.h" #include "qemu/error-report.h" #define PM_CNTL_MODE 0x10 From 42ab9014a9de94fc8c0aa97b1822230a5ee96bfa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Mon, 1 Sep 2025 08:46:27 +0200 Subject: [PATCH 0311/2396] hw/mips/malta: Include 'system/system.h' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Files using serial_hd() should include 'system/system.h'. Fix that. Cc: Philippe Mathieu-Daudé Cc: Aurelien Jarno Cc: Jiaxun Yang Signed-off-by: Cédric Le Goater Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250901064631.530723-5-clg@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/mips/malta.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/mips/malta.c b/hw/mips/malta.c index cbdbb21056..344dc8ca76 100644 --- a/hw/mips/malta.c +++ b/hw/mips/malta.c @@ -52,6 +52,7 @@ #include "system/qtest.h" #include "system/reset.h" #include "system/runstate.h" +#include "system/system.h" #include "qapi/error.h" #include "qemu/error-report.h" #include "system/kvm.h" From 21dca6e6c79e28dc05f7a0722895618b489223a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 1 Sep 2025 09:27:35 +0200 Subject: [PATCH 0312/2396] docs/about/removed-features: Clarify 'device_add' is removed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All other titles in removed-features.rst mention when the feature was removed using "removed in". Use that instead of "since" which we use for when a feature is deprecated. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Daniel P. Berrangé Reviewed-by: Markus Armbruster Message-Id: <20250901113957.17113-1-philmd@linaro.org> --- docs/about/removed-features.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/about/removed-features.rst b/docs/about/removed-features.rst index dc3d4eaa2d..fff781d6b7 100644 --- a/docs/about/removed-features.rst +++ b/docs/about/removed-features.rst @@ -730,8 +730,8 @@ Use ``multifd-channels`` instead. Use ``multifd-compression`` instead. -Incorrectly typed ``device_add`` arguments (since 9.2) -'''''''''''''''''''''''''''''''''''''''''''''''''''''' +Incorrectly typed ``device_add`` arguments (removed in 9.2) +''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' Due to shortcomings in the internal implementation of ``device_add``, QEMU used to incorrectly accept certain invalid arguments. Any object From 7e52554c293184083f571265daacfc9aa57c3d55 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 1 Sep 2025 11:22:14 +0100 Subject: [PATCH 0313/2396] hw/arm/boot: Correctly free the MemoryDeviceInfoList MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When running the bios-tables-test under ASAN we see leaks like this: Direct leak of 16 byte(s) in 1 object(s) allocated from: #0 0x5bc58579b00d in calloc (/mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/arm-asan/qemu-system-aarch64+0x250400d) (BuildId: 2e27b63dc9ac45f522ced40a17c2a60cc32f1d38) #1 0x7b4ad90337b1 in g_malloc0 (/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x637b1) (BuildId: 1eb6131419edb83b2178b682829a6913cf682d75) #2 0x5bc5861826db in qmp_memory_device_list /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/arm-asan/../../hw/mem/memory-device.c:307:34 #3 0x5bc587a9edb6 in arm_load_dtb /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/arm-asan/../../hw/arm/boot.c:656:15 Indirect leak of 28 byte(s) in 2 object(s) allocated from: #0 0x5bc58579ae23 in malloc (/mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/arm-asan/qemu-system-aarch64+0x2503e23) (BuildId: 2e27b63dc9ac45f522ced40a17c2a60cc32f1d38) #1 0x7b4ad6c8f947 in __vasprintf_internal libio/vasprintf.c:116:16 #2 0x7b4ad9080a52 in g_vasprintf (/lib/x86_64-linux-gnu/libglib-2.0.so.0+0xb0a52) (BuildId: 1eb6131419edb83b2178b682829a6913cf682d75) #3 0x7b4ad90515e4 in g_strdup_vprintf (/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x815e4) (BuildId: 1eb6131419edb83b2178b682829a6913cf682d75) #4 0x7b4ad9051940 in g_strdup_printf (/lib/x86_64-linux-gnu/libglib-2.0.so.0+0x81940) (BuildId: 1eb6131419edb83b2178b682829a6913cf682d75) #5 0x5bc5885eb739 in object_get_canonical_path /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/arm-asan/../../qom/object.c:2123:19 #6 0x5bc58618dca8 in pc_dimm_md_fill_device_info /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/arm-asan/../../hw/mem/pc-dimm.c:268:18 #7 0x5bc586182792 in qmp_memory_device_list /mnt/nvmedisk/linaro/qemu-from-laptop/qemu/build/arm-asan/../../hw/mem/memory-device.c:310:9 This happens because we declared the MemoryDeviceInfoList *md_list with g_autofree, which will free the direct memory with g_free() but doesn't free all the other data structures referenced by it. Instead what we want is to declare the pointer with g_autoptr(), which will automatically call the qapi_free_MemoryDeviceInfoList() cleanup function when the variable goes out of scope. Fixes: 36bc78aca83cfd ("hw/arm: add static NVDIMMs in device tree") Signed-off-by: Peter Maydell Reviewed-by: Manos Pitsidianakis Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250901102214.3748011-1-peter.maydell@linaro.org> Signed-off-by: Philippe Mathieu-Daudé --- hw/arm/boot.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/arm/boot.c b/hw/arm/boot.c index 1e57c4ab9e..d0840308f5 100644 --- a/hw/arm/boot.c +++ b/hw/arm/boot.c @@ -549,7 +549,7 @@ int arm_load_dtb(hwaddr addr, const struct arm_boot_info *binfo, unsigned int i; hwaddr mem_base, mem_len; char **node_path; - g_autofree MemoryDeviceInfoList *md_list = NULL; + g_autoptr(MemoryDeviceInfoList) md_list = NULL; Error *err = NULL; if (binfo->dtb_filename) { From e502e614f4c3e5ee7b12cf1c926d9581262fd626 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Mon, 1 Sep 2025 21:31:58 +0100 Subject: [PATCH 0314/2396] hw/i386/pc_piix.c: remove unnecessary if() from pc_init1() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that the isapc logic has been split out of pc_piix.c, the PCI Host Bridge (phb) object is now always set in pc_init1(). Since phb is now guaranteed not to be NULL, Coverity reports that the if() statement surrounding ioapic_init_gsi() is now unnecessary and can be removed along with the phb NULL initialiser. Coverity: CID 1620557 Signed-off-by: Mark Cave-Ayland Fixes: 99d0630a45 ("hw/i386/pc_piix.c: assume pcmc->pci_enabled is always true in pc_init1()") Reviewed-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250901203409.1196620-1-mark.caveayland@nutanix.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/i386/pc_piix.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 7e78b6daa6..caf8bab68e 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -106,7 +106,7 @@ static void pc_init1(MachineState *machine, const char *pci_type) X86MachineState *x86ms = X86_MACHINE(machine); MemoryRegion *system_memory = get_system_memory(); MemoryRegion *system_io = get_system_io(); - Object *phb = NULL; + Object *phb; ISABus *isa_bus; Object *piix4_pm = NULL; qemu_irq smi_irq; @@ -284,9 +284,7 @@ static void pc_init1(MachineState *machine, const char *pci_type) pc_i8259_create(isa_bus, gsi_state->i8259_irq); } - if (phb) { - ioapic_init_gsi(gsi_state, phb); - } + ioapic_init_gsi(gsi_state, phb); if (tcg_enabled()) { x86_register_ferr_irq(x86ms->gsi[13]); From 1566b8c8df9e8603f5d03cc1a7708c4ecfda0897 Mon Sep 17 00:00:00 2001 From: Stefan Weil via Date: Sat, 9 Aug 2025 08:13:02 +0200 Subject: [PATCH 0315/2396] chardev/baum: Fix compiler warning for Windows builds Compiler warning: ../chardev/baum.c:657:25: warning: comparison between pointer and integer Use brlapi_fileDescriptor instead of int for brlapi_fd and BRLAPI_INVALID_FILE_DESCRIPTOR instead of -1. Signed-off-by: Stefan Weil Reviewed-by: Samuel Thibault Reviewed-by: Michael Tokarev Signed-off-by: Michael Tokarev --- chardev/baum.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/chardev/baum.c b/chardev/baum.c index f3e8cd27f0..ad68321504 100644 --- a/chardev/baum.c +++ b/chardev/baum.c @@ -94,7 +94,7 @@ struct BaumChardev { Chardev parent; brlapi_handle_t *brlapi; - int brlapi_fd; + brlapi_fileDescriptor brlapi_fd; unsigned int x, y; bool deferred_init; @@ -654,7 +654,7 @@ static void baum_chr_open(Chardev *chr, baum->brlapi = handle; baum->brlapi_fd = brlapi__openConnection(handle, NULL, NULL); - if (baum->brlapi_fd == -1) { + if (baum->brlapi_fd == BRLAPI_INVALID_FILE_DESCRIPTOR) { error_setg(errp, "brlapi__openConnection: %s", brlapi_strerror(brlapi_error_location())); g_free(handle); @@ -665,6 +665,10 @@ static void baum_chr_open(Chardev *chr, baum->cellCount_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, baum_cellCount_timer_cb, baum); + /* + * On Windows, brlapi_fd is a pointer, which is being used here + * as an integer, but in practice it seems to work + */ qemu_set_fd_handler(baum->brlapi_fd, baum_chr_read, NULL, baum); } From 606978500c3d18fb89a49844f253097b17f757de Mon Sep 17 00:00:00 2001 From: Michael Tokarev Date: Sun, 24 Aug 2025 03:05:32 +0300 Subject: [PATCH 0316/2396] block/curl: fix curl internal handles handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit block/curl.c uses CURLMOPT_SOCKETFUNCTION to register a socket callback. According to the documentation, this callback is called not just with application-created sockets but also with internal curl sockets, - and for such sockets, user data pointer is not set by the application, so the result qemu crashing. Pass BDRVCURLState directly to the callback function as user pointer, instead of relying on CURLINFO_PRIVATE. This problem started happening with update of libcurl from 8.9 to 8.10 -- apparently with this change curl started using private handles more. (CURLINFO_PRIVATE is used in one more place, in curl_multi_check_completion() - it might need a similar fix too) Resolves: https://gitlab.com/qemu-project/qemu/-/issues/3081 Cc: qemu-stable@qemu.org Reviewed-by: Daniel P. Berrangé Signed-off-by: Michael Tokarev --- block/curl.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/block/curl.c b/block/curl.c index 5467678024..00b949ea45 100644 --- a/block/curl.c +++ b/block/curl.c @@ -162,13 +162,9 @@ static int curl_timer_cb(CURLM *multi, long timeout_ms, void *opaque) static int curl_sock_cb(CURL *curl, curl_socket_t fd, int action, void *userp, void *sp) { - BDRVCURLState *s; - CURLState *state = NULL; + BDRVCURLState *s = userp; CURLSocket *socket; - curl_easy_getinfo(curl, CURLINFO_PRIVATE, (char **)&state); - s = state->s; - socket = g_hash_table_lookup(s->sockets, GINT_TO_POINTER(fd)); if (!socket) { socket = g_new0(CURLSocket, 1); @@ -605,6 +601,7 @@ static void curl_attach_aio_context(BlockDriverState *bs, assert(!s->multi); s->multi = curl_multi_init(); s->aio_context = new_context; + curl_multi_setopt(s->multi, CURLMOPT_SOCKETDATA, s); curl_multi_setopt(s->multi, CURLMOPT_SOCKETFUNCTION, curl_sock_cb); curl_multi_setopt(s->multi, CURLMOPT_TIMERDATA, s); curl_multi_setopt(s->multi, CURLMOPT_TIMERFUNCTION, curl_timer_cb); From 29e68f41c064299d4b45f3517c2e4400b1c17231 Mon Sep 17 00:00:00 2001 From: Michael Tokarev Date: Mon, 25 Aug 2025 12:52:46 +0300 Subject: [PATCH 0317/2396] block/curl: drop old/unuspported curl version checks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We currently require libcurl >=7.29.0 (since f9cd86fe72be3cd8). Drop older LIBCURL_VERSION_NUM checks from the driver. Reviewed-by: Daniel P. Berrangé Signed-off-by: Michael Tokarev --- block/curl.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/block/curl.c b/block/curl.c index 00b949ea45..e0f98e035a 100644 --- a/block/curl.c +++ b/block/curl.c @@ -516,7 +516,7 @@ static int curl_init_state(BDRVCURLState *s, CURLState *state) CURLOPT_REDIR_PROTOCOLS_STR, PROTOCOLS)) { goto err; } -#elif LIBCURL_VERSION_NUM >= 0x071304 +#else if (curl_easy_setopt(state->curl, CURLOPT_PROTOCOLS, PROTOCOLS) || curl_easy_setopt(state->curl, CURLOPT_REDIR_PROTOCOLS, PROTOCOLS)) { goto err; @@ -821,22 +821,11 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags, goto out; } #endif - /* Prior CURL 7.19.4 return value of 0 could mean that the file size is not - * know or the size is zero. From 7.19.4 CURL returns -1 if size is not - * known and zero if it is really zero-length file. */ -#if LIBCURL_VERSION_NUM >= 0x071304 if (cl < 0) { pstrcpy(state->errmsg, CURL_ERROR_SIZE, "Server didn't report file size."); goto out; } -#else - if (cl <= 0) { - pstrcpy(state->errmsg, CURL_ERROR_SIZE, - "Unknown file size or zero-length file."); - goto out; - } -#endif s->len = cl; From 0ac122d933323610b3dc7ce846cb47ba48d78266 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 11 Aug 2025 11:43:41 +0200 Subject: [PATCH 0318/2396] scripts/coverity-scan/COMPONENTS.md: Add a 'plugins' category MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cover the TCG plugins files under their own Coverity category. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Peter Maydell Acked-by: Alex Bennée Reviewed-by: Michael Tokarev Signed-off-by: Michael Tokarev --- scripts/coverity-scan/COMPONENTS.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/scripts/coverity-scan/COMPONENTS.md b/scripts/coverity-scan/COMPONENTS.md index 72995903ff..95805b536b 100644 --- a/scripts/coverity-scan/COMPONENTS.md +++ b/scripts/coverity-scan/COMPONENTS.md @@ -147,6 +147,9 @@ tcg system ~ .*/qemu(/system/.*|/accel/.*) +plugins + ~ .*/qemu(/contrib|/tests/tcg)?/plugins/.* + (headers) ~ .*/qemu(/include/.*) From 25fef09ce17ac1ae22638a0b57d97c2bd5cd7d83 Mon Sep 17 00:00:00 2001 From: Aditya Gupta Date: Wed, 27 Aug 2025 11:02:28 +0530 Subject: [PATCH 0319/2396] docs: fix typo in xive doc "Interrupt Pending Buffer" IPB, which got written as IBP due to typo. The "IPB" register is also mentioned in same doc multiple times. Signed-off-by: Aditya Gupta Reviewed-by: Thomas Huth Reviewed-by: Michael Tokarev Signed-off-by: Michael Tokarev --- docs/specs/ppc-xive.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/specs/ppc-xive.rst b/docs/specs/ppc-xive.rst index 83d43f658b..968cc760d4 100644 --- a/docs/specs/ppc-xive.rst +++ b/docs/specs/ppc-xive.rst @@ -157,7 +157,7 @@ Interrupt flow from an O/S perspective After an event data has been enqueued in the O/S Event Queue, the IVPE raises the bit corresponding to the priority of the pending interrupt -in the register IBP (Interrupt Pending Buffer) to indicate that an +in the register IPB (Interrupt Pending Buffer) to indicate that an event is pending in one of the 8 priority queues. The Pending Interrupt Priority Register (PIPR) is also updated using the IPB. This register represent the priority of the most favored pending From 27ea28a0b369b4b14a485a5d6f045e0dc1db4e38 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 29 Aug 2025 13:49:06 +0000 Subject: [PATCH 0320/2396] tcg/arm: Fix tgen_deposit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When converting from tcg_out_deposit, the arguments were not shuffled properly. Cc: qemu-stable@nongnu.org Fixes: cf4905c03135f1181e8 ("tcg: Convert deposit to TCGOutOpDeposit") Reported-by: Michael Tokarev Tested-by: Michael Tokarev Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/arm/tcg-target.c.inc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tcg/arm/tcg-target.c.inc b/tcg/arm/tcg-target.c.inc index 836894b16a..338c57b061 100644 --- a/tcg/arm/tcg-target.c.inc +++ b/tcg/arm/tcg-target.c.inc @@ -975,7 +975,8 @@ static void tgen_deposit(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2, unsigned ofs, unsigned len) { /* bfi/bfc */ - tcg_out32(s, 0x07c00010 | (COND_AL << 28) | (a0 << 12) | a1 + tcg_debug_assert(a0 == a1); + tcg_out32(s, 0x07c00010 | (COND_AL << 28) | (a0 << 12) | a2 | (ofs << 7) | ((ofs + len - 1) << 16)); } From b8eb3dd49583729edceb18628e626eac15a15de4 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 5 Aug 2025 11:40:31 +1000 Subject: [PATCH 0321/2396] cpuinfo/i386: Detect GFNI as an AVX extension We won't use the SSE GFNI instructions, so delay detection until we know AVX is present. Signed-off-by: Richard Henderson --- host/include/i386/host/cpuinfo.h | 1 + include/qemu/cpuid.h | 3 +++ util/cpuinfo-i386.c | 1 + 3 files changed, 5 insertions(+) diff --git a/host/include/i386/host/cpuinfo.h b/host/include/i386/host/cpuinfo.h index 9541a64da6..93d029d499 100644 --- a/host/include/i386/host/cpuinfo.h +++ b/host/include/i386/host/cpuinfo.h @@ -27,6 +27,7 @@ #define CPUINFO_ATOMIC_VMOVDQU (1u << 17) #define CPUINFO_AES (1u << 18) #define CPUINFO_PCLMUL (1u << 19) +#define CPUINFO_GFNI (1u << 20) /* Initialized with a constructor. */ extern unsigned cpuinfo; diff --git a/include/qemu/cpuid.h b/include/qemu/cpuid.h index b11161555b..de7a900509 100644 --- a/include/qemu/cpuid.h +++ b/include/qemu/cpuid.h @@ -68,6 +68,9 @@ #ifndef bit_AVX512VBMI2 #define bit_AVX512VBMI2 (1 << 6) #endif +#ifndef bit_GFNI +#define bit_GFNI (1 << 8) +#endif /* Leaf 0x80000001, %ecx */ #ifndef bit_LZCNT diff --git a/util/cpuinfo-i386.c b/util/cpuinfo-i386.c index c8c8a1b370..f4c5b6ff40 100644 --- a/util/cpuinfo-i386.c +++ b/util/cpuinfo-i386.c @@ -50,6 +50,7 @@ unsigned __attribute__((constructor)) cpuinfo_init(void) if ((bv & 6) == 6) { info |= CPUINFO_AVX1; info |= (b7 & bit_AVX2 ? CPUINFO_AVX2 : 0); + info |= (c7 & bit_GFNI ? CPUINFO_GFNI : 0); if ((bv & 0xe0) == 0xe0) { info |= (b7 & bit_AVX512F ? CPUINFO_AVX512F : 0); From 26c41cc4a3d998caa700407a27e18755a6e1895c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 27 Aug 2025 20:33:27 +1000 Subject: [PATCH 0322/2396] tcg/i386: Expand sari of bits-1 as pcmpgt Expand arithmetic right shift of bits-1 as a comparison vs 0. Suggested-by: Paolo Bonzini Signed-off-by: Richard Henderson --- tcg/i386/tcg-target.c.inc | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tcg/i386/tcg-target.c.inc b/tcg/i386/tcg-target.c.inc index 088c6c9264..4cd5d4276c 100644 --- a/tcg/i386/tcg-target.c.inc +++ b/tcg/i386/tcg-target.c.inc @@ -4357,6 +4357,12 @@ static void expand_vec_sari(TCGType type, unsigned vece, { TCGv_vec t1, t2; + if (imm >= (8 << vece) - 1) { + tcg_gen_cmp_vec(TCG_COND_LT, vece, v0, v1, + tcg_constant_vec(type, MO_64, 0)); + return; + } + switch (vece) { case MO_8: /* Unpack to 16-bit, shift, and repack. */ From ba8a86d67aca43a854c78380c917845059c83d4c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 27 Aug 2025 20:38:40 +1000 Subject: [PATCH 0323/2396] tcg/i386: Use canonical operand ordering in expand_vec_sari MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The optimizer prefers to have constants as the second operand, so expand LT x,0 instead of GT 0,x. This will not affect the generated code at all. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/i386/tcg-target.c.inc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tcg/i386/tcg-target.c.inc b/tcg/i386/tcg-target.c.inc index 4cd5d4276c..8260c35edd 100644 --- a/tcg/i386/tcg-target.c.inc +++ b/tcg/i386/tcg-target.c.inc @@ -4399,8 +4399,8 @@ static void expand_vec_sari(TCGType type, unsigned vece, /* Otherwise we will need to use a compare vs 0 to produce * the sign-extend, shift and merge. */ - tcg_gen_cmp_vec(TCG_COND_GT, MO_64, t1, - tcg_constant_vec(type, MO_64, 0), v1); + tcg_gen_cmp_vec(TCG_COND_LT, MO_64, t1, v1, + tcg_constant_vec(type, MO_64, 0)); tcg_gen_shri_vec(MO_64, v0, v1, imm); tcg_gen_shli_vec(MO_64, t1, t1, 64 - imm); tcg_gen_or_vec(MO_64, v0, v0, t1); From 6c76a1f687cd509d26dae44ea39bf396b251fe0e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 5 Aug 2025 11:56:33 +1000 Subject: [PATCH 0324/2396] tcg/i386: Add INDEX_op_x86_vgf2p8affineqb_vec Add a backend-specific opcode for expanding the GFNI vgf2p8affineqb instruction, which we can use for expanding 8-bit immediate shifts and rotates. Signed-off-by: Richard Henderson --- tcg/i386/tcg-target-opc.h.inc | 1 + tcg/i386/tcg-target.c.inc | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/tcg/i386/tcg-target-opc.h.inc b/tcg/i386/tcg-target-opc.h.inc index 8cc0dbaeaf..8a5cb34dbe 100644 --- a/tcg/i386/tcg-target-opc.h.inc +++ b/tcg/i386/tcg-target-opc.h.inc @@ -35,3 +35,4 @@ DEF(x86_punpckh_vec, 1, 2, 0, TCG_OPF_VECTOR) DEF(x86_vpshldi_vec, 1, 2, 1, TCG_OPF_VECTOR) DEF(x86_vpshldv_vec, 1, 3, 0, TCG_OPF_VECTOR) DEF(x86_vpshrdv_vec, 1, 3, 0, TCG_OPF_VECTOR) +DEF(x86_vgf2p8affineqb_vec, 1, 2, 1, TCG_OPF_VECTOR) diff --git a/tcg/i386/tcg-target.c.inc b/tcg/i386/tcg-target.c.inc index 8260c35edd..efaca0ca67 100644 --- a/tcg/i386/tcg-target.c.inc +++ b/tcg/i386/tcg-target.c.inc @@ -451,6 +451,7 @@ static bool tcg_target_const_match(int64_t val, int ct, #define OPC_VPBROADCASTW (0x79 | P_EXT38 | P_DATA16) #define OPC_VPBROADCASTD (0x58 | P_EXT38 | P_DATA16) #define OPC_VPBROADCASTQ (0x59 | P_EXT38 | P_DATA16) +#define OPC_VGF2P8AFFINEQB (0xce | P_EXT3A | P_DATA16 | P_VEXW) #define OPC_VPMOVM2B (0x28 | P_EXT38 | P_SIMDF3 | P_EVEX) #define OPC_VPMOVM2W (0x28 | P_EXT38 | P_SIMDF3 | P_VEXW | P_EVEX) #define OPC_VPMOVM2D (0x38 | P_EXT38 | P_SIMDF3 | P_EVEX) @@ -4084,6 +4085,10 @@ static void tcg_out_vec_op(TCGContext *s, TCGOpcode opc, insn = vpshldi_insn[vece]; sub = args[3]; goto gen_simd_imm8; + case INDEX_op_x86_vgf2p8affineqb_vec: + insn = OPC_VGF2P8AFFINEQB; + sub = args[3]; + goto gen_simd_imm8; case INDEX_op_not_vec: insn = OPC_VPTERNLOGQ; @@ -4188,6 +4193,7 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_x86_punpckl_vec: case INDEX_op_x86_punpckh_vec: case INDEX_op_x86_vpshldi_vec: + case INDEX_op_x86_vgf2p8affineqb_vec: #if TCG_TARGET_REG_BITS == 32 case INDEX_op_dup2_vec: #endif From cb2540979264c8d3984e26c5dd90a840e47ec5dd Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 5 Aug 2025 15:21:44 +1000 Subject: [PATCH 0325/2396] tcg/i386: Use vgf2p8affineqb for MO_8 vector shifts A constant matrix can describe the movement of the 8 bits, so these shifts can be performed with one instruction. Logic courtesy of Andi Kleen : https://gcc.gnu.org/pipermail/gcc-patches/2025-August/691624.html Signed-off-by: Richard Henderson --- tcg/i386/tcg-target.c.inc | 75 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 71 insertions(+), 4 deletions(-) diff --git a/tcg/i386/tcg-target.c.inc b/tcg/i386/tcg-target.c.inc index efaca0ca67..ee27266861 100644 --- a/tcg/i386/tcg-target.c.inc +++ b/tcg/i386/tcg-target.c.inc @@ -4342,12 +4342,46 @@ int tcg_can_emit_vec_op(TCGOpcode opc, TCGType type, unsigned vece) } } +static void gen_vgf2p8affineqb0(TCGType type, TCGv_vec v0, + TCGv_vec v1, uint64_t matrix) +{ + vec_gen_4(INDEX_op_x86_vgf2p8affineqb_vec, type, MO_8, + tcgv_vec_arg(v0), tcgv_vec_arg(v1), + tcgv_vec_arg(tcg_constant_vec(type, MO_64, matrix)), 0); +} + static void expand_vec_shi(TCGType type, unsigned vece, bool right, TCGv_vec v0, TCGv_vec v1, TCGArg imm) { + static const uint64_t gf2_shi[2][8] = { + /* left shift */ + { 0, + 0x0001020408102040ull, + 0x0000010204081020ull, + 0x0000000102040810ull, + 0x0000000001020408ull, + 0x0000000000010204ull, + 0x0000000000000102ull, + 0x0000000000000001ull }, + /* right shift */ + { 0, + 0x0204081020408000ull, + 0x0408102040800000ull, + 0x0810204080000000ull, + 0x1020408000000000ull, + 0x2040800000000000ull, + 0x4080000000000000ull, + 0x8000000000000000ull } + }; uint8_t mask; tcg_debug_assert(vece == MO_8); + + if (cpuinfo & CPUINFO_GFNI) { + gen_vgf2p8affineqb0(type, v0, v1, gf2_shi[right][imm]); + return; + } + if (right) { mask = 0xff >> imm; tcg_gen_shri_vec(MO_16, v0, v1, imm); @@ -4361,6 +4395,16 @@ static void expand_vec_shi(TCGType type, unsigned vece, bool right, static void expand_vec_sari(TCGType type, unsigned vece, TCGv_vec v0, TCGv_vec v1, TCGArg imm) { + static const uint64_t gf2_sar[8] = { + 0, + 0x0204081020408080ull, + 0x0408102040808080ull, + 0x0810204080808080ull, + 0x1020408080808080ull, + 0x2040808080808080ull, + 0x4080808080808080ull, + 0x8080808080808080ull, + }; TCGv_vec t1, t2; if (imm >= (8 << vece) - 1) { @@ -4371,6 +4415,11 @@ static void expand_vec_sari(TCGType type, unsigned vece, switch (vece) { case MO_8: + if (cpuinfo & CPUINFO_GFNI) { + gen_vgf2p8affineqb0(type, v0, v1, gf2_sar[imm]); + break; + } + /* Unpack to 16-bit, shift, and repack. */ t1 = tcg_temp_new_vec(type); t2 = tcg_temp_new_vec(type); @@ -4422,12 +4471,30 @@ static void expand_vec_sari(TCGType type, unsigned vece, static void expand_vec_rotli(TCGType type, unsigned vece, TCGv_vec v0, TCGv_vec v1, TCGArg imm) { + static const uint64_t gf2_rol[8] = { + 0, + 0x8001020408102040ull, + 0x4080010204081020ull, + 0x2040800102040810ull, + 0x1020408001020408ull, + 0x0810204080010204ull, + 0x0408102040800102ull, + 0x0204081020408001ull, + }; TCGv_vec t; - if (vece != MO_8 && have_avx512vbmi2) { - vec_gen_4(INDEX_op_x86_vpshldi_vec, type, vece, - tcgv_vec_arg(v0), tcgv_vec_arg(v1), tcgv_vec_arg(v1), imm); - return; + if (vece == MO_8) { + if (cpuinfo & CPUINFO_GFNI) { + gen_vgf2p8affineqb0(type, v0, v1, gf2_rol[imm]); + return; + } + } else { + if (have_avx512vbmi2) { + vec_gen_4(INDEX_op_x86_vpshldi_vec, type, vece, + tcgv_vec_arg(v0), tcgv_vec_arg(v1), + tcgv_vec_arg(v1), imm); + return; + } } t = tcg_temp_new_vec(type); From 889bf9a067804211c2fc4d09a8dc1a66f1472f89 Mon Sep 17 00:00:00 2001 From: Glenn Miles Date: Tue, 5 Aug 2025 16:36:31 -0500 Subject: [PATCH 0326/2396] MAINTAINERS: Add myself as reviewer for PowerNV and XIVE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adding myself as reviewer for PowerNV and XIVE areas. Signed-off-by: Glenn Miles Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250805213646.3285026-1-milesg@linux.ibm.com Signed-off-by: Cédric Le Goater --- MAINTAINERS | 2 ++ 1 file changed, 2 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 1ae28e8804..fb045388b9 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1575,6 +1575,7 @@ F: tests/functional/ppc64/test_tuxrun.py PowerNV (Non-Virtualized) M: Nicholas Piggin R: Aditya Gupta +R: Glenn Miles L: qemu-ppc@nongnu.org S: Odd Fixes F: docs/system/ppc/powernv.rst @@ -2781,6 +2782,7 @@ T: git https://github.com/philmd/qemu.git fw_cfg-next XIVE R: Gautam Menghani +R: Glenn Miles L: qemu-ppc@nongnu.org S: Odd Fixes F: hw/*/*xive* From 432ca3dfa3d57a7bf1e427576fcfca4ab0079a50 Mon Sep 17 00:00:00 2001 From: Tomita Moeko Date: Thu, 14 Aug 2025 00:05:10 +0800 Subject: [PATCH 0327/2396] vfio/igd: Enable quirks when IGD is not the primary display MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since linux 6.15, commit 41112160ca87 ("vfio/pci: match IGD devices in display controller class"), IGD related regions are also exposed when IGD is not primary display (device class is Display controller). Allow IGD quirks to be enabled in this configuration so that guests can have display output on IGD when it is not the primary display. Signed-off-by: Tomita Moeko Reviewed-by: Alex Williamson Link: https://lore.kernel.org/qemu-devel/20250813160510.23553-1-tomitamoeko@gmail.com Signed-off-by: Cédric Le Goater --- hw/vfio/igd.c | 7 ++++--- hw/vfio/pci.h | 5 +++++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/hw/vfio/igd.c b/hw/vfio/igd.c index ee0767b0b8..f116c40ccd 100644 --- a/hw/vfio/igd.c +++ b/hw/vfio/igd.c @@ -460,7 +460,7 @@ void vfio_probe_igd_bar0_quirk(VFIOPCIDevice *vdev, int nr) int gen; if (!vfio_pci_is(vdev, PCI_VENDOR_ID_INTEL, PCI_ANY_ID) || - !vfio_is_vga(vdev) || nr != 0) { + !vfio_is_base_display(vdev) || nr != 0) { return; } @@ -518,7 +518,7 @@ static bool vfio_pci_igd_config_quirk(VFIOPCIDevice *vdev, Error **errp) Error *err = NULL; if (!vfio_pci_is(vdev, PCI_VENDOR_ID_INTEL, PCI_ANY_ID) || - !vfio_is_vga(vdev)) { + !vfio_is_base_display(vdev)) { return true; } @@ -534,12 +534,13 @@ static bool vfio_pci_igd_config_quirk(VFIOPCIDevice *vdev, Error **errp) /* * For backward compatibility, enable legacy mode when * - Device geneation is 6 to 9 (including both) - * - IGD claims VGA cycles on host + * - IGD exposes itself as VGA controller and claims VGA cycles on host * - Machine type is i440fx (pc_piix) * - IGD device is at guest BDF 00:02.0 * - Not manually disabled by x-igd-legacy-mode=off */ if ((vdev->igd_legacy_mode != ON_OFF_AUTO_OFF) && + vfio_is_vga(vdev) && (gen >= 6 && gen <= 9) && !(gmch & IGD_GMCH_VGA_DISABLE) && !strcmp(MACHINE_GET_CLASS(qdev_get_machine())->family, "pc_piix") && diff --git a/hw/vfio/pci.h b/hw/vfio/pci.h index 810a842f4a..923cf9c2f7 100644 --- a/hw/vfio/pci.h +++ b/hw/vfio/pci.h @@ -203,6 +203,11 @@ static inline bool vfio_is_vga(VFIOPCIDevice *vdev) return (vdev->class_code >> 8) == PCI_CLASS_DISPLAY_VGA; } +static inline bool vfio_is_base_display(VFIOPCIDevice *vdev) +{ + return (vdev->class_code >> 16) == PCI_BASE_CLASS_DISPLAY; +} + /* MSI/MSI-X/INTx */ void vfio_pci_vector_init(VFIOPCIDevice *vdev, int nr); void vfio_pci_add_kvm_msi_virq(VFIOPCIDevice *vdev, VFIOMSIVector *vector, From aeb1a50d4a7f464a8ff0a66e0beec2a5e1ef6342 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Mon, 1 Sep 2025 08:46:28 +0200 Subject: [PATCH 0328/2396] vfio: Remove 'vfio-amd-xgbe' device MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The VFIO_AMD_XGBE device type has been deprecated in the QEMU 10.0 timeframe. The AMD "Seattle" device is not supported anymore. Remove it. Reviewed-by: Eric Auger Reviewed-by: Alex Williamson Link: https://lore.kernel.org/qemu-devel/20250901064631.530723-6-clg@redhat.com Signed-off-by: Cédric Le Goater --- docs/about/deprecated.rst | 6 - docs/about/removed-features.rst | 9 + docs/devel/kconfig.rst | 1 - hw/arm/Kconfig | 1 - hw/arm/virt.c | 2 - hw/core/sysbus-fdt.c | 316 -------------------------------- hw/vfio/Kconfig | 5 - hw/vfio/amd-xgbe.c | 61 ------ hw/vfio/meson.build | 1 - include/hw/vfio/vfio-amd-xgbe.h | 46 ----- 10 files changed, 9 insertions(+), 439 deletions(-) delete mode 100644 hw/vfio/amd-xgbe.c delete mode 100644 include/hw/vfio/vfio-amd-xgbe.h diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst index b2420732e1..eb424f96d2 100644 --- a/docs/about/deprecated.rst +++ b/docs/about/deprecated.rst @@ -498,12 +498,6 @@ The vfio-calxeda-xgmac device allows to assign a host Calxeda Highbank string) to a guest. Calxeda HW has been ewasted now and there is no point keeping that device. -``-device vfio-amd-xgbe`` (since 10.0) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -The vfio-amd-xgbe device allows to assign a host AMD 10GbE controller -to a guest ("amd,xgbe-seattle-v1a" compatibility string). AMD "Seattle" -is not supported anymore and there is no point keeping that device. - ``-device vfio-platform`` (since 10.0) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The vfio-platform device allows to assign a host platform device diff --git a/docs/about/removed-features.rst b/docs/about/removed-features.rst index fff781d6b7..eb0e5128ba 100644 --- a/docs/about/removed-features.rst +++ b/docs/about/removed-features.rst @@ -1280,6 +1280,15 @@ The corresponding upstream server project is no longer maintained. Users are recommended to switch to an alternative distributed block device driver such as RBD. +VFIO devices +------------ + +``-device vfio-amd-xgbe`` (since 10.2) +'''''''''''''''''''''''''''''''''''''' +The vfio-amd-xgbe device allows to assign a host AMD 10GbE controller +to a guest ("amd,xgbe-seattle-v1a" compatibility string). AMD "Seattle" +is not supported anymore and there is no point keeping that device. + Tools ----- diff --git a/docs/devel/kconfig.rst b/docs/devel/kconfig.rst index 493b76c4fb..9fdf501529 100644 --- a/docs/devel/kconfig.rst +++ b/docs/devel/kconfig.rst @@ -59,7 +59,6 @@ stanza like the following:: config ARM_VIRT bool imply PCI_DEVICES - imply VFIO_AMD_XGBE imply VFIO_XGMAC select A15MPCORE select ACPI diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index 2aa4b5d778..64b2ec87b5 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -5,7 +5,6 @@ config ARM_VIRT depends on TCG || KVM || HVF imply PCI_DEVICES imply TEST_DEVICES - imply VFIO_AMD_XGBE imply VFIO_PLATFORM imply VFIO_XGMAC imply TPM_TIS_SYSBUS diff --git a/hw/arm/virt.c b/hw/arm/virt.c index e5c4142e82..75fb157f6c 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -39,7 +39,6 @@ #include "hw/arm/virt.h" #include "hw/block/flash.h" #include "hw/vfio/vfio-calxeda-xgmac.h" -#include "hw/vfio/vfio-amd-xgbe.h" #include "hw/display/ramfb.h" #include "net/net.h" #include "system/device_tree.h" @@ -3219,7 +3218,6 @@ static void virt_machine_class_init(ObjectClass *oc, const void *data) */ mc->max_cpus = 512; machine_class_allow_dynamic_sysbus_dev(mc, TYPE_VFIO_CALXEDA_XGMAC); - machine_class_allow_dynamic_sysbus_dev(mc, TYPE_VFIO_AMD_XGBE); machine_class_allow_dynamic_sysbus_dev(mc, TYPE_RAMFB_DEVICE); machine_class_allow_dynamic_sysbus_dev(mc, TYPE_VFIO_PLATFORM); machine_class_allow_dynamic_sysbus_dev(mc, TYPE_UEFI_VARS_SYSBUS); diff --git a/hw/core/sysbus-fdt.c b/hw/core/sysbus-fdt.c index c339a27875..d3649d5367 100644 --- a/hw/core/sysbus-fdt.c +++ b/hw/core/sysbus-fdt.c @@ -34,7 +34,6 @@ #include "hw/platform-bus.h" #include "hw/vfio/vfio-platform.h" #include "hw/vfio/vfio-calxeda-xgmac.h" -#include "hw/vfio/vfio-amd-xgbe.h" #include "hw/vfio/vfio-region.h" #include "hw/display/ramfb.h" #include "hw/uefi/var-service-api.h" @@ -68,142 +67,6 @@ typedef struct HostProperty { #ifdef CONFIG_LINUX -/** - * copy_properties_from_host - * - * copies properties listed in an array from host device tree to - * guest device tree. If a non optional property is not found, the - * function asserts. An optional property is ignored if not found - * in the host device tree. - * @props: array of HostProperty to copy - * @nb_props: number of properties in the array - * @host_dt: host device tree blob - * @guest_dt: guest device tree blob - * @node_path: host dt node path where the property is supposed to be - found - * @nodename: guest node name the properties should be added to - */ -static void copy_properties_from_host(HostProperty *props, int nb_props, - void *host_fdt, void *guest_fdt, - char *node_path, char *nodename) -{ - int i, prop_len; - const void *r; - Error *err = NULL; - - for (i = 0; i < nb_props; i++) { - r = qemu_fdt_getprop(host_fdt, node_path, - props[i].name, - &prop_len, - &err); - if (r) { - qemu_fdt_setprop(guest_fdt, nodename, - props[i].name, r, prop_len); - } else { - if (props[i].optional && prop_len == -FDT_ERR_NOTFOUND) { - /* optional property does not exist */ - error_free(err); - } else { - error_report_err(err); - } - if (!props[i].optional) { - /* mandatory property not found: bail out */ - exit(1); - } - err = NULL; - } - } -} - -/* clock properties whose values are copied/pasted from host */ -static HostProperty clock_copied_properties[] = { - {"compatible", false}, - {"#clock-cells", false}, - {"clock-frequency", true}, - {"clock-output-names", true}, -}; - -/** - * fdt_build_clock_node - * - * Build a guest clock node, used as a dependency from a passthrough'ed - * device. Most information are retrieved from the host clock node. - * Also check the host clock is a fixed one. - * - * @host_fdt: host device tree blob from which info are retrieved - * @guest_fdt: guest device tree blob where the clock node is added - * @host_phandle: phandle of the clock in host device tree - * @guest_phandle: phandle to assign to the guest node - */ -static void fdt_build_clock_node(void *host_fdt, void *guest_fdt, - uint32_t host_phandle, - uint32_t guest_phandle) -{ - char *node_path = NULL; - char *nodename; - const void *r; - int ret, node_offset, prop_len, path_len = 16; - - node_offset = fdt_node_offset_by_phandle(host_fdt, host_phandle); - if (node_offset <= 0) { - error_report("not able to locate clock handle %d in host device tree", - host_phandle); - exit(1); - } - node_path = g_malloc(path_len); - while ((ret = fdt_get_path(host_fdt, node_offset, node_path, path_len)) - == -FDT_ERR_NOSPACE) { - path_len += 16; - node_path = g_realloc(node_path, path_len); - } - if (ret < 0) { - error_report("not able to retrieve node path for clock handle %d", - host_phandle); - exit(1); - } - - r = qemu_fdt_getprop(host_fdt, node_path, "compatible", &prop_len, - &error_fatal); - if (strcmp(r, "fixed-clock")) { - error_report("clock handle %d is not a fixed clock", host_phandle); - exit(1); - } - - nodename = strrchr(node_path, '/'); - qemu_fdt_add_subnode(guest_fdt, nodename); - - copy_properties_from_host(clock_copied_properties, - ARRAY_SIZE(clock_copied_properties), - host_fdt, guest_fdt, - node_path, nodename); - - qemu_fdt_setprop_cell(guest_fdt, nodename, "phandle", guest_phandle); - - g_free(node_path); -} - -/** - * sysfs_to_dt_name: convert the name found in sysfs into the node name - * for instance e0900000.xgmac is converted into xgmac@e0900000 - * @sysfs_name: directory name in sysfs - * - * returns the device tree name upon success or NULL in case the sysfs name - * does not match the expected format - */ -static char *sysfs_to_dt_name(const char *sysfs_name) -{ - gchar **substrings = g_strsplit(sysfs_name, ".", 2); - char *dt_name = NULL; - - if (!substrings || !substrings[0] || !substrings[1]) { - goto out; - } - dt_name = g_strdup_printf("%s@%s", substrings[1], substrings[0]); -out: - g_strfreev(substrings); - return dt_name; -} - /* Device Specific Code */ /** @@ -261,183 +124,6 @@ static int add_calxeda_midway_xgmac_fdt_node(SysBusDevice *sbdev, void *opaque) g_free(nodename); return 0; } - -/* AMD xgbe properties whose values are copied/pasted from host */ -static HostProperty amd_xgbe_copied_properties[] = { - {"compatible", false}, - {"dma-coherent", true}, - {"amd,per-channel-interrupt", true}, - {"phy-mode", false}, - {"mac-address", true}, - {"amd,speed-set", false}, - {"amd,serdes-blwc", true}, - {"amd,serdes-cdr-rate", true}, - {"amd,serdes-pq-skew", true}, - {"amd,serdes-tx-amp", true}, - {"amd,serdes-dfe-tap-config", true}, - {"amd,serdes-dfe-tap-enable", true}, - {"clock-names", false}, -}; - -/** - * add_amd_xgbe_fdt_node - * - * Generates the combined xgbe/phy node following kernel >=4.2 - * binding documentation: - * Documentation/devicetree/bindings/net/amd-xgbe.txt: - * Also 2 clock nodes are created (dma and ptp) - * - * Asserts in case of error - */ -static int add_amd_xgbe_fdt_node(SysBusDevice *sbdev, void *opaque) -{ - PlatformBusFDTData *data = opaque; - PlatformBusDevice *pbus = data->pbus; - VFIOPlatformDevice *vdev = VFIO_PLATFORM_DEVICE(sbdev); - VFIODevice *vbasedev = &vdev->vbasedev; - VFIOINTp *intp; - const char *parent_node = data->pbus_node_name; - char **node_path, *nodename, *dt_name; - void *guest_fdt = data->fdt, *host_fdt; - const void *r; - int i, prop_len; - uint32_t *irq_attr, *reg_attr; - const uint32_t *host_clock_phandles; - uint64_t mmio_base, irq_number; - uint32_t guest_clock_phandles[2]; - - host_fdt = load_device_tree_from_sysfs(); - - dt_name = sysfs_to_dt_name(vbasedev->name); - if (!dt_name) { - error_report("%s incorrect sysfs device name %s", - __func__, vbasedev->name); - exit(1); - } - node_path = qemu_fdt_node_path(host_fdt, dt_name, vdev->compat, - &error_fatal); - if (!node_path || !node_path[0]) { - error_report("%s unable to retrieve node path for %s/%s", - __func__, dt_name, vdev->compat); - exit(1); - } - - if (node_path[1]) { - error_report("%s more than one node matching %s/%s!", - __func__, dt_name, vdev->compat); - exit(1); - } - - g_free(dt_name); - - if (vbasedev->num_regions != 5) { - error_report("%s Does the host dt node combine XGBE/PHY?", __func__); - exit(1); - } - - /* generate nodes for DMA_CLK and PTP_CLK */ - r = qemu_fdt_getprop(host_fdt, node_path[0], "clocks", - &prop_len, &error_fatal); - if (prop_len != 8) { - error_report("%s clocks property should contain 2 handles", __func__); - exit(1); - } - host_clock_phandles = r; - guest_clock_phandles[0] = qemu_fdt_alloc_phandle(guest_fdt); - guest_clock_phandles[1] = qemu_fdt_alloc_phandle(guest_fdt); - - /** - * clock handles fetched from host dt are in be32 layout whereas - * rest of the code uses cpu layout. Also guest clock handles are - * in cpu layout. - */ - fdt_build_clock_node(host_fdt, guest_fdt, - be32_to_cpu(host_clock_phandles[0]), - guest_clock_phandles[0]); - - fdt_build_clock_node(host_fdt, guest_fdt, - be32_to_cpu(host_clock_phandles[1]), - guest_clock_phandles[1]); - - /* combined XGBE/PHY node */ - mmio_base = platform_bus_get_mmio_addr(pbus, sbdev, 0); - nodename = g_strdup_printf("%s/%s@%" PRIx64, parent_node, - vbasedev->name, mmio_base); - qemu_fdt_add_subnode(guest_fdt, nodename); - - copy_properties_from_host(amd_xgbe_copied_properties, - ARRAY_SIZE(amd_xgbe_copied_properties), - host_fdt, guest_fdt, - node_path[0], nodename); - - qemu_fdt_setprop_cells(guest_fdt, nodename, "clocks", - guest_clock_phandles[0], - guest_clock_phandles[1]); - - reg_attr = g_new(uint32_t, vbasedev->num_regions * 2); - for (i = 0; i < vbasedev->num_regions; i++) { - mmio_base = platform_bus_get_mmio_addr(pbus, sbdev, i); - reg_attr[2 * i] = cpu_to_be32(mmio_base); - reg_attr[2 * i + 1] = cpu_to_be32( - memory_region_size(vdev->regions[i]->mem)); - } - qemu_fdt_setprop(guest_fdt, nodename, "reg", reg_attr, - vbasedev->num_regions * 2 * sizeof(uint32_t)); - - irq_attr = g_new(uint32_t, vbasedev->num_irqs * 3); - for (i = 0; i < vbasedev->num_irqs; i++) { - irq_number = platform_bus_get_irqn(pbus, sbdev , i) - + data->irq_start; - irq_attr[3 * i] = cpu_to_be32(GIC_FDT_IRQ_TYPE_SPI); - irq_attr[3 * i + 1] = cpu_to_be32(irq_number); - /* - * General device interrupt and PCS auto-negotiation interrupts are - * level-sensitive while the 4 per-channel interrupts are edge - * sensitive - */ - QLIST_FOREACH(intp, &vdev->intp_list, next) { - if (intp->pin == i) { - break; - } - } - if (intp->flags & VFIO_IRQ_INFO_AUTOMASKED) { - irq_attr[3 * i + 2] = cpu_to_be32(GIC_FDT_IRQ_FLAGS_LEVEL_HI); - } else { - irq_attr[3 * i + 2] = cpu_to_be32(GIC_FDT_IRQ_FLAGS_EDGE_LO_HI); - } - } - qemu_fdt_setprop(guest_fdt, nodename, "interrupts", - irq_attr, vbasedev->num_irqs * 3 * sizeof(uint32_t)); - - g_free(host_fdt); - g_strfreev(node_path); - g_free(irq_attr); - g_free(reg_attr); - g_free(nodename); - return 0; -} - -/* DT compatible matching */ -static bool vfio_platform_match(SysBusDevice *sbdev, - const BindingEntry *entry) -{ - VFIOPlatformDevice *vdev = VFIO_PLATFORM_DEVICE(sbdev); - const char *compat; - unsigned int n; - - for (n = vdev->num_compat, compat = vdev->compat; n > 0; - n--, compat += strlen(compat) + 1) { - if (!strcmp(entry->compat, compat)) { - return true; - } - } - - return false; -} - -#define VFIO_PLATFORM_BINDING(compat, add_fn) \ - {TYPE_VFIO_PLATFORM, (compat), (add_fn), vfio_platform_match} - #endif /* CONFIG_LINUX */ #ifdef CONFIG_TPM @@ -512,8 +198,6 @@ static bool type_match(SysBusDevice *sbdev, const BindingEntry *entry) static const BindingEntry bindings[] = { #ifdef CONFIG_LINUX TYPE_BINDING(TYPE_VFIO_CALXEDA_XGMAC, add_calxeda_midway_xgmac_fdt_node), - TYPE_BINDING(TYPE_VFIO_AMD_XGBE, add_amd_xgbe_fdt_node), - VFIO_PLATFORM_BINDING("amd,xgbe-seattle-v1a", add_amd_xgbe_fdt_node), #endif #ifdef CONFIG_TPM TYPE_BINDING(TYPE_TPM_TIS_SYSBUS, add_tpm_tis_fdt_node), diff --git a/hw/vfio/Kconfig b/hw/vfio/Kconfig index 91d9023b79..bc984f1986 100644 --- a/hw/vfio/Kconfig +++ b/hw/vfio/Kconfig @@ -28,11 +28,6 @@ config VFIO_XGMAC default y depends on VFIO_PLATFORM -config VFIO_AMD_XGBE - bool - default y - depends on VFIO_PLATFORM - config VFIO_AP bool default y diff --git a/hw/vfio/amd-xgbe.c b/hw/vfio/amd-xgbe.c deleted file mode 100644 index 58f590e385..0000000000 --- a/hw/vfio/amd-xgbe.c +++ /dev/null @@ -1,61 +0,0 @@ -/* - * AMD XGBE VFIO device - * - * Copyright Linaro Limited, 2015 - * - * Authors: - * Eric Auger - * - * This work is licensed under the terms of the GNU GPL, version 2. See - * the COPYING file in the top-level directory. - * - */ - -#include "qemu/osdep.h" -#include "hw/vfio/vfio-amd-xgbe.h" -#include "migration/vmstate.h" -#include "qemu/module.h" -#include "qemu/error-report.h" - -static void amd_xgbe_realize(DeviceState *dev, Error **errp) -{ - VFIOPlatformDevice *vdev = VFIO_PLATFORM_DEVICE(dev); - VFIOAmdXgbeDeviceClass *k = VFIO_AMD_XGBE_DEVICE_GET_CLASS(dev); - - warn_report("-device vfio-amd-xgbe is deprecated"); - vdev->compat = g_strdup("amd,xgbe-seattle-v1a"); - vdev->num_compat = 1; - - k->parent_realize(dev, errp); -} - -static const VMStateDescription vfio_platform_amd_xgbe_vmstate = { - .name = "vfio-amd-xgbe", - .unmigratable = 1, -}; - -static void vfio_amd_xgbe_class_init(ObjectClass *klass, const void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - VFIOAmdXgbeDeviceClass *vcxc = - VFIO_AMD_XGBE_DEVICE_CLASS(klass); - device_class_set_parent_realize(dc, amd_xgbe_realize, - &vcxc->parent_realize); - dc->desc = "VFIO AMD XGBE"; - dc->vmsd = &vfio_platform_amd_xgbe_vmstate; -} - -static const TypeInfo vfio_amd_xgbe_dev_info = { - .name = TYPE_VFIO_AMD_XGBE, - .parent = TYPE_VFIO_PLATFORM, - .instance_size = sizeof(VFIOAmdXgbeDevice), - .class_init = vfio_amd_xgbe_class_init, - .class_size = sizeof(VFIOAmdXgbeDeviceClass), -}; - -static void register_amd_xgbe_dev_type(void) -{ - type_register_static(&vfio_amd_xgbe_dev_info); -} - -type_init(register_amd_xgbe_dev_type) diff --git a/hw/vfio/meson.build b/hw/vfio/meson.build index bfaf6be805..0edcaf5155 100644 --- a/hw/vfio/meson.build +++ b/hw/vfio/meson.build @@ -20,7 +20,6 @@ vfio_ss.add(when: 'CONFIG_VFIO_IGD', if_true: files('igd.c')) specific_ss.add_all(when: 'CONFIG_VFIO', if_true: vfio_ss) system_ss.add(when: 'CONFIG_VFIO_XGMAC', if_true: files('calxeda-xgmac.c')) -system_ss.add(when: 'CONFIG_VFIO_AMD_XGBE', if_true: files('amd-xgbe.c')) system_ss.add(when: 'CONFIG_VFIO', if_true: files( 'cpr.c', 'cpr-legacy.c', diff --git a/include/hw/vfio/vfio-amd-xgbe.h b/include/hw/vfio/vfio-amd-xgbe.h deleted file mode 100644 index a894546c02..0000000000 --- a/include/hw/vfio/vfio-amd-xgbe.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * VFIO AMD XGBE device - * - * Copyright Linaro Limited, 2015 - * - * Authors: - * Eric Auger - * - * This work is licensed under the terms of the GNU GPL, version 2. See - * the COPYING file in the top-level directory. - * - */ - -#ifndef HW_VFIO_VFIO_AMD_XGBE_H -#define HW_VFIO_VFIO_AMD_XGBE_H - -#include "hw/vfio/vfio-platform.h" -#include "qom/object.h" - -#define TYPE_VFIO_AMD_XGBE "vfio-amd-xgbe" - -/** - * This device exposes: - * - 5 MMIO regions: MAC, PCS, SerDes Rx/Tx regs, - SerDes Integration Registers 1/2 & 2/2 - * - 2 level sensitive IRQs and optional DMA channel IRQs - */ -struct VFIOAmdXgbeDevice { - VFIOPlatformDevice vdev; -}; - -typedef struct VFIOAmdXgbeDevice VFIOAmdXgbeDevice; - -struct VFIOAmdXgbeDeviceClass { - /*< private >*/ - VFIOPlatformDeviceClass parent_class; - /*< public >*/ - DeviceRealize parent_realize; -}; - -typedef struct VFIOAmdXgbeDeviceClass VFIOAmdXgbeDeviceClass; - -DECLARE_OBJ_CHECKERS(VFIOAmdXgbeDevice, VFIOAmdXgbeDeviceClass, - VFIO_AMD_XGBE_DEVICE, TYPE_VFIO_AMD_XGBE) - -#endif From 8ebc416ac17a71aec267df1ca5cb5301cc6c4906 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Mon, 1 Sep 2025 08:46:29 +0200 Subject: [PATCH 0329/2396] vfio: Remove 'vfio-calxeda-xgmac' device MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The VFIO_XGMAC device type has been deprecated in the QEMU 10.0 timeframe. Remove it. Reviewed-by: Eric Auger Reviewed-by: Alex Williamson Link: https://lore.kernel.org/qemu-devel/20250901064631.530723-7-clg@redhat.com Signed-off-by: Cédric Le Goater --- docs/about/deprecated.rst | 7 --- docs/about/removed-features.rst | 7 +++ docs/devel/kconfig.rst | 1 - hw/arm/Kconfig | 1 - hw/arm/virt.c | 3 +- hw/core/sysbus-fdt.c | 67 ---------------------------- hw/vfio/Kconfig | 5 --- hw/vfio/calxeda-xgmac.c | 61 ------------------------- hw/vfio/meson.build | 1 - include/hw/vfio/vfio-calxeda-xgmac.h | 43 ------------------ 10 files changed, 8 insertions(+), 188 deletions(-) delete mode 100644 hw/vfio/calxeda-xgmac.c delete mode 100644 include/hw/vfio/vfio-calxeda-xgmac.h diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst index eb424f96d2..d0fa8e5536 100644 --- a/docs/about/deprecated.rst +++ b/docs/about/deprecated.rst @@ -491,13 +491,6 @@ which is not enough for all types of use cases, use ``reconnect-ms`` instead. VFIO device options ''''''''''''''''''' -``-device vfio-calxeda-xgmac`` (since 10.0) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -The vfio-calxeda-xgmac device allows to assign a host Calxeda Highbank -10Gb XGMAC Ethernet controller device ("calxeda,hb-xgmac" compatibility -string) to a guest. Calxeda HW has been ewasted now and there is no point -keeping that device. - ``-device vfio-platform`` (since 10.0) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The vfio-platform device allows to assign a host platform device diff --git a/docs/about/removed-features.rst b/docs/about/removed-features.rst index eb0e5128ba..db3f22941c 100644 --- a/docs/about/removed-features.rst +++ b/docs/about/removed-features.rst @@ -1283,6 +1283,13 @@ device driver such as RBD. VFIO devices ------------ +``-device vfio-calxeda-xgmac`` (since 10.2) +''''''''''''''''''''''''''''''''''''''''''' +The vfio-calxeda-xgmac device allows to assign a host Calxeda Highbank +10Gb XGMAC Ethernet controller device ("calxeda,hb-xgmac" compatibility +string) to a guest. Calxeda HW has been ewasted now and there is no point +keeping that device. + ``-device vfio-amd-xgbe`` (since 10.2) '''''''''''''''''''''''''''''''''''''' The vfio-amd-xgbe device allows to assign a host AMD 10GbE controller diff --git a/docs/devel/kconfig.rst b/docs/devel/kconfig.rst index 9fdf501529..1d4a114a02 100644 --- a/docs/devel/kconfig.rst +++ b/docs/devel/kconfig.rst @@ -59,7 +59,6 @@ stanza like the following:: config ARM_VIRT bool imply PCI_DEVICES - imply VFIO_XGMAC select A15MPCORE select ACPI select ARM_SMMUV3 diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index 64b2ec87b5..3fca48349a 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -6,7 +6,6 @@ config ARM_VIRT imply PCI_DEVICES imply TEST_DEVICES imply VFIO_PLATFORM - imply VFIO_XGMAC imply TPM_TIS_SYSBUS imply TPM_TIS_I2C imply NVDIMM diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 75fb157f6c..6a887228bb 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -38,7 +38,7 @@ #include "hw/arm/primecell.h" #include "hw/arm/virt.h" #include "hw/block/flash.h" -#include "hw/vfio/vfio-calxeda-xgmac.h" +#include "hw/vfio/vfio-platform.h" #include "hw/display/ramfb.h" #include "net/net.h" #include "system/device_tree.h" @@ -3217,7 +3217,6 @@ static void virt_machine_class_init(ObjectClass *oc, const void *data) * configuration of the particular instance. */ mc->max_cpus = 512; - machine_class_allow_dynamic_sysbus_dev(mc, TYPE_VFIO_CALXEDA_XGMAC); machine_class_allow_dynamic_sysbus_dev(mc, TYPE_RAMFB_DEVICE); machine_class_allow_dynamic_sysbus_dev(mc, TYPE_VFIO_PLATFORM); machine_class_allow_dynamic_sysbus_dev(mc, TYPE_UEFI_VARS_SYSBUS); diff --git a/hw/core/sysbus-fdt.c b/hw/core/sysbus-fdt.c index d3649d5367..07117363a6 100644 --- a/hw/core/sysbus-fdt.c +++ b/hw/core/sysbus-fdt.c @@ -32,9 +32,6 @@ #include "system/device_tree.h" #include "system/tpm.h" #include "hw/platform-bus.h" -#include "hw/vfio/vfio-platform.h" -#include "hw/vfio/vfio-calxeda-xgmac.h" -#include "hw/vfio/vfio-region.h" #include "hw/display/ramfb.h" #include "hw/uefi/var-service-api.h" #include "hw/arm/fdt.h" @@ -65,67 +62,6 @@ typedef struct HostProperty { bool optional; } HostProperty; -#ifdef CONFIG_LINUX - -/* Device Specific Code */ - -/** - * add_calxeda_midway_xgmac_fdt_node - * - * Generates a simple node with following properties: - * compatible string, regs, interrupts, dma-coherent - */ -static int add_calxeda_midway_xgmac_fdt_node(SysBusDevice *sbdev, void *opaque) -{ - PlatformBusFDTData *data = opaque; - PlatformBusDevice *pbus = data->pbus; - void *fdt = data->fdt; - const char *parent_node = data->pbus_node_name; - int compat_str_len, i; - char *nodename; - uint32_t *irq_attr, *reg_attr; - uint64_t mmio_base, irq_number; - VFIOPlatformDevice *vdev = VFIO_PLATFORM_DEVICE(sbdev); - VFIODevice *vbasedev = &vdev->vbasedev; - - mmio_base = platform_bus_get_mmio_addr(pbus, sbdev, 0); - nodename = g_strdup_printf("%s/%s@%" PRIx64, parent_node, - vbasedev->name, mmio_base); - qemu_fdt_add_subnode(fdt, nodename); - - compat_str_len = strlen(vdev->compat) + 1; - qemu_fdt_setprop(fdt, nodename, "compatible", - vdev->compat, compat_str_len); - - qemu_fdt_setprop(fdt, nodename, "dma-coherent", "", 0); - - reg_attr = g_new(uint32_t, vbasedev->num_regions * 2); - for (i = 0; i < vbasedev->num_regions; i++) { - mmio_base = platform_bus_get_mmio_addr(pbus, sbdev, i); - reg_attr[2 * i] = cpu_to_be32(mmio_base); - reg_attr[2 * i + 1] = cpu_to_be32( - memory_region_size(vdev->regions[i]->mem)); - } - qemu_fdt_setprop(fdt, nodename, "reg", reg_attr, - vbasedev->num_regions * 2 * sizeof(uint32_t)); - - irq_attr = g_new(uint32_t, vbasedev->num_irqs * 3); - for (i = 0; i < vbasedev->num_irqs; i++) { - irq_number = platform_bus_get_irqn(pbus, sbdev , i) - + data->irq_start; - irq_attr[3 * i] = cpu_to_be32(GIC_FDT_IRQ_TYPE_SPI); - irq_attr[3 * i + 1] = cpu_to_be32(irq_number); - irq_attr[3 * i + 2] = cpu_to_be32(GIC_FDT_IRQ_FLAGS_LEVEL_HI); - } - qemu_fdt_setprop(fdt, nodename, "interrupts", - irq_attr, vbasedev->num_irqs * 3 * sizeof(uint32_t)); - g_free(irq_attr); - g_free(reg_attr); - g_free(nodename); - return 0; -} -#endif /* CONFIG_LINUX */ - #ifdef CONFIG_TPM /* * add_tpm_tis_fdt_node: Create a DT node for TPM TIS @@ -196,9 +132,6 @@ static bool type_match(SysBusDevice *sbdev, const BindingEntry *entry) /* list of supported dynamic sysbus bindings */ static const BindingEntry bindings[] = { -#ifdef CONFIG_LINUX - TYPE_BINDING(TYPE_VFIO_CALXEDA_XGMAC, add_calxeda_midway_xgmac_fdt_node), -#endif #ifdef CONFIG_TPM TYPE_BINDING(TYPE_TPM_TIS_SYSBUS, add_tpm_tis_fdt_node), #endif diff --git a/hw/vfio/Kconfig b/hw/vfio/Kconfig index bc984f1986..9a1dbe2926 100644 --- a/hw/vfio/Kconfig +++ b/hw/vfio/Kconfig @@ -23,11 +23,6 @@ config VFIO_PLATFORM select VFIO depends on LINUX && PLATFORM_BUS -config VFIO_XGMAC - bool - default y - depends on VFIO_PLATFORM - config VFIO_AP bool default y diff --git a/hw/vfio/calxeda-xgmac.c b/hw/vfio/calxeda-xgmac.c deleted file mode 100644 index 03f2ff5763..0000000000 --- a/hw/vfio/calxeda-xgmac.c +++ /dev/null @@ -1,61 +0,0 @@ -/* - * calxeda xgmac VFIO device - * - * Copyright Linaro Limited, 2014 - * - * Authors: - * Eric Auger - * - * This work is licensed under the terms of the GNU GPL, version 2. See - * the COPYING file in the top-level directory. - * - */ - -#include "qemu/osdep.h" -#include "hw/vfio/vfio-calxeda-xgmac.h" -#include "migration/vmstate.h" -#include "qemu/module.h" -#include "qemu/error-report.h" - -static void calxeda_xgmac_realize(DeviceState *dev, Error **errp) -{ - VFIOPlatformDevice *vdev = VFIO_PLATFORM_DEVICE(dev); - VFIOCalxedaXgmacDeviceClass *k = VFIO_CALXEDA_XGMAC_DEVICE_GET_CLASS(dev); - - warn_report("-device vfio-calxeda-xgmac is deprecated"); - vdev->compat = g_strdup("calxeda,hb-xgmac"); - vdev->num_compat = 1; - - k->parent_realize(dev, errp); -} - -static const VMStateDescription vfio_platform_calxeda_xgmac_vmstate = { - .name = "vfio-calxeda-xgmac", - .unmigratable = 1, -}; - -static void vfio_calxeda_xgmac_class_init(ObjectClass *klass, const void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - VFIOCalxedaXgmacDeviceClass *vcxc = - VFIO_CALXEDA_XGMAC_DEVICE_CLASS(klass); - device_class_set_parent_realize(dc, calxeda_xgmac_realize, - &vcxc->parent_realize); - dc->desc = "VFIO Calxeda XGMAC"; - dc->vmsd = &vfio_platform_calxeda_xgmac_vmstate; -} - -static const TypeInfo vfio_calxeda_xgmac_dev_info = { - .name = TYPE_VFIO_CALXEDA_XGMAC, - .parent = TYPE_VFIO_PLATFORM, - .instance_size = sizeof(VFIOCalxedaXgmacDevice), - .class_init = vfio_calxeda_xgmac_class_init, - .class_size = sizeof(VFIOCalxedaXgmacDeviceClass), -}; - -static void register_calxeda_xgmac_dev_type(void) -{ - type_register_static(&vfio_calxeda_xgmac_dev_info); -} - -type_init(register_calxeda_xgmac_dev_type) diff --git a/hw/vfio/meson.build b/hw/vfio/meson.build index 0edcaf5155..06473a0789 100644 --- a/hw/vfio/meson.build +++ b/hw/vfio/meson.build @@ -19,7 +19,6 @@ vfio_ss.add(when: 'CONFIG_VFIO_IGD', if_true: files('igd.c')) specific_ss.add_all(when: 'CONFIG_VFIO', if_true: vfio_ss) -system_ss.add(when: 'CONFIG_VFIO_XGMAC', if_true: files('calxeda-xgmac.c')) system_ss.add(when: 'CONFIG_VFIO', if_true: files( 'cpr.c', 'cpr-legacy.c', diff --git a/include/hw/vfio/vfio-calxeda-xgmac.h b/include/hw/vfio/vfio-calxeda-xgmac.h deleted file mode 100644 index 8482f151dd..0000000000 --- a/include/hw/vfio/vfio-calxeda-xgmac.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * VFIO calxeda xgmac device - * - * Copyright Linaro Limited, 2014 - * - * Authors: - * Eric Auger - * - * This work is licensed under the terms of the GNU GPL, version 2. See - * the COPYING file in the top-level directory. - * - */ - -#ifndef HW_VFIO_VFIO_CALXEDA_XGMAC_H -#define HW_VFIO_VFIO_CALXEDA_XGMAC_H - -#include "hw/vfio/vfio-platform.h" -#include "qom/object.h" - -#define TYPE_VFIO_CALXEDA_XGMAC "vfio-calxeda-xgmac" - -/** - * This device exposes: - * - a single MMIO region corresponding to its register space - * - 3 IRQS (main and 2 power related IRQs) - */ -struct VFIOCalxedaXgmacDevice { - VFIOPlatformDevice vdev; -}; -typedef struct VFIOCalxedaXgmacDevice VFIOCalxedaXgmacDevice; - -struct VFIOCalxedaXgmacDeviceClass { - /*< private >*/ - VFIOPlatformDeviceClass parent_class; - /*< public >*/ - DeviceRealize parent_realize; -}; -typedef struct VFIOCalxedaXgmacDeviceClass VFIOCalxedaXgmacDeviceClass; - -DECLARE_OBJ_CHECKERS(VFIOCalxedaXgmacDevice, VFIOCalxedaXgmacDeviceClass, - VFIO_CALXEDA_XGMAC_DEVICE, TYPE_VFIO_CALXEDA_XGMAC) - -#endif From 762c85543948bf1f7838d663995648635d3f4b92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Mon, 1 Sep 2025 08:46:30 +0200 Subject: [PATCH 0330/2396] vfio: Remove 'vfio-platform' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The VFIO_PLATFORM device type has been deprecated in the QEMU 10.0 timeframe. All dependent devices have been removed. Now remove the core vfio platform framework. Rename VFIO_DEVICE_TYPE_PLATFORM enum to VFIO_DEVICE_TYPE_UNUSED to maintain the same index for the CCW and AP VFIO device types. Reviewed-by: Eric Auger Reviewed-by: Alex Williamson Link: https://lore.kernel.org/qemu-devel/20250901064631.530723-8-clg@redhat.com Signed-off-by: Cédric Le Goater --- docs/about/deprecated.rst | 12 - docs/about/removed-features.rst | 9 + hw/arm/Kconfig | 1 - hw/arm/virt.c | 2 - hw/vfio/Kconfig | 6 - hw/vfio/meson.build | 1 - hw/vfio/platform.c | 716 -------------------------------- hw/vfio/trace-events | 11 - include/hw/vfio/vfio-device.h | 2 +- include/hw/vfio/vfio-platform.h | 78 ---- 10 files changed, 10 insertions(+), 828 deletions(-) delete mode 100644 hw/vfio/platform.c delete mode 100644 include/hw/vfio/vfio-platform.h diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst index d0fa8e5536..2fa2c47b68 100644 --- a/docs/about/deprecated.rst +++ b/docs/about/deprecated.rst @@ -488,18 +488,6 @@ Stream ``reconnect`` (since 9.2) The ``reconnect`` option only allows specifying second granularity timeouts, which is not enough for all types of use cases, use ``reconnect-ms`` instead. -VFIO device options -''''''''''''''''''' - -``-device vfio-platform`` (since 10.0) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -The vfio-platform device allows to assign a host platform device -to a guest in a generic manner. Integrating a new device into -the vfio-platform infrastructure requires some adaptation at -both kernel and qemu level. No such attempt has been done for years -and the conclusion is that vfio-platform has not got any traction. -PCIe passthrough shall be the mainline solution. - CPU device properties ''''''''''''''''''''' diff --git a/docs/about/removed-features.rst b/docs/about/removed-features.rst index db3f22941c..2d3a684e53 100644 --- a/docs/about/removed-features.rst +++ b/docs/about/removed-features.rst @@ -1296,6 +1296,15 @@ The vfio-amd-xgbe device allows to assign a host AMD 10GbE controller to a guest ("amd,xgbe-seattle-v1a" compatibility string). AMD "Seattle" is not supported anymore and there is no point keeping that device. +``-device vfio-platform`` (since 10.2) +'''''''''''''''''''''''''''''''''''''' +The vfio-platform device allows to assign a host platform device +to a guest in a generic manner. Integrating a new device into +the vfio-platform infrastructure requires some adaptation at +both kernel and qemu level. No such attempt has been done for years +and the conclusion is that vfio-platform has not got any traction. +PCIe passthrough shall be the mainline solution. + Tools ----- diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index 3fca48349a..3baa6c6c74 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -5,7 +5,6 @@ config ARM_VIRT depends on TCG || KVM || HVF imply PCI_DEVICES imply TEST_DEVICES - imply VFIO_PLATFORM imply TPM_TIS_SYSBUS imply TPM_TIS_I2C imply NVDIMM diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 6a887228bb..6f01746e74 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -38,7 +38,6 @@ #include "hw/arm/primecell.h" #include "hw/arm/virt.h" #include "hw/block/flash.h" -#include "hw/vfio/vfio-platform.h" #include "hw/display/ramfb.h" #include "net/net.h" #include "system/device_tree.h" @@ -3218,7 +3217,6 @@ static void virt_machine_class_init(ObjectClass *oc, const void *data) */ mc->max_cpus = 512; machine_class_allow_dynamic_sysbus_dev(mc, TYPE_RAMFB_DEVICE); - machine_class_allow_dynamic_sysbus_dev(mc, TYPE_VFIO_PLATFORM); machine_class_allow_dynamic_sysbus_dev(mc, TYPE_UEFI_VARS_SYSBUS); #ifdef CONFIG_TPM machine_class_allow_dynamic_sysbus_dev(mc, TYPE_TPM_TIS_SYSBUS); diff --git a/hw/vfio/Kconfig b/hw/vfio/Kconfig index 9a1dbe2926..27de24e4db 100644 --- a/hw/vfio/Kconfig +++ b/hw/vfio/Kconfig @@ -17,12 +17,6 @@ config VFIO_CCW select VFIO depends on LINUX && S390_CCW_VIRTIO -config VFIO_PLATFORM - bool - default y - select VFIO - depends on LINUX && PLATFORM_BUS - config VFIO_AP bool default y diff --git a/hw/vfio/meson.build b/hw/vfio/meson.build index 06473a0789..d3ed3cb7ac 100644 --- a/hw/vfio/meson.build +++ b/hw/vfio/meson.build @@ -13,7 +13,6 @@ vfio_ss.add(when: 'CONFIG_VFIO_PCI', if_true: files( 'pci.c', )) vfio_ss.add(when: 'CONFIG_VFIO_CCW', if_true: files('ccw.c')) -vfio_ss.add(when: 'CONFIG_VFIO_PLATFORM', if_true: files('platform.c')) vfio_ss.add(when: 'CONFIG_VFIO_AP', if_true: files('ap.c')) vfio_ss.add(when: 'CONFIG_VFIO_IGD', if_true: files('igd.c')) diff --git a/hw/vfio/platform.c b/hw/vfio/platform.c deleted file mode 100644 index 5c1795a26f..0000000000 --- a/hw/vfio/platform.c +++ /dev/null @@ -1,716 +0,0 @@ -/* - * vfio based device assignment support - platform devices - * - * Copyright Linaro Limited, 2014 - * - * Authors: - * Kim Phillips - * Eric Auger - * - * This work is licensed under the terms of the GNU GPL, version 2. See - * the COPYING file in the top-level directory. - * - * Based on vfio based PCI device assignment support: - * Copyright Red Hat, Inc. 2012 - */ - -#include "qemu/osdep.h" -#include CONFIG_DEVICES /* CONFIG_IOMMUFD */ -#include "qapi/error.h" -#include -#include - -#include "hw/vfio/vfio-platform.h" -#include "system/iommufd.h" -#include "migration/vmstate.h" -#include "qemu/error-report.h" -#include "qemu/lockable.h" -#include "qemu/main-loop.h" -#include "qemu/module.h" -#include "qemu/range.h" -#include "system/memory.h" -#include "system/address-spaces.h" -#include "qemu/queue.h" -#include "hw/sysbus.h" -#include "trace.h" -#include "hw/irq.h" -#include "hw/platform-bus.h" -#include "hw/qdev-properties.h" -#include "system/kvm.h" -#include "hw/vfio/vfio-region.h" - -/* - * Functions used whatever the injection method - */ - -static inline bool vfio_irq_is_automasked(VFIOINTp *intp) -{ - return intp->flags & VFIO_IRQ_INFO_AUTOMASKED; -} - -/** - * vfio_init_intp - allocate, initialize the IRQ struct pointer - * and add it into the list of IRQs - * @vbasedev: the VFIO device handle - * @info: irq info struct retrieved from VFIO driver - * @errp: error object - */ -static VFIOINTp *vfio_init_intp(VFIODevice *vbasedev, - struct vfio_irq_info info, Error **errp) -{ - int ret; - VFIOPlatformDevice *vdev = - container_of(vbasedev, VFIOPlatformDevice, vbasedev); - SysBusDevice *sbdev = SYS_BUS_DEVICE(vdev); - VFIOINTp *intp; - - intp = g_malloc0(sizeof(*intp)); - intp->vdev = vdev; - intp->pin = info.index; - intp->flags = info.flags; - intp->state = VFIO_IRQ_INACTIVE; - intp->kvm_accel = false; - - sysbus_init_irq(sbdev, &intp->qemuirq); - - /* Get an eventfd for trigger */ - intp->interrupt = g_new0(EventNotifier, 1); - ret = event_notifier_init(intp->interrupt, 0); - if (ret) { - g_free(intp->interrupt); - g_free(intp); - error_setg_errno(errp, -ret, - "failed to initialize trigger eventfd notifier"); - return NULL; - } - if (vfio_irq_is_automasked(intp)) { - /* Get an eventfd for resample/unmask */ - intp->unmask = g_new0(EventNotifier, 1); - ret = event_notifier_init(intp->unmask, 0); - if (ret) { - g_free(intp->interrupt); - g_free(intp->unmask); - g_free(intp); - error_setg_errno(errp, -ret, - "failed to initialize resample eventfd notifier"); - return NULL; - } - } - - QLIST_INSERT_HEAD(&vdev->intp_list, intp, next); - return intp; -} - -/** - * vfio_set_trigger_eventfd - set VFIO eventfd handling - * - * @intp: IRQ struct handle - * @handler: handler to be called on eventfd signaling - * - * Setup VFIO signaling and attach an optional user-side handler - * to the eventfd - */ -static int vfio_set_trigger_eventfd(VFIOINTp *intp, - eventfd_user_side_handler_t handler) -{ - VFIODevice *vbasedev = &intp->vdev->vbasedev; - int32_t fd = event_notifier_get_fd(intp->interrupt); - Error *err = NULL; - - qemu_set_fd_handler(fd, (IOHandler *)handler, NULL, intp); - - if (!vfio_device_irq_set_signaling(vbasedev, intp->pin, 0, - VFIO_IRQ_SET_ACTION_TRIGGER, fd, &err)) { - error_reportf_err(err, VFIO_MSG_PREFIX, vbasedev->name); - qemu_set_fd_handler(fd, NULL, NULL, NULL); - return -EINVAL; - } - - return 0; -} - -/* - * Functions only used when eventfds are handled on user-side - * ie. without irqfd - */ - -/** - * vfio_mmap_set_enabled - enable/disable the fast path mode - * @vdev: the VFIO platform device - * @enabled: the target mmap state - * - * enabled = true ~ fast path = MMIO region is mmaped (no KVM TRAP); - * enabled = false ~ slow path = MMIO region is trapped and region callbacks - * are called; slow path enables to trap the device IRQ status register reset -*/ - -static void vfio_mmap_set_enabled(VFIOPlatformDevice *vdev, bool enabled) -{ - int i; - - for (i = 0; i < vdev->vbasedev.num_regions; i++) { - vfio_region_mmaps_set_enabled(vdev->regions[i], enabled); - } -} - -/** - * vfio_intp_mmap_enable - timer function, restores the fast path - * if there is no more active IRQ - * @opaque: actually points to the VFIO platform device - * - * Called on mmap timer timeout, this function checks whether the - * IRQ is still active and if not, restores the fast path. - * by construction a single eventfd is handled at a time. - * if the IRQ is still active, the timer is re-programmed. - */ -static void vfio_intp_mmap_enable(void *opaque) -{ - VFIOINTp *tmp; - VFIOPlatformDevice *vdev = (VFIOPlatformDevice *)opaque; - - QEMU_LOCK_GUARD(&vdev->intp_mutex); - QLIST_FOREACH(tmp, &vdev->intp_list, next) { - if (tmp->state == VFIO_IRQ_ACTIVE) { - trace_vfio_platform_intp_mmap_enable(tmp->pin); - /* re-program the timer to check active status later */ - timer_mod(vdev->mmap_timer, - qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + - vdev->mmap_timeout); - return; - } - } - vfio_mmap_set_enabled(vdev, true); -} - -/** - * vfio_intp_inject_pending_lockheld - Injects a pending IRQ - * @opaque: opaque pointer, in practice the VFIOINTp handle - * - * The function is called on a previous IRQ completion, from - * vfio_platform_eoi, while the intp_mutex is locked. - * Also in such situation, the slow path already is set and - * the mmap timer was already programmed. - */ -static void vfio_intp_inject_pending_lockheld(VFIOINTp *intp) -{ - trace_vfio_platform_intp_inject_pending_lockheld(intp->pin, - event_notifier_get_fd(intp->interrupt)); - - intp->state = VFIO_IRQ_ACTIVE; - - /* trigger the virtual IRQ */ - qemu_set_irq(intp->qemuirq, 1); -} - -/** - * vfio_intp_interrupt - The user-side eventfd handler - * @opaque: opaque pointer which in practice is the VFIOINTp handle - * - * the function is entered in event handler context: - * the vIRQ is injected into the guest if there is no other active - * or pending IRQ. - */ -static void vfio_intp_interrupt(VFIOINTp *intp) -{ - int ret; - VFIOINTp *tmp; - VFIOPlatformDevice *vdev = intp->vdev; - bool delay_handling = false; - - QEMU_LOCK_GUARD(&vdev->intp_mutex); - if (intp->state == VFIO_IRQ_INACTIVE) { - QLIST_FOREACH(tmp, &vdev->intp_list, next) { - if (tmp->state == VFIO_IRQ_ACTIVE || - tmp->state == VFIO_IRQ_PENDING) { - delay_handling = true; - break; - } - } - } - if (delay_handling) { - /* - * the new IRQ gets a pending status and is pushed in - * the pending queue - */ - intp->state = VFIO_IRQ_PENDING; - trace_vfio_intp_interrupt_set_pending(intp->pin); - QSIMPLEQ_INSERT_TAIL(&vdev->pending_intp_queue, - intp, pqnext); - event_notifier_test_and_clear(intp->interrupt); - return; - } - - trace_vfio_platform_intp_interrupt(intp->pin, - event_notifier_get_fd(intp->interrupt)); - - ret = event_notifier_test_and_clear(intp->interrupt); - if (!ret) { - error_report("Error when clearing fd=%d (ret = %d)", - event_notifier_get_fd(intp->interrupt), ret); - } - - intp->state = VFIO_IRQ_ACTIVE; - - /* sets slow path */ - vfio_mmap_set_enabled(vdev, false); - - /* trigger the virtual IRQ */ - qemu_set_irq(intp->qemuirq, 1); - - /* - * Schedule the mmap timer which will restore fastpath when no IRQ - * is active anymore - */ - if (vdev->mmap_timeout) { - timer_mod(vdev->mmap_timer, - qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + - vdev->mmap_timeout); - } -} - -/** - * vfio_platform_eoi - IRQ completion routine - * @vbasedev: the VFIO device handle - * - * De-asserts the active virtual IRQ and unmasks the physical IRQ - * (effective for level sensitive IRQ auto-masked by the VFIO driver). - * Then it handles next pending IRQ if any. - * eoi function is called on the first access to any MMIO region - * after an IRQ was triggered, trapped since slow path was set. - * It is assumed this access corresponds to the IRQ status - * register reset. With such a mechanism, a single IRQ can be - * handled at a time since there is no way to know which IRQ - * was completed by the guest (we would need additional details - * about the IRQ status register mask). - */ -static void vfio_platform_eoi(VFIODevice *vbasedev) -{ - VFIOINTp *intp; - VFIOPlatformDevice *vdev = - container_of(vbasedev, VFIOPlatformDevice, vbasedev); - - QEMU_LOCK_GUARD(&vdev->intp_mutex); - QLIST_FOREACH(intp, &vdev->intp_list, next) { - if (intp->state == VFIO_IRQ_ACTIVE) { - trace_vfio_platform_eoi(intp->pin, - event_notifier_get_fd(intp->interrupt)); - intp->state = VFIO_IRQ_INACTIVE; - - /* deassert the virtual IRQ */ - qemu_set_irq(intp->qemuirq, 0); - - if (vfio_irq_is_automasked(intp)) { - /* unmasks the physical level-sensitive IRQ */ - vfio_device_irq_unmask(vbasedev, intp->pin); - } - - /* a single IRQ can be active at a time */ - break; - } - } - /* in case there are pending IRQs, handle the first one */ - if (!QSIMPLEQ_EMPTY(&vdev->pending_intp_queue)) { - intp = QSIMPLEQ_FIRST(&vdev->pending_intp_queue); - vfio_intp_inject_pending_lockheld(intp); - QSIMPLEQ_REMOVE_HEAD(&vdev->pending_intp_queue, pqnext); - } -} - -/** - * vfio_start_eventfd_injection - starts the virtual IRQ injection using - * user-side handled eventfds - * @sbdev: the sysbus device handle - * @irq: the qemu irq handle - */ - -static void vfio_start_eventfd_injection(SysBusDevice *sbdev, qemu_irq irq) -{ - VFIOPlatformDevice *vdev = VFIO_PLATFORM_DEVICE(sbdev); - VFIOINTp *intp; - - QLIST_FOREACH(intp, &vdev->intp_list, next) { - if (intp->qemuirq == irq) { - break; - } - } - assert(intp); - - if (vfio_set_trigger_eventfd(intp, vfio_intp_interrupt)) { - abort(); - } -} - -/* - * Functions used for irqfd - */ - -/** - * vfio_set_resample_eventfd - sets the resamplefd for an IRQ - * @intp: the IRQ struct handle - * programs the VFIO driver to unmask this IRQ when the - * intp->unmask eventfd is triggered - */ -static int vfio_set_resample_eventfd(VFIOINTp *intp) -{ - int32_t fd = event_notifier_get_fd(intp->unmask); - VFIODevice *vbasedev = &intp->vdev->vbasedev; - Error *err = NULL; - - qemu_set_fd_handler(fd, NULL, NULL, NULL); - if (!vfio_device_irq_set_signaling(vbasedev, intp->pin, 0, - VFIO_IRQ_SET_ACTION_UNMASK, fd, &err)) { - error_reportf_err(err, VFIO_MSG_PREFIX, vbasedev->name); - return -EINVAL; - } - return 0; -} - -/** - * vfio_start_irqfd_injection - starts the virtual IRQ injection using - * irqfd - * - * @sbdev: the sysbus device handle - * @irq: the qemu irq handle - * - * In case the irqfd setup fails, we fallback to userspace handled eventfd - */ -static void vfio_start_irqfd_injection(SysBusDevice *sbdev, qemu_irq irq) -{ - VFIOPlatformDevice *vdev = VFIO_PLATFORM_DEVICE(sbdev); - VFIOINTp *intp; - - if (!kvm_irqfds_enabled() || !kvm_resamplefds_enabled() || - !vdev->irqfd_allowed) { - goto fail_irqfd; - } - - QLIST_FOREACH(intp, &vdev->intp_list, next) { - if (intp->qemuirq == irq) { - break; - } - } - assert(intp); - - if (kvm_irqchip_add_irqfd_notifier(kvm_state, intp->interrupt, - intp->unmask, irq) < 0) { - goto fail_irqfd; - } - - if (vfio_set_trigger_eventfd(intp, NULL) < 0) { - goto fail_vfio; - } - if (vfio_irq_is_automasked(intp)) { - if (vfio_set_resample_eventfd(intp) < 0) { - goto fail_vfio; - } - trace_vfio_platform_start_level_irqfd_injection(intp->pin, - event_notifier_get_fd(intp->interrupt), - event_notifier_get_fd(intp->unmask)); - } else { - trace_vfio_platform_start_edge_irqfd_injection(intp->pin, - event_notifier_get_fd(intp->interrupt)); - } - - intp->kvm_accel = true; - - return; -fail_vfio: - kvm_irqchip_remove_irqfd_notifier(kvm_state, intp->interrupt, irq); - abort(); -fail_irqfd: - vfio_start_eventfd_injection(sbdev, irq); -} - -/* VFIO skeleton */ - -static void vfio_platform_compute_needs_reset(VFIODevice *vbasedev) -{ - vbasedev->needs_reset = true; -} - -/* not implemented yet */ -static int vfio_platform_hot_reset_multi(VFIODevice *vbasedev) -{ - return -1; -} - -/** - * vfio_populate_device - Allocate and populate MMIO region - * and IRQ structs according to driver returned information - * @vbasedev: the VFIO device handle - * @errp: error object - * - */ -static bool vfio_populate_device(VFIODevice *vbasedev, Error **errp) -{ - VFIOINTp *intp, *tmp; - int i, ret = -1; - VFIOPlatformDevice *vdev = - container_of(vbasedev, VFIOPlatformDevice, vbasedev); - - if (!(vbasedev->flags & VFIO_DEVICE_FLAGS_PLATFORM)) { - error_setg(errp, "this isn't a platform device"); - return false; - } - - vdev->regions = g_new0(VFIORegion *, vbasedev->num_regions); - - for (i = 0; i < vbasedev->num_regions; i++) { - char *name = g_strdup_printf("VFIO %s region %d\n", vbasedev->name, i); - - vdev->regions[i] = g_new0(VFIORegion, 1); - ret = vfio_region_setup(OBJECT(vdev), vbasedev, - vdev->regions[i], i, name); - g_free(name); - if (ret) { - error_setg_errno(errp, -ret, "failed to get region %d info", i); - goto reg_error; - } - } - - vdev->mmap_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL, - vfio_intp_mmap_enable, vdev); - - QSIMPLEQ_INIT(&vdev->pending_intp_queue); - - for (i = 0; i < vbasedev->num_irqs; i++) { - struct vfio_irq_info irq; - - ret = vfio_device_get_irq_info(vbasedev, i, &irq); - - if (ret) { - error_setg_errno(errp, -ret, "failed to get device irq info"); - goto irq_err; - } else { - trace_vfio_platform_populate_interrupts(irq.index, - irq.count, - irq.flags); - intp = vfio_init_intp(vbasedev, irq, errp); - if (!intp) { - goto irq_err; - } - } - } - return true; -irq_err: - timer_del(vdev->mmap_timer); - QLIST_FOREACH_SAFE(intp, &vdev->intp_list, next, tmp) { - QLIST_REMOVE(intp, next); - g_free(intp); - } -reg_error: - for (i = 0; i < vbasedev->num_regions; i++) { - if (vdev->regions[i]) { - vfio_region_finalize(vdev->regions[i]); - } - g_free(vdev->regions[i]); - } - g_free(vdev->regions); - return false; -} - -/* specialized functions for VFIO Platform devices */ -static VFIODeviceOps vfio_platform_ops = { - .vfio_compute_needs_reset = vfio_platform_compute_needs_reset, - .vfio_hot_reset_multi = vfio_platform_hot_reset_multi, - .vfio_eoi = vfio_platform_eoi, -}; - -/** - * vfio_base_device_init - perform preliminary VFIO setup - * @vbasedev: the VFIO device handle - * @errp: error object - * - * Implement the VFIO command sequence that allows to discover - * assigned device resources: group extraction, device - * fd retrieval, resource query. - * Precondition: the device name must be initialized - */ -static bool vfio_base_device_init(VFIODevice *vbasedev, Error **errp) -{ - /* @fd takes precedence over @sysfsdev which takes precedence over @host */ - if (vbasedev->fd < 0 && vbasedev->sysfsdev) { - vfio_device_free_name(vbasedev); - vbasedev->name = g_path_get_basename(vbasedev->sysfsdev); - } else if (vbasedev->fd < 0) { - if (!vbasedev->name || strchr(vbasedev->name, '/')) { - error_setg(errp, "wrong host device name"); - return false; - } - - vbasedev->sysfsdev = g_strdup_printf("/sys/bus/platform/devices/%s", - vbasedev->name); - } - - if (!vfio_device_get_name(vbasedev, errp)) { - return false; - } - - if (!vfio_device_attach(vbasedev->name, vbasedev, - &address_space_memory, errp)) { - return false; - } - - if (vfio_populate_device(vbasedev, errp)) { - return true; - } - - vfio_device_detach(vbasedev); - return false; -} - -/** - * vfio_platform_realize - the device realize function - * @dev: device state pointer - * @errp: error - * - * initialize the device, its memory regions and IRQ structures - * IRQ are started separately - */ -static void vfio_platform_realize(DeviceState *dev, Error **errp) -{ - ERRP_GUARD(); - VFIOPlatformDevice *vdev = VFIO_PLATFORM_DEVICE(dev); - SysBusDevice *sbdev = SYS_BUS_DEVICE(dev); - VFIODevice *vbasedev = &vdev->vbasedev; - int i; - - warn_report("-device vfio-platform is deprecated"); - qemu_mutex_init(&vdev->intp_mutex); - - trace_vfio_platform_realize(vbasedev->sysfsdev ? - vbasedev->sysfsdev : vbasedev->name, - vdev->compat); - - if (!vfio_base_device_init(vbasedev, errp)) { - goto init_err; - } - - if (!vdev->compat) { - GError *gerr = NULL; - gchar *contents; - gsize length; - char *path; - - path = g_strdup_printf("%s/of_node/compatible", vbasedev->sysfsdev); - if (!g_file_get_contents(path, &contents, &length, &gerr)) { - error_setg(errp, "%s", gerr->message); - g_error_free(gerr); - g_free(path); - return; - } - g_free(path); - vdev->compat = contents; - for (vdev->num_compat = 0; length; vdev->num_compat++) { - size_t skip = strlen(contents) + 1; - contents += skip; - length -= skip; - } - } - - for (i = 0; i < vbasedev->num_regions; i++) { - if (vfio_region_mmap(vdev->regions[i])) { - warn_report("%s mmap unsupported, performance may be slow", - memory_region_name(vdev->regions[i]->mem)); - } - sysbus_init_mmio(sbdev, vdev->regions[i]->mem); - } - return; - -init_err: - if (vdev->vbasedev.name) { - error_prepend(errp, VFIO_MSG_PREFIX, vdev->vbasedev.name); - } else { - error_prepend(errp, "vfio error: "); - } -} - -static const VMStateDescription vfio_platform_vmstate = { - .name = "vfio-platform", - .unmigratable = 1, -}; - -static const Property vfio_platform_dev_properties[] = { - DEFINE_PROP_STRING("host", VFIOPlatformDevice, vbasedev.name), - DEFINE_PROP_STRING("sysfsdev", VFIOPlatformDevice, vbasedev.sysfsdev), - DEFINE_PROP_BOOL("x-no-mmap", VFIOPlatformDevice, vbasedev.no_mmap, false), - DEFINE_PROP_UINT32("mmap-timeout-ms", VFIOPlatformDevice, - mmap_timeout, 1100), - DEFINE_PROP_BOOL("x-irqfd", VFIOPlatformDevice, irqfd_allowed, true), -#ifdef CONFIG_IOMMUFD - DEFINE_PROP_LINK("iommufd", VFIOPlatformDevice, vbasedev.iommufd, - TYPE_IOMMUFD_BACKEND, IOMMUFDBackend *), -#endif -}; - -static void vfio_platform_instance_init(Object *obj) -{ - VFIOPlatformDevice *vdev = VFIO_PLATFORM_DEVICE(obj); - VFIODevice *vbasedev = &vdev->vbasedev; - - vfio_device_init(vbasedev, VFIO_DEVICE_TYPE_PLATFORM, &vfio_platform_ops, - DEVICE(vdev), false); -} - -#ifdef CONFIG_IOMMUFD -static void vfio_platform_set_fd(Object *obj, const char *str, Error **errp) -{ - vfio_device_set_fd(&VFIO_PLATFORM_DEVICE(obj)->vbasedev, str, errp); -} -#endif - -static void vfio_platform_class_init(ObjectClass *klass, const void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(klass); - - dc->realize = vfio_platform_realize; - device_class_set_props(dc, vfio_platform_dev_properties); -#ifdef CONFIG_IOMMUFD - object_class_property_add_str(klass, "fd", NULL, vfio_platform_set_fd); -#endif - dc->vmsd = &vfio_platform_vmstate; - dc->desc = "VFIO-based platform device assignment"; - sbc->connect_irq_notifier = vfio_start_irqfd_injection; - set_bit(DEVICE_CATEGORY_MISC, dc->categories); - - object_class_property_set_description(klass, /* 2.4 */ - "host", - "Host device name of assigned device"); - object_class_property_set_description(klass, /* 2.4 and 2.5 */ - "x-no-mmap", - "Disable MMAP for device. Allows to trace MMIO " - "accesses (DEBUG)"); - object_class_property_set_description(klass, /* 2.4 */ - "mmap-timeout-ms", - "When EOI is not provided by KVM/QEMU, wait time " - "(milliseconds) to re-enable device direct access " - "after level interrupt (DEBUG)"); - object_class_property_set_description(klass, /* 2.4 */ - "x-irqfd", - "Allow disabling irqfd support (DEBUG)"); - object_class_property_set_description(klass, /* 2.6 */ - "sysfsdev", - "Host sysfs path of assigned device"); -#ifdef CONFIG_IOMMUFD - object_class_property_set_description(klass, /* 9.0 */ - "iommufd", - "Set host IOMMUFD backend device"); -#endif -} - -static const TypeInfo vfio_platform_dev_info = { - .name = TYPE_VFIO_PLATFORM, - .parent = TYPE_DYNAMIC_SYS_BUS_DEVICE, - .instance_size = sizeof(VFIOPlatformDevice), - .instance_init = vfio_platform_instance_init, - .class_init = vfio_platform_class_init, - .class_size = sizeof(VFIOPlatformDeviceClass), -}; - -static void register_vfio_platform_dev_type(void) -{ - type_register_static(&vfio_platform_dev_info); -} - -type_init(register_vfio_platform_dev_type) diff --git a/hw/vfio/trace-events b/hw/vfio/trace-events index fc6ed230d0..e3d571f8c8 100644 --- a/hw/vfio/trace-events +++ b/hw/vfio/trace-events @@ -127,17 +127,6 @@ vfio_region_unmap(const char *name, unsigned long offset, unsigned long end) "Re vfio_region_sparse_mmap_header(const char *name, int index, int nr_areas) "Device %s region %d: %d sparse mmap entries" vfio_region_sparse_mmap_entry(int i, unsigned long start, unsigned long end) "sparse entry %d [0x%lx - 0x%lx]" -# platform.c -vfio_platform_realize(char *name, char *compat) "vfio device %s, compat = %s" -vfio_platform_eoi(int pin, int fd) "EOI IRQ pin %d (fd=%d)" -vfio_platform_intp_mmap_enable(int pin) "IRQ #%d still active, stay in slow path" -vfio_platform_intp_interrupt(int pin, int fd) "Inject IRQ #%d (fd = %d)" -vfio_platform_intp_inject_pending_lockheld(int pin, int fd) "Inject pending IRQ #%d (fd = %d)" -vfio_platform_populate_interrupts(int pin, int count, int flags) "- IRQ index %d: count %d, flags=0x%x" -vfio_intp_interrupt_set_pending(int index) "irq %d is set PENDING" -vfio_platform_start_level_irqfd_injection(int index, int fd, int resamplefd) "IRQ index=%d, fd = %d, resamplefd = %d" -vfio_platform_start_edge_irqfd_injection(int index, int fd) "IRQ index=%d, fd = %d" - # spapr.c vfio_prereg_listener_region_add_skip(uint64_t start, uint64_t end) "0x%"PRIx64" - 0x%"PRIx64 vfio_prereg_listener_region_del_skip(uint64_t start, uint64_t end) "0x%"PRIx64" - 0x%"PRIx64 diff --git a/include/hw/vfio/vfio-device.h b/include/hw/vfio/vfio-device.h index 6e4d5ccdac..e7e6243e2d 100644 --- a/include/hw/vfio/vfio-device.h +++ b/include/hw/vfio/vfio-device.h @@ -36,7 +36,7 @@ enum { VFIO_DEVICE_TYPE_PCI = 0, - VFIO_DEVICE_TYPE_PLATFORM = 1, + VFIO_DEVICE_TYPE_UNUSED = 1, VFIO_DEVICE_TYPE_CCW = 2, VFIO_DEVICE_TYPE_AP = 3, }; diff --git a/include/hw/vfio/vfio-platform.h b/include/hw/vfio/vfio-platform.h deleted file mode 100644 index 256d8500b7..0000000000 --- a/include/hw/vfio/vfio-platform.h +++ /dev/null @@ -1,78 +0,0 @@ -/* - * vfio based device assignment support - platform devices - * - * Copyright Linaro Limited, 2014 - * - * Authors: - * Kim Phillips - * - * This work is licensed under the terms of the GNU GPL, version 2. See - * the COPYING file in the top-level directory. - * - * Based on vfio based PCI device assignment support: - * Copyright Red Hat, Inc. 2012 - */ - -#ifndef HW_VFIO_VFIO_PLATFORM_H -#define HW_VFIO_VFIO_PLATFORM_H - -#include "hw/sysbus.h" -#include "hw/vfio/vfio-device.h" -#include "qemu/event_notifier.h" -#include "qemu/queue.h" -#include "qom/object.h" - -#define TYPE_VFIO_PLATFORM "vfio-platform" - -enum { - VFIO_IRQ_INACTIVE = 0, - VFIO_IRQ_PENDING = 1, - VFIO_IRQ_ACTIVE = 2, - /* VFIO_IRQ_ACTIVE_AND_PENDING cannot happen with VFIO */ -}; - -typedef struct VFIOINTp { - QLIST_ENTRY(VFIOINTp) next; /* entry for IRQ list */ - QSIMPLEQ_ENTRY(VFIOINTp) pqnext; /* entry for pending IRQ queue */ - EventNotifier *interrupt; /* eventfd triggered on interrupt */ - EventNotifier *unmask; /* eventfd for unmask on QEMU bypass */ - qemu_irq qemuirq; - struct VFIOPlatformDevice *vdev; /* back pointer to device */ - int state; /* inactive, pending, active */ - uint8_t pin; /* index */ - uint32_t flags; /* IRQ info flags */ - bool kvm_accel; /* set when QEMU bypass through KVM enabled */ -} VFIOINTp; - -/* function type for user side eventfd handler */ -typedef void (*eventfd_user_side_handler_t)(VFIOINTp *intp); - -typedef struct VFIORegion VFIORegion; - -struct VFIOPlatformDevice { - SysBusDevice sbdev; - VFIODevice vbasedev; /* not a QOM object */ - VFIORegion **regions; - QLIST_HEAD(, VFIOINTp) intp_list; /* list of IRQs */ - /* queue of pending IRQs */ - QSIMPLEQ_HEAD(, VFIOINTp) pending_intp_queue; - char *compat; /* DT compatible values, separated by NUL */ - unsigned int num_compat; /* number of compatible values */ - uint32_t mmap_timeout; /* delay to re-enable mmaps after interrupt */ - QEMUTimer *mmap_timer; /* allows fast-path resume after IRQ hit */ - QemuMutex intp_mutex; /* protect the intp_list IRQ state */ - bool irqfd_allowed; /* debug option to force irqfd on/off */ -}; -typedef struct VFIOPlatformDevice VFIOPlatformDevice; - -struct VFIOPlatformDeviceClass { - /*< private >*/ - SysBusDeviceClass parent_class; - /*< public >*/ -}; -typedef struct VFIOPlatformDeviceClass VFIOPlatformDeviceClass; - -DECLARE_OBJ_CHECKERS(VFIOPlatformDevice, VFIOPlatformDeviceClass, - VFIO_PLATFORM_DEVICE, TYPE_VFIO_PLATFORM) - -#endif /* HW_VFIO_VFIO_PLATFORM_H */ From e7a47f717718441b546090fe3fa91e2705ca125b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Mon, 1 Sep 2025 08:46:31 +0200 Subject: [PATCH 0331/2396] vfio: Move vfio-region.h under hw/vfio/ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since the removal of vfio-platform, header file vfio-region.h no longer needs to be a public VFIO interface. Move it under hw/vfio. Reviewed-by: Eric Auger Reviewed-by: Alex Williamson Link: https://lore.kernel.org/qemu-devel/20250901064631.530723-9-clg@redhat.com Signed-off-by: Cédric Le Goater --- {include/hw => hw}/vfio/vfio-region.h | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {include/hw => hw}/vfio/vfio-region.h (100%) diff --git a/include/hw/vfio/vfio-region.h b/hw/vfio/vfio-region.h similarity index 100% rename from include/hw/vfio/vfio-region.h rename to hw/vfio/vfio-region.h From 36cd81dc139f899127d868ba9baaf3079c336efc Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Wed, 13 Aug 2025 07:17:47 -0700 Subject: [PATCH 0332/2396] vfio/container: set error on cpr failure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Set an error message if vfio_cpr_ram_discard_register_listener fails so the fail label gets a valid error object. Reported-by: Cédric Le Goater Fixes: eba1f657cbb1 ("vfio/container: recover from unmap-all-vaddr failure") Signed-off-by: Steve Sistare Reviewed-by: Zhenzhong Duan Link: https://lore.kernel.org/qemu-devel/1755094667-281419-1-git-send-email-steven.sistare@oracle.com Signed-off-by: Cédric Le Goater --- hw/vfio/listener.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/hw/vfio/listener.c b/hw/vfio/listener.c index f498e23a93..5ebafaa07e 100644 --- a/hw/vfio/listener.c +++ b/hw/vfio/listener.c @@ -574,6 +574,9 @@ void vfio_container_region_add(VFIOContainerBase *bcontainer, vfio_ram_discard_register_listener(bcontainer, section); } else if (!vfio_cpr_ram_discard_register_listener(bcontainer, section)) { + error_setg(&err, + "vfio_cpr_ram_discard_register_listener for %s failed", + memory_region_name(section->mr)); goto fail; } return; From ceb59c1cc61dee57c8806571e7c723e555914547 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Thu, 14 Aug 2025 17:34:19 +0200 Subject: [PATCH 0333/2396] vfio: Report an error when the 'dma_max_mappings' limit is reached MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The VFIO IOMMU Type1 kernel driver enforces a default IOMMU mapping limit of 65535, which is configurable via the 'dma_max_mappings' module parameter. When this limit is reached, QEMU issues a warning and fails the mapping operation, but allows the VM to continue running, potentially causing issues later. This scenario occurs with SEV-SNP guests, which must update all IOMMU mappings during initialization. To address this, update vfio_ram_discard_register_listener() to accept an 'Error **' parameter and propagate the error to the caller. This change will halt the VM immediately, at init time, with the same error message. Additionally, the same behavior will be enforced at runtime. While this might be considered too brutal, the rarity of this case and the planned removal of the dma_max_mappings module parameter make it a reasonable approach. Cc: Alex Williamson Reviewed-by: Yi Liu Reviewed-by: Alex Williamson Link: https://lore.kernel.org/qemu-devel/20250814153419.1643897-1-clg@redhat.com Signed-off-by: Cédric Le Goater --- hw/vfio/listener.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/hw/vfio/listener.c b/hw/vfio/listener.c index 5ebafaa07e..c244be5e21 100644 --- a/hw/vfio/listener.c +++ b/hw/vfio/listener.c @@ -250,8 +250,9 @@ static int vfio_ram_discard_notify_populate(RamDiscardListener *rdl, return 0; } -static void vfio_ram_discard_register_listener(VFIOContainerBase *bcontainer, - MemoryRegionSection *section) +static bool vfio_ram_discard_register_listener(VFIOContainerBase *bcontainer, + MemoryRegionSection *section, + Error **errp) { RamDiscardManager *rdm = memory_region_get_ram_discard_manager(section->mr); int target_page_size = qemu_target_page_size(); @@ -316,13 +317,15 @@ static void vfio_ram_discard_register_listener(VFIOContainerBase *bcontainer, if (vrdl_mappings + max_memslots - vrdl_count > bcontainer->dma_max_mappings) { - warn_report("%s: possibly running out of DMA mappings. E.g., try" + error_setg(errp, "%s: possibly running out of DMA mappings. E.g., try" " increasing the 'block-size' of virtio-mem devies." " Maximum possible DMA mappings: %d, Maximum possible" " memslots: %d", __func__, bcontainer->dma_max_mappings, max_memslots); + return false; } } + return true; } static void vfio_ram_discard_unregister_listener(VFIOContainerBase *bcontainer, @@ -571,7 +574,9 @@ void vfio_container_region_add(VFIOContainerBase *bcontainer, */ if (memory_region_has_ram_discard_manager(section->mr)) { if (!cpr_remap) { - vfio_ram_discard_register_listener(bcontainer, section); + if (!vfio_ram_discard_register_listener(bcontainer, section, &err)) { + goto fail; + } } else if (!vfio_cpr_ram_discard_register_listener(bcontainer, section)) { error_setg(&err, From 1b50621881241ac5bc75ae7f8aa4c278ada8a668 Mon Sep 17 00:00:00 2001 From: John Levon Date: Wed, 27 Aug 2025 20:08:10 +0100 Subject: [PATCH 0334/2396] hw/vfio-user: add x-pci-class-code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This new option was not added to vfio_user_pci_dev_properties, which caused an incorrect class code for vfio-user devices. Fixes: a59d06305fff ("vfio/pci: Introduce x-pci-class-code option") Signed-off-by: John Levon Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250827190810.1645340-1-john.levon@nutanix.com Signed-off-by: Cédric Le Goater --- hw/vfio-user/pci.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hw/vfio-user/pci.c b/hw/vfio-user/pci.c index be71c77729..dfaa89498d 100644 --- a/hw/vfio-user/pci.c +++ b/hw/vfio-user/pci.c @@ -406,6 +406,8 @@ static const Property vfio_user_pci_dev_properties[] = { sub_vendor_id, PCI_ANY_ID), DEFINE_PROP_UINT32("x-pci-sub-device-id", VFIOPCIDevice, sub_device_id, PCI_ANY_ID), + DEFINE_PROP_UINT32("x-pci-class-code", VFIOPCIDevice, + class_code, PCI_ANY_ID), DEFINE_PROP_BOOL("x-send-queued", VFIOUserPCIDevice, send_queued, false), DEFINE_PROP_UINT32("x-msg-timeout", VFIOUserPCIDevice, wait_time, 5000), DEFINE_PROP_BOOL("x-no-posted-writes", VFIOUserPCIDevice, no_post, false), From bb1a6f1f43374a1850c314c4d0e945667d013d07 Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Fri, 22 Aug 2025 02:40:42 -0400 Subject: [PATCH 0335/2396] vfio: Introduce helper vfio_pci_from_vfio_device() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce helper vfio_pci_from_vfio_device() to transform from VFIODevice to VFIOPCIDevice, also to hide low level VFIO_DEVICE_TYPE_PCI type check. Suggested-by: Cédric Le Goater Signed-off-by: Zhenzhong Duan Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250822064101.123526-5-zhenzhong.duan@intel.com [ clg: Added documentation ] Signed-off-by: Cédric Le Goater --- hw/vfio/container.c | 4 ++-- hw/vfio/device.c | 2 +- hw/vfio/iommufd.c | 4 ++-- hw/vfio/listener.c | 4 ++-- hw/vfio/pci.c | 9 +++++++++ hw/vfio/pci.h | 12 ++++++++++++ 6 files changed, 28 insertions(+), 7 deletions(-) diff --git a/hw/vfio/container.c b/hw/vfio/container.c index 3e13feaa74..134ddccc52 100644 --- a/hw/vfio/container.c +++ b/hw/vfio/container.c @@ -1087,7 +1087,7 @@ static int vfio_legacy_pci_hot_reset(VFIODevice *vbasedev, bool single) /* Prep dependent devices for reset and clear our marker. */ QLIST_FOREACH(vbasedev_iter, &group->device_list, next) { if (!vbasedev_iter->dev->realized || - vbasedev_iter->type != VFIO_DEVICE_TYPE_PCI) { + !vfio_pci_from_vfio_device(vbasedev_iter)) { continue; } tmp = container_of(vbasedev_iter, VFIOPCIDevice, vbasedev); @@ -1172,7 +1172,7 @@ out: QLIST_FOREACH(vbasedev_iter, &group->device_list, next) { if (!vbasedev_iter->dev->realized || - vbasedev_iter->type != VFIO_DEVICE_TYPE_PCI) { + !vfio_pci_from_vfio_device(vbasedev_iter)) { continue; } tmp = container_of(vbasedev_iter, VFIOPCIDevice, vbasedev); diff --git a/hw/vfio/device.c b/hw/vfio/device.c index 52a1996dc4..08f12ac31f 100644 --- a/hw/vfio/device.c +++ b/hw/vfio/device.c @@ -129,7 +129,7 @@ static inline const char *action_to_str(int action) static const char *index_to_str(VFIODevice *vbasedev, int index) { - if (vbasedev->type != VFIO_DEVICE_TYPE_PCI) { + if (!vfio_pci_from_vfio_device(vbasedev)) { return NULL; } diff --git a/hw/vfio/iommufd.c b/hw/vfio/iommufd.c index 48c590b6a9..8c27222f75 100644 --- a/hw/vfio/iommufd.c +++ b/hw/vfio/iommufd.c @@ -737,8 +737,8 @@ iommufd_cdev_dep_get_realized_vpdev(struct vfio_pci_dependent_device *dep_dev, } vbasedev_tmp = iommufd_cdev_pci_find_by_devid(dep_dev->devid); - if (!vbasedev_tmp || !vbasedev_tmp->dev->realized || - vbasedev_tmp->type != VFIO_DEVICE_TYPE_PCI) { + if (!vfio_pci_from_vfio_device(vbasedev_tmp) || + !vbasedev_tmp->dev->realized) { return NULL; } diff --git a/hw/vfio/listener.c b/hw/vfio/listener.c index c244be5e21..e093833165 100644 --- a/hw/vfio/listener.c +++ b/hw/vfio/listener.c @@ -453,7 +453,7 @@ static void vfio_device_error_append(VFIODevice *vbasedev, Error **errp) * MMIO region mapping failures are not fatal but in this case PCI * peer-to-peer transactions are broken. */ - if (vbasedev && vbasedev->type == VFIO_DEVICE_TYPE_PCI) { + if (vfio_pci_from_vfio_device(vbasedev)) { error_append_hint(errp, "%s: PCI peer-to-peer transactions " "on BARs are not supported.\n", vbasedev->name); } @@ -759,7 +759,7 @@ static bool vfio_section_is_vfio_pci(MemoryRegionSection *section, owner = memory_region_owner(section->mr); QLIST_FOREACH(vbasedev, &bcontainer->device_list, container_next) { - if (vbasedev->type != VFIO_DEVICE_TYPE_PCI) { + if (!vfio_pci_from_vfio_device(vbasedev)) { continue; } pcidev = container_of(vbasedev, VFIOPCIDevice, vbasedev); diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index 07257d0fa0..3fe5b03eb1 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -2833,6 +2833,15 @@ static int vfio_pci_load_config(VFIODevice *vbasedev, QEMUFile *f) return ret; } +/* Transform from VFIODevice to VFIOPCIDevice. Return NULL if fails. */ +VFIOPCIDevice *vfio_pci_from_vfio_device(VFIODevice *vbasedev) +{ + if (vbasedev && vbasedev->type == VFIO_DEVICE_TYPE_PCI) { + return container_of(vbasedev, VFIOPCIDevice, vbasedev); + } + return NULL; +} + void vfio_sub_page_bar_update_mappings(VFIOPCIDevice *vdev) { PCIDevice *pdev = &vdev->pdev; diff --git a/hw/vfio/pci.h b/hw/vfio/pci.h index 923cf9c2f7..96144b6fde 100644 --- a/hw/vfio/pci.h +++ b/hw/vfio/pci.h @@ -226,6 +226,18 @@ void vfio_pci_write_config(PCIDevice *pdev, uint64_t vfio_vga_read(void *opaque, hwaddr addr, unsigned size); void vfio_vga_write(void *opaque, hwaddr addr, uint64_t data, unsigned size); +/** + * vfio_pci_from_vfio_device: Transform from VFIODevice to + * VFIOPCIDevice + * + * This function checks if the given @vbasedev is a VFIO PCI device. + * If it is, it returns the containing VFIOPCIDevice. + * + * @vbasedev: The VFIODevice to transform + * + * Return: The VFIOPCIDevice on success, NULL on failure. + */ +VFIOPCIDevice *vfio_pci_from_vfio_device(VFIODevice *vbasedev); void vfio_sub_page_bar_update_mappings(VFIOPCIDevice *vdev); bool vfio_opt_rom_in_denylist(VFIOPCIDevice *vdev); bool vfio_config_quirk_setup(VFIOPCIDevice *vdev, Error **errp); From 42875d256d204e69b608f2bd265f85fae32dd4bd Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Tue, 15 Jul 2025 10:25:41 +0100 Subject: [PATCH 0336/2396] vfio/vfio-container-base.h: update VFIOContainerBase declaration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update the VFIOContainerBase declaration to match our current coding guidelines: remove the explicit typedef (this is already handled by the OBJECT_DECLARE_TYPE() macro), add a blank line after the parent object, rename parent to parent_obj, and move the macro declaration next to the VFIOContainerBase struct declaration. Signed-off-by: Mark Cave-Ayland Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250715093110.107317-2-mark.caveayland@nutanix.com Signed-off-by: Cédric Le Goater --- include/hw/vfio/vfio-container-base.h | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/include/hw/vfio/vfio-container-base.h b/include/hw/vfio/vfio-container-base.h index bded6e993f..acbd48a18a 100644 --- a/include/hw/vfio/vfio-container-base.h +++ b/include/hw/vfio/vfio-container-base.h @@ -33,8 +33,9 @@ typedef struct VFIOAddressSpace { /* * This is the base object for vfio container backends */ -typedef struct VFIOContainerBase { - Object parent; +struct VFIOContainerBase { + Object parent_obj; + VFIOAddressSpace *space; MemoryListener listener; Error *error; @@ -51,7 +52,10 @@ typedef struct VFIOContainerBase { QLIST_HEAD(, VFIODevice) device_list; GList *iova_ranges; NotifierWithReturn cpr_reboot_notifier; -} VFIOContainerBase; +}; + +#define TYPE_VFIO_IOMMU "vfio-iommu" +OBJECT_DECLARE_TYPE(VFIOContainerBase, VFIOIOMMUClass, VFIO_IOMMU) typedef struct VFIOGuestIOMMU { VFIOContainerBase *bcontainer; @@ -105,14 +109,11 @@ vfio_container_get_page_size_mask(const VFIOContainerBase *bcontainer) return bcontainer->pgsizes; } -#define TYPE_VFIO_IOMMU "vfio-iommu" #define TYPE_VFIO_IOMMU_LEGACY TYPE_VFIO_IOMMU "-legacy" #define TYPE_VFIO_IOMMU_SPAPR TYPE_VFIO_IOMMU "-spapr" #define TYPE_VFIO_IOMMU_IOMMUFD TYPE_VFIO_IOMMU "-iommufd" #define TYPE_VFIO_IOMMU_USER TYPE_VFIO_IOMMU "-user" -OBJECT_DECLARE_TYPE(VFIOContainerBase, VFIOIOMMUClass, VFIO_IOMMU) - struct VFIOIOMMUClass { ObjectClass parent_class; From 98c12de5aeb1cb464a9cde13cd7a53dd6520d3aa Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Tue, 15 Jul 2025 10:25:42 +0100 Subject: [PATCH 0337/2396] vfio/vfio-container.h: update VFIOContainer declaration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update the VFIOContainer declaration so that it is closer to our coding guidelines: emove the explicit typedef (this is already handled by the OBJECT_DECLARE_TYPE() macro) and add a blank line after the parent object. Signed-off-by: Mark Cave-Ayland Reviewed-by: Cédric Le Goater Reviewed-by: Philippe Mathieu-Daudé Link: https://lore.kernel.org/qemu-devel/20250715093110.107317-3-mark.caveayland@nutanix.com Signed-off-by: Cédric Le Goater --- include/hw/vfio/vfio-container.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/include/hw/vfio/vfio-container.h b/include/hw/vfio/vfio-container.h index 21e5807e48..50c91788d5 100644 --- a/include/hw/vfio/vfio-container.h +++ b/include/hw/vfio/vfio-container.h @@ -25,13 +25,14 @@ typedef struct VFIOGroup { bool ram_block_discard_allowed; } VFIOGroup; -typedef struct VFIOContainer { +struct VFIOContainer { VFIOContainerBase bcontainer; + int fd; /* /dev/vfio/vfio, empowered by the attached groups */ unsigned iommu_type; QLIST_HEAD(, VFIOGroup) group_list; VFIOContainerCPR cpr; -} VFIOContainer; +}; OBJECT_DECLARE_SIMPLE_TYPE(VFIOContainer, VFIO_IOMMU_LEGACY); From 5255ba39b16539bd4b28a961d196812af1684c02 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Tue, 15 Jul 2025 10:25:43 +0100 Subject: [PATCH 0338/2396] hw/vfio/cpr-legacy.c: use QOM casts where appropriate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use QOM casts to convert between VFIOContainer and VFIOContainerBase instead of accessing bcontainer directly. Signed-off-by: Mark Cave-Ayland Reviewed-by: Cédric Le Goater Reviewed-by: Steve Sistare Link: https://lore.kernel.org/qemu-devel/20250715093110.107317-4-mark.caveayland@nutanix.com Signed-off-by: Cédric Le Goater --- hw/vfio/cpr-legacy.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/hw/vfio/cpr-legacy.c b/hw/vfio/cpr-legacy.c index 553b203e9b..8f437194fa 100644 --- a/hw/vfio/cpr-legacy.c +++ b/hw/vfio/cpr-legacy.c @@ -41,8 +41,8 @@ static int vfio_legacy_cpr_dma_map(const VFIOContainerBase *bcontainer, hwaddr iova, ram_addr_t size, void *vaddr, bool readonly, MemoryRegion *mr) { - const VFIOContainer *container = container_of(bcontainer, VFIOContainer, - bcontainer); + const VFIOContainer *container = VFIO_IOMMU_LEGACY(bcontainer); + struct vfio_iommu_type1_dma_map map = { .argsz = sizeof(map), .flags = VFIO_DMA_MAP_FLAG_VADDR, @@ -65,7 +65,7 @@ static void vfio_region_remap(MemoryListener *listener, { VFIOContainer *container = container_of(listener, VFIOContainer, cpr.remap_listener); - vfio_container_region_add(&container->bcontainer, section, true); + vfio_container_region_add(VFIO_IOMMU(container), section, true); } static bool vfio_cpr_supported(VFIOContainer *container, Error **errp) @@ -98,7 +98,7 @@ static int vfio_container_pre_save(void *opaque) static int vfio_container_post_load(void *opaque, int version_id) { VFIOContainer *container = opaque; - VFIOContainerBase *bcontainer = &container->bcontainer; + VFIOContainerBase *bcontainer = VFIO_IOMMU(container); VFIOIOMMUClass *vioc = VFIO_IOMMU_GET_CLASS(bcontainer); dma_map_fn saved_dma_map = vioc->dma_map; Error *local_err = NULL; @@ -135,7 +135,7 @@ static int vfio_cpr_fail_notifier(NotifierWithReturn *notifier, { VFIOContainer *container = container_of(notifier, VFIOContainer, cpr.transfer_notifier); - VFIOContainerBase *bcontainer = &container->bcontainer; + VFIOContainerBase *bcontainer = VFIO_IOMMU(container); if (e->type != MIG_EVENT_PRECOPY_FAILED) { return 0; @@ -167,7 +167,7 @@ static int vfio_cpr_fail_notifier(NotifierWithReturn *notifier, bool vfio_legacy_cpr_register_container(VFIOContainer *container, Error **errp) { - VFIOContainerBase *bcontainer = &container->bcontainer; + VFIOContainerBase *bcontainer = VFIO_IOMMU(container); Error **cpr_blocker = &container->cpr.blocker; migration_add_notifier_mode(&bcontainer->cpr_reboot_notifier, @@ -191,7 +191,7 @@ bool vfio_legacy_cpr_register_container(VFIOContainer *container, Error **errp) void vfio_legacy_cpr_unregister_container(VFIOContainer *container) { - VFIOContainerBase *bcontainer = &container->bcontainer; + VFIOContainerBase *bcontainer = VFIO_IOMMU(container); migration_remove_notifier(&bcontainer->cpr_reboot_notifier); migrate_del_blocker(&container->cpr.blocker); From 5947f69b63639b54f02db1727c559f8aae32d849 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Tue, 15 Jul 2025 10:25:44 +0100 Subject: [PATCH 0339/2396] hw/vfio/container.c: use QOM casts where appropriate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use QOM casts to convert between VFIOContainer and VFIOContainerBase instead of accessing bcontainer directly. Signed-off-by: Mark Cave-Ayland Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250715093110.107317-5-mark.caveayland@nutanix.com Signed-off-by: Cédric Le Goater --- hw/vfio/container.c | 31 +++++++++++++------------------ 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/hw/vfio/container.c b/hw/vfio/container.c index 134ddccc52..030c6d3f89 100644 --- a/hw/vfio/container.c +++ b/hw/vfio/container.c @@ -71,7 +71,7 @@ static int vfio_dma_unmap_bitmap(const VFIOContainer *container, hwaddr iova, ram_addr_t size, IOMMUTLBEntry *iotlb) { - const VFIOContainerBase *bcontainer = &container->bcontainer; + const VFIOContainerBase *bcontainer = VFIO_IOMMU(container); struct vfio_iommu_type1_dma_unmap *unmap; struct vfio_bitmap *bitmap; VFIOBitmap vbmap; @@ -124,8 +124,7 @@ static int vfio_legacy_dma_unmap_one(const VFIOContainerBase *bcontainer, hwaddr iova, ram_addr_t size, IOMMUTLBEntry *iotlb) { - const VFIOContainer *container = container_of(bcontainer, VFIOContainer, - bcontainer); + const VFIOContainer *container = VFIO_IOMMU_LEGACY(bcontainer); struct vfio_iommu_type1_dma_unmap unmap = { .argsz = sizeof(unmap), .flags = 0, @@ -213,8 +212,7 @@ static int vfio_legacy_dma_map(const VFIOContainerBase *bcontainer, hwaddr iova, ram_addr_t size, void *vaddr, bool readonly, MemoryRegion *mr) { - const VFIOContainer *container = container_of(bcontainer, VFIOContainer, - bcontainer); + const VFIOContainer *container = VFIO_IOMMU_LEGACY(bcontainer); struct vfio_iommu_type1_dma_map map = { .argsz = sizeof(map), .flags = VFIO_DMA_MAP_FLAG_READ, @@ -246,8 +244,7 @@ static int vfio_legacy_set_dirty_page_tracking(const VFIOContainerBase *bcontainer, bool start, Error **errp) { - const VFIOContainer *container = container_of(bcontainer, VFIOContainer, - bcontainer); + const VFIOContainer *container = VFIO_IOMMU_LEGACY(bcontainer); int ret; struct vfio_iommu_type1_dirty_bitmap dirty = { .argsz = sizeof(dirty), @@ -272,8 +269,7 @@ vfio_legacy_set_dirty_page_tracking(const VFIOContainerBase *bcontainer, static int vfio_legacy_query_dirty_bitmap(const VFIOContainerBase *bcontainer, VFIOBitmap *vbmap, hwaddr iova, hwaddr size, Error **errp) { - const VFIOContainer *container = container_of(bcontainer, VFIOContainer, - bcontainer); + const VFIOContainer *container = VFIO_IOMMU_LEGACY(bcontainer); struct vfio_iommu_type1_dirty_bitmap *dbitmap; struct vfio_iommu_type1_dirty_bitmap_get *range; int ret; @@ -495,7 +491,7 @@ static void vfio_get_iommu_info_migration(VFIOContainer *container, { struct vfio_info_cap_header *hdr; struct vfio_iommu_type1_info_cap_migration *cap_mig; - VFIOContainerBase *bcontainer = &container->bcontainer; + VFIOContainerBase *bcontainer = VFIO_IOMMU(container); hdr = vfio_get_iommu_info_cap(info, VFIO_IOMMU_TYPE1_INFO_CAP_MIGRATION); if (!hdr) { @@ -518,8 +514,7 @@ static void vfio_get_iommu_info_migration(VFIOContainer *container, static bool vfio_legacy_setup(VFIOContainerBase *bcontainer, Error **errp) { - VFIOContainer *container = container_of(bcontainer, VFIOContainer, - bcontainer); + VFIOContainer *container = VFIO_IOMMU_LEGACY(bcontainer); g_autofree struct vfio_iommu_type1_info *info = NULL; int ret; @@ -634,7 +629,7 @@ static bool vfio_container_connect(VFIOGroup *group, AddressSpace *as, if (!cpr_is_incoming()) { QLIST_FOREACH(bcontainer, &space->containers, next) { - container = container_of(bcontainer, VFIOContainer, bcontainer); + container = VFIO_IOMMU_LEGACY(bcontainer); if (!ioctl(group->fd, VFIO_GROUP_SET_CONTAINER, &container->fd)) { return vfio_container_group_add(container, group, errp); } @@ -652,7 +647,7 @@ static bool vfio_container_connect(VFIOGroup *group, AddressSpace *as, * create the container struct and group list. */ QLIST_FOREACH(bcontainer, &space->containers, next) { - container = container_of(bcontainer, VFIOContainer, bcontainer); + container = VFIO_IOMMU_LEGACY(bcontainer); if (vfio_cpr_container_match(container, group, fd)) { return vfio_container_group_add(container, group, errp); @@ -672,7 +667,7 @@ static bool vfio_container_connect(VFIOGroup *group, AddressSpace *as, goto fail; } new_container = true; - bcontainer = &container->bcontainer; + bcontainer = VFIO_IOMMU(container); if (!vfio_legacy_cpr_register_container(container, errp)) { goto fail; @@ -735,7 +730,7 @@ fail: static void vfio_container_disconnect(VFIOGroup *group) { VFIOContainer *container = group->container; - VFIOContainerBase *bcontainer = &container->bcontainer; + VFIOContainerBase *bcontainer = VFIO_IOMMU(container); VFIOIOMMUClass *vioc = VFIO_IOMMU_GET_CLASS(bcontainer); QLIST_REMOVE(group, container_next); @@ -781,7 +776,7 @@ static VFIOGroup *vfio_group_get(int groupid, AddressSpace *as, Error **errp) QLIST_FOREACH(group, &vfio_group_list, next) { if (group->groupid == groupid) { /* Found it. Now is it already in the right context? */ - if (group->container->bcontainer.space->as == as) { + if (VFIO_IOMMU(group->container)->space->as == as) { return group; } else { error_setg(errp, "group %d used in multiple address spaces", @@ -895,7 +890,7 @@ static bool vfio_device_get(VFIOGroup *group, const char *name, } } - vfio_device_prepare(vbasedev, &group->container->bcontainer, info); + vfio_device_prepare(vbasedev, VFIO_IOMMU(group->container), info); vbasedev->fd = fd; vbasedev->group = group; From 1f778031ec64cab0e79ab22f1313de59b81de3e5 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Tue, 15 Jul 2025 10:25:45 +0100 Subject: [PATCH 0340/2396] ppc/spapr_pci_vfio.c: use QOM casts where appropriate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use a QOM cast to convert to VFIOContainer instead of accessing bcontainer directly. Signed-off-by: Mark Cave-Ayland Reviewed-by: Cédric Le Goater Reviewed-by: Harsh Prateek Bora Link: https://lore.kernel.org/qemu-devel/20250715093110.107317-6-mark.caveayland@nutanix.com Signed-off-by: Cédric Le Goater --- hw/ppc/spapr_pci_vfio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/ppc/spapr_pci_vfio.c b/hw/ppc/spapr_pci_vfio.c index e318d0d912..7e1c71ef59 100644 --- a/hw/ppc/spapr_pci_vfio.c +++ b/hw/ppc/spapr_pci_vfio.c @@ -106,7 +106,7 @@ static VFIOContainer *vfio_eeh_as_container(AddressSpace *as) out: vfio_address_space_put(space); - return container_of(bcontainer, VFIOContainer, bcontainer); + return VFIO_IOMMU_LEGACY(bcontainer); } static bool vfio_eeh_as_ok(AddressSpace *as) From 1ea79b4b9a1477dca3ff53358fc9ebdd73d55938 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Tue, 15 Jul 2025 10:25:46 +0100 Subject: [PATCH 0341/2396] vfio/spapr.c: use QOM casts where appropriate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use QOM casts to convert between VFIOContainer and VFIOContainerBase instead of accessing bcontainer directly. Signed-off-by: Mark Cave-Ayland Reviewed-by: Cédric Le Goater Reviewed-by: Harsh Prateek Bora Link: https://lore.kernel.org/qemu-devel/20250715093110.107317-7-mark.caveayland@nutanix.com Signed-off-by: Cédric Le Goater --- hw/vfio/spapr.c | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/hw/vfio/spapr.c b/hw/vfio/spapr.c index 564b70ef97..c41e4588d6 100644 --- a/hw/vfio/spapr.c +++ b/hw/vfio/spapr.c @@ -62,7 +62,7 @@ static void vfio_prereg_listener_region_add(MemoryListener *listener, VFIOSpaprContainer *scontainer = container_of(listener, VFIOSpaprContainer, prereg_listener); VFIOContainer *container = &scontainer->container; - VFIOContainerBase *bcontainer = &container->bcontainer; + VFIOContainerBase *bcontainer = VFIO_IOMMU(container); const hwaddr gpa = section->offset_within_address_space; hwaddr end; int ret; @@ -244,7 +244,7 @@ static bool vfio_spapr_create_window(VFIOContainer *container, hwaddr *pgsize, Error **errp) { int ret = 0; - VFIOContainerBase *bcontainer = &container->bcontainer; + VFIOContainerBase *bcontainer = VFIO_IOMMU(container); VFIOSpaprContainer *scontainer = container_of(container, VFIOSpaprContainer, container); IOMMUMemoryRegion *iommu_mr = IOMMU_MEMORY_REGION(section->mr); @@ -352,8 +352,7 @@ vfio_spapr_container_add_section_window(VFIOContainerBase *bcontainer, MemoryRegionSection *section, Error **errp) { - VFIOContainer *container = container_of(bcontainer, VFIOContainer, - bcontainer); + VFIOContainer *container = VFIO_IOMMU_LEGACY(bcontainer); VFIOSpaprContainer *scontainer = container_of(container, VFIOSpaprContainer, container); VFIOHostDMAWindow *hostwin; @@ -443,8 +442,7 @@ static void vfio_spapr_container_del_section_window(VFIOContainerBase *bcontainer, MemoryRegionSection *section) { - VFIOContainer *container = container_of(bcontainer, VFIOContainer, - bcontainer); + VFIOContainer *container = VFIO_IOMMU_LEGACY(bcontainer); VFIOSpaprContainer *scontainer = container_of(container, VFIOSpaprContainer, container); @@ -465,8 +463,7 @@ vfio_spapr_container_del_section_window(VFIOContainerBase *bcontainer, static void vfio_spapr_container_release(VFIOContainerBase *bcontainer) { - VFIOContainer *container = container_of(bcontainer, VFIOContainer, - bcontainer); + VFIOContainer *container = VFIO_IOMMU_LEGACY(bcontainer); VFIOSpaprContainer *scontainer = container_of(container, VFIOSpaprContainer, container); VFIOHostDMAWindow *hostwin, *next; @@ -484,8 +481,7 @@ static void vfio_spapr_container_release(VFIOContainerBase *bcontainer) static bool vfio_spapr_container_setup(VFIOContainerBase *bcontainer, Error **errp) { - VFIOContainer *container = container_of(bcontainer, VFIOContainer, - bcontainer); + VFIOContainer *container = VFIO_IOMMU_LEGACY(bcontainer); VFIOSpaprContainer *scontainer = container_of(container, VFIOSpaprContainer, container); struct vfio_iommu_spapr_tce_info info; From 507a118e9f8cc328c55adc531336e075fa8ee2d7 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Tue, 15 Jul 2025 10:25:47 +0100 Subject: [PATCH 0342/2396] vfio/vfio-container.h: rename VFIOContainer bcontainer field to parent_obj MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that nothing accesses the bcontainer field directly, rename bcontainer to parent_obj as per our current coding guidelines. Signed-off-by: Mark Cave-Ayland Reviewed-by: Cédric Le Goater Reviewed-by: Philippe Mathieu-Daudé Link: https://lore.kernel.org/qemu-devel/20250715093110.107317-8-mark.caveayland@nutanix.com Signed-off-by: Cédric Le Goater --- include/hw/vfio/vfio-container.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/hw/vfio/vfio-container.h b/include/hw/vfio/vfio-container.h index 50c91788d5..240f566993 100644 --- a/include/hw/vfio/vfio-container.h +++ b/include/hw/vfio/vfio-container.h @@ -26,7 +26,7 @@ typedef struct VFIOGroup { } VFIOGroup; struct VFIOContainer { - VFIOContainerBase bcontainer; + VFIOContainerBase parent_obj; int fd; /* /dev/vfio/vfio, empowered by the attached groups */ unsigned iommu_type; From 52a1cc3dc00f09779aeb9aa9c6fcdc0284a40d4c Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Tue, 15 Jul 2025 10:25:48 +0100 Subject: [PATCH 0343/2396] vfio-user/container.h: update VFIOUserContainer declaration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update the VFIOUserContainer declaration so that it is closer to our coding guidelines: remove the explicit typedef (this is already handled by the OBJECT_DECLARE_TYPE() macro) and add a blank line after the parent object. Signed-off-by: Mark Cave-Ayland Reviewed-by: Cédric Le Goater Reviewed-by: John Levon Reviewed-by: Philippe Mathieu-Daudé Link: https://lore.kernel.org/qemu-devel/20250715093110.107317-9-mark.caveayland@nutanix.com Signed-off-by: Cédric Le Goater --- hw/vfio-user/container.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/hw/vfio-user/container.h b/hw/vfio-user/container.h index 2bb1fa1343..d5d2275af7 100644 --- a/hw/vfio-user/container.h +++ b/hw/vfio-user/container.h @@ -13,10 +13,11 @@ #include "hw/vfio-user/proxy.h" /* MMU container sub-class for vfio-user. */ -typedef struct VFIOUserContainer { +struct VFIOUserContainer { VFIOContainerBase bcontainer; + VFIOUserProxy *proxy; -} VFIOUserContainer; +}; OBJECT_DECLARE_SIMPLE_TYPE(VFIOUserContainer, VFIO_IOMMU_USER); From 06229592fa7e6173b979158e9759f0d40a183861 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Tue, 15 Jul 2025 10:25:49 +0100 Subject: [PATCH 0344/2396] vfio/container.c: use QOM casts where appropriate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use QOM casts to convert between VFIOUserContainer and VFIOContainerBase instead of accessing bcontainer directly. Signed-off-by: Mark Cave-Ayland Reviewed-by: Cédric Le Goater Reviewed-by: John Levon Link: https://lore.kernel.org/qemu-devel/20250715093110.107317-10-mark.caveayland@nutanix.com Signed-off-by: Cédric Le Goater --- hw/vfio-user/container.c | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/hw/vfio-user/container.c b/hw/vfio-user/container.c index d589dd90f5..3cdbd44c1a 100644 --- a/hw/vfio-user/container.c +++ b/hw/vfio-user/container.c @@ -24,16 +24,14 @@ */ static void vfio_user_listener_begin(VFIOContainerBase *bcontainer) { - VFIOUserContainer *container = container_of(bcontainer, VFIOUserContainer, - bcontainer); + VFIOUserContainer *container = VFIO_IOMMU_USER(bcontainer); container->proxy->async_ops = true; } static void vfio_user_listener_commit(VFIOContainerBase *bcontainer) { - VFIOUserContainer *container = container_of(bcontainer, VFIOUserContainer, - bcontainer); + VFIOUserContainer *container = VFIO_IOMMU_USER(bcontainer); /* wait here for any async requests sent during the transaction */ container->proxy->async_ops = false; @@ -44,8 +42,8 @@ static int vfio_user_dma_unmap(const VFIOContainerBase *bcontainer, hwaddr iova, ram_addr_t size, IOMMUTLBEntry *iotlb, bool unmap_all) { - VFIOUserContainer *container = container_of(bcontainer, VFIOUserContainer, - bcontainer); + VFIOUserContainer *container = VFIO_IOMMU_USER(bcontainer); + Error *local_err = NULL; int ret = 0; @@ -86,8 +84,8 @@ static int vfio_user_dma_map(const VFIOContainerBase *bcontainer, hwaddr iova, ram_addr_t size, void *vaddr, bool readonly, MemoryRegion *mrp) { - VFIOUserContainer *container = container_of(bcontainer, VFIOUserContainer, - bcontainer); + VFIOUserContainer *container = VFIO_IOMMU_USER(bcontainer); + int fd = memory_region_get_fd(mrp); Error *local_err = NULL; int ret = 0; @@ -173,8 +171,7 @@ static int vfio_user_query_dirty_bitmap(const VFIOContainerBase *bcontainer, static bool vfio_user_setup(VFIOContainerBase *bcontainer, Error **errp) { - VFIOUserContainer *container = container_of(bcontainer, VFIOUserContainer, - bcontainer); + VFIOUserContainer *container = VFIO_IOMMU_USER(bcontainer); assert(container->proxy->dma_pgsizes != 0); bcontainer->pgsizes = container->proxy->dma_pgsizes; @@ -218,7 +215,7 @@ vfio_user_container_connect(AddressSpace *as, VFIODevice *vbasedev, goto put_space_exit; } - bcontainer = &container->bcontainer; + bcontainer = VFIO_IOMMU(container); ret = ram_block_uncoordinated_discard_disable(true); if (ret) { @@ -263,7 +260,7 @@ put_space_exit: static void vfio_user_container_disconnect(VFIOUserContainer *container) { - VFIOContainerBase *bcontainer = &container->bcontainer; + VFIOContainerBase *bcontainer = VFIO_IOMMU(container); VFIOIOMMUClass *vioc = VFIO_IOMMU_GET_CLASS(bcontainer); VFIOAddressSpace *space = bcontainer->space; @@ -291,7 +288,7 @@ static bool vfio_user_device_get(VFIOUserContainer *container, vbasedev->fd = -1; - vfio_device_prepare(vbasedev, &container->bcontainer, &info); + vfio_device_prepare(vbasedev, VFIO_IOMMU(container), &info); return true; } @@ -315,8 +312,7 @@ static bool vfio_user_device_attach(const char *name, VFIODevice *vbasedev, static void vfio_user_device_detach(VFIODevice *vbasedev) { - VFIOUserContainer *container = container_of(vbasedev->bcontainer, - VFIOUserContainer, bcontainer); + VFIOUserContainer *container = VFIO_IOMMU_USER(vbasedev->bcontainer); vfio_device_unprepare(vbasedev); From 81b53891ca52288540e6e4f34bb924284feebc58 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Tue, 15 Jul 2025 10:25:50 +0100 Subject: [PATCH 0345/2396] vfio-user/container.h: rename VFIOUserContainer bcontainer field to parent_obj MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that nothing accesses the bcontainer field directly, rename bcontainer to parent_obj as per our current coding guidelines. Signed-off-by: Mark Cave-Ayland Reviewed-by: Cédric Le Goater Reviewed-by: John Levon Reviewed-by: Philippe Mathieu-Daudé Link: https://lore.kernel.org/qemu-devel/20250715093110.107317-11-mark.caveayland@nutanix.com Signed-off-by: Cédric Le Goater --- hw/vfio-user/container.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/vfio-user/container.h b/hw/vfio-user/container.h index d5d2275af7..96aa6785d9 100644 --- a/hw/vfio-user/container.h +++ b/hw/vfio-user/container.h @@ -14,7 +14,7 @@ /* MMU container sub-class for vfio-user. */ struct VFIOUserContainer { - VFIOContainerBase bcontainer; + VFIOContainerBase parent_obj; VFIOUserProxy *proxy; }; From b458e9e9e4c171622f19df18f5363c7ef4e8697f Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Tue, 15 Jul 2025 10:25:51 +0100 Subject: [PATCH 0346/2396] vfio-user/pci.c: update VFIOUserPCIDevice declaration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update the VFIOUserPCIDevice declaration so that it is closer to our coding guidelines: add a blank line after the parent object. Signed-off-by: Mark Cave-Ayland Reviewed-by: Cédric Le Goater Reviewed-by: John Levon Reviewed-by: Philippe Mathieu-Daudé Link: https://lore.kernel.org/qemu-devel/20250715093110.107317-12-mark.caveayland@nutanix.com Signed-off-by: Cédric Le Goater --- hw/vfio-user/pci.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/vfio-user/pci.c b/hw/vfio-user/pci.c index dfaa89498d..29cb592e9c 100644 --- a/hw/vfio-user/pci.c +++ b/hw/vfio-user/pci.c @@ -21,6 +21,7 @@ OBJECT_DECLARE_SIMPLE_TYPE(VFIOUserPCIDevice, VFIO_USER_PCI) struct VFIOUserPCIDevice { VFIOPCIDevice device; + SocketAddress *socket; bool send_queued; /* all sends are queued */ uint32_t wait_time; /* timeout for message replies */ From 5d1219e358a559680fdc34b112e2b04806f5ff62 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Tue, 15 Jul 2025 10:25:52 +0100 Subject: [PATCH 0347/2396] vfio-user/pci.c: use QOM casts where appropriate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use QOM casts to convert between VFIOUserPCIDevice and VFIOPCIDevice instead of accessing device directly. Signed-off-by: Mark Cave-Ayland Reviewed-by: John Levon Link: https://lore.kernel.org/qemu-devel/20250715093110.107317-13-mark.caveayland@nutanix.com Signed-off-by: Cédric Le Goater --- hw/vfio-user/pci.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/hw/vfio-user/pci.c b/hw/vfio-user/pci.c index 29cb592e9c..7b6a6514f6 100644 --- a/hw/vfio-user/pci.c +++ b/hw/vfio-user/pci.c @@ -214,8 +214,9 @@ static void vfio_user_compute_needs_reset(VFIODevice *vbasedev) static Object *vfio_user_pci_get_object(VFIODevice *vbasedev) { - VFIOUserPCIDevice *vdev = container_of(vbasedev, VFIOUserPCIDevice, - device.vbasedev); + VFIOUserPCIDevice *vdev = VFIO_USER_PCI(container_of(vbasedev, + VFIOPCIDevice, + vbasedev)); return OBJECT(vdev); } @@ -420,7 +421,7 @@ static void vfio_user_pci_set_socket(Object *obj, Visitor *v, const char *name, VFIOUserPCIDevice *udev = VFIO_USER_PCI(obj); bool success; - if (udev->device.vbasedev.proxy) { + if (VFIO_PCI_BASE(udev)->vbasedev.proxy) { error_setg(errp, "Proxy is connected"); return; } From 5fc421b8cd4813fa8ca9131905f12c4eedf55051 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Tue, 15 Jul 2025 10:25:53 +0100 Subject: [PATCH 0348/2396] vfio-user/pci.c: rename VFIOUserPCIDevice device field to parent_obj MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that nothing accesses the device field directly, rename device to parent_obj as per our current coding guidelines. Signed-off-by: Mark Cave-Ayland Reviewed-by: Cédric Le Goater Reviewed-by: John Levon Reviewed-by: Philippe Mathieu-Daudé Link: https://lore.kernel.org/qemu-devel/20250715093110.107317-14-mark.caveayland@nutanix.com Signed-off-by: Cédric Le Goater --- hw/vfio-user/pci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/vfio-user/pci.c b/hw/vfio-user/pci.c index 7b6a6514f6..c3947a8f2e 100644 --- a/hw/vfio-user/pci.c +++ b/hw/vfio-user/pci.c @@ -20,7 +20,7 @@ OBJECT_DECLARE_SIMPLE_TYPE(VFIOUserPCIDevice, VFIO_USER_PCI) struct VFIOUserPCIDevice { - VFIOPCIDevice device; + VFIOPCIDevice parent_obj; SocketAddress *socket; bool send_queued; /* all sends are queued */ From 750e424fd04311aaeeb85536744c4cd3e460404d Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Tue, 15 Jul 2025 10:25:54 +0100 Subject: [PATCH 0349/2396] vfio/pci.h: update VFIOPCIDevice declaration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update the VFIOPCIDevice declaration so that it is closer to our coding guidelines: add a blank line after the parent object. Signed-off-by: Mark Cave-Ayland Reviewed-by: Cédric Le Goater Reviewed-by: Philippe Mathieu-Daudé Link: https://lore.kernel.org/qemu-devel/20250715093110.107317-15-mark.caveayland@nutanix.com Signed-off-by: Cédric Le Goater --- hw/vfio/pci.h | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/vfio/pci.h b/hw/vfio/pci.h index 96144b6fde..2db76b6f4f 100644 --- a/hw/vfio/pci.h +++ b/hw/vfio/pci.h @@ -124,6 +124,7 @@ OBJECT_DECLARE_SIMPLE_TYPE(VFIOPCIDevice, VFIO_PCI_BASE) struct VFIOPCIDevice { PCIDevice pdev; + VFIODevice vbasedev; VFIOINTx intx; unsigned int config_size; From 77f143cc418121fb30ad9e26dd90334dcf5851fc Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Tue, 15 Jul 2025 10:25:56 +0100 Subject: [PATCH 0350/2396] vfio/pci.c: use QOM casts where appropriate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use QOM casts to convert between VFIOPCIDevice and PCIDevice instead of accessing pdev directly. Signed-off-by: Mark Cave-Ayland Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250715093110.107317-17-mark.caveayland@nutanix.com [ clg: Updated vfio_sub_page_bar_update_mappings() ] Signed-off-by: Cédric Le Goater --- hw/vfio/pci.c | 204 ++++++++++++++++++++++++++++++-------------------- 1 file changed, 121 insertions(+), 83 deletions(-) diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index 3fe5b03eb1..052591d2c9 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -117,6 +117,7 @@ static void vfio_intx_mmap_enable(void *opaque) static void vfio_intx_interrupt(void *opaque) { VFIOPCIDevice *vdev = opaque; + PCIDevice *pdev = PCI_DEVICE(vdev); if (!event_notifier_test_and_clear(&vdev->intx.interrupt)) { return; @@ -125,7 +126,7 @@ static void vfio_intx_interrupt(void *opaque) trace_vfio_intx_interrupt(vdev->vbasedev.name, 'A' + vdev->intx.pin); vdev->intx.pending = true; - pci_irq_assert(&vdev->pdev); + pci_irq_assert(pdev); vfio_mmap_set_enabled(vdev, false); if (vdev->intx.mmap_timeout) { timer_mod(vdev->intx.mmap_timer, @@ -136,6 +137,7 @@ static void vfio_intx_interrupt(void *opaque) void vfio_pci_intx_eoi(VFIODevice *vbasedev) { VFIOPCIDevice *vdev = container_of(vbasedev, VFIOPCIDevice, vbasedev); + PCIDevice *pdev = PCI_DEVICE(vdev); if (!vdev->intx.pending) { return; @@ -144,13 +146,14 @@ void vfio_pci_intx_eoi(VFIODevice *vbasedev) trace_vfio_pci_intx_eoi(vbasedev->name); vdev->intx.pending = false; - pci_irq_deassert(&vdev->pdev); + pci_irq_deassert(pdev); vfio_device_irq_unmask(vbasedev, VFIO_PCI_INTX_IRQ_INDEX); } static bool vfio_intx_enable_kvm(VFIOPCIDevice *vdev, Error **errp) { #ifdef CONFIG_KVM + PCIDevice *pdev = PCI_DEVICE(vdev); int irq_fd = event_notifier_get_fd(&vdev->intx.interrupt); if (vdev->no_kvm_intx || !kvm_irqfds_enabled() || @@ -163,7 +166,7 @@ static bool vfio_intx_enable_kvm(VFIOPCIDevice *vdev, Error **errp) qemu_set_fd_handler(irq_fd, NULL, NULL, vdev); vfio_device_irq_mask(&vdev->vbasedev, VFIO_PCI_INTX_IRQ_INDEX); vdev->intx.pending = false; - pci_irq_deassert(&vdev->pdev); + pci_irq_deassert(pdev); /* Get an eventfd for resample/unmask */ if (!vfio_notifier_init(vdev, &vdev->intx.unmask, "intx-unmask", 0, errp)) { @@ -241,6 +244,8 @@ static bool vfio_cpr_intx_enable_kvm(VFIOPCIDevice *vdev, Error **errp) static void vfio_intx_disable_kvm(VFIOPCIDevice *vdev) { #ifdef CONFIG_KVM + PCIDevice *pdev = PCI_DEVICE(vdev); + if (!vdev->intx.kvm_accel) { return; } @@ -251,7 +256,7 @@ static void vfio_intx_disable_kvm(VFIOPCIDevice *vdev) */ vfio_device_irq_mask(&vdev->vbasedev, VFIO_PCI_INTX_IRQ_INDEX); vdev->intx.pending = false; - pci_irq_deassert(&vdev->pdev); + pci_irq_deassert(pdev); /* Tell KVM to stop listening for an INTx irqfd */ if (kvm_irqchip_remove_irqfd_notifier_gsi(kvm_state, &vdev->intx.interrupt, @@ -307,7 +312,7 @@ static void vfio_intx_routing_notifier(PCIDevice *pdev) return; } - route = pci_device_route_intx_to_irq(&vdev->pdev, vdev->intx.pin); + route = pci_device_route_intx_to_irq(pdev, vdev->intx.pin); if (pci_intx_route_changed(&vdev->intx.route, &route)) { vfio_intx_update(vdev, &route); @@ -324,7 +329,8 @@ static void vfio_irqchip_change(Notifier *notify, void *data) static bool vfio_intx_enable(VFIOPCIDevice *vdev, Error **errp) { - uint8_t pin = vfio_pci_read_config(&vdev->pdev, PCI_INTERRUPT_PIN, 1); + PCIDevice *pdev = PCI_DEVICE(vdev); + uint8_t pin = vfio_pci_read_config(pdev, PCI_INTERRUPT_PIN, 1); Error *err = NULL; int32_t fd; @@ -342,7 +348,7 @@ static bool vfio_intx_enable(VFIOPCIDevice *vdev, Error **errp) } vdev->intx.pin = pin - 1; /* Pin A (1) -> irq[0] */ - pci_config_set_interrupt_pin(vdev->pdev.config, pin); + pci_config_set_interrupt_pin(pdev->config, pin); #ifdef CONFIG_KVM /* @@ -350,7 +356,7 @@ static bool vfio_intx_enable(VFIOPCIDevice *vdev, Error **errp) * where we won't actually use the result anyway. */ if (kvm_irqfds_enabled() && kvm_resamplefds_enabled()) { - vdev->intx.route = pci_device_route_intx_to_irq(&vdev->pdev, + vdev->intx.route = pci_device_route_intx_to_irq(pdev, vdev->intx.pin); } #endif @@ -390,13 +396,14 @@ skip_signaling: static void vfio_intx_disable(VFIOPCIDevice *vdev) { + PCIDevice *pdev = PCI_DEVICE(vdev); int fd; timer_del(vdev->intx.mmap_timer); vfio_intx_disable_kvm(vdev); vfio_device_irq_disable(&vdev->vbasedev, VFIO_PCI_INTX_IRQ_INDEX); vdev->intx.pending = false; - pci_irq_deassert(&vdev->pdev); + pci_irq_deassert(pdev); vfio_mmap_set_enabled(vdev, true); fd = event_notifier_get_fd(&vdev->intx.interrupt); @@ -428,6 +435,7 @@ static void vfio_msi_interrupt(void *opaque) { VFIOMSIVector *vector = opaque; VFIOPCIDevice *vdev = vector->vdev; + PCIDevice *pdev = PCI_DEVICE(vdev); MSIMessage (*get_msg)(PCIDevice *dev, unsigned vector); void (*notify)(PCIDevice *dev, unsigned vector); MSIMessage msg; @@ -442,9 +450,9 @@ static void vfio_msi_interrupt(void *opaque) notify = msix_notify; /* A masked vector firing needs to use the PBA, enable it */ - if (msix_is_masked(&vdev->pdev, nr)) { + if (msix_is_masked(pdev, nr)) { set_bit(nr, vdev->msix->pending); - memory_region_set_enabled(&vdev->pdev.msix_pba_mmio, true); + memory_region_set_enabled(&pdev->msix_pba_mmio, true); trace_vfio_msix_pba_enable(vdev->vbasedev.name); } } else if (vdev->interrupt == VFIO_INT_MSI) { @@ -454,9 +462,9 @@ static void vfio_msi_interrupt(void *opaque) abort(); } - msg = get_msg(&vdev->pdev, nr); + msg = get_msg(pdev, nr); trace_vfio_msi_interrupt(vdev->vbasedev.name, nr, msg.address, msg.data); - notify(&vdev->pdev, nr); + notify(pdev, nr); } void vfio_pci_msi_set_handler(VFIOPCIDevice *vdev, int nr, bool enable) @@ -495,6 +503,7 @@ static int vfio_enable_msix_no_vec(VFIOPCIDevice *vdev) static int vfio_enable_vectors(VFIOPCIDevice *vdev, bool msix) { + PCIDevice *pdev = PCI_DEVICE(vdev); struct vfio_irq_set *irq_set; int ret = 0, i, argsz; int32_t *fds; @@ -537,7 +546,7 @@ static int vfio_enable_vectors(VFIOPCIDevice *vdev, bool msix) */ if (vdev->msi_vectors[i].use) { if (vdev->msi_vectors[i].virq < 0 || - (msix && msix_is_masked(&vdev->pdev, i))) { + (msix && msix_is_masked(pdev, i))) { fd = event_notifier_get_fd(&vdev->msi_vectors[i].interrupt); } else { fd = event_notifier_get_fd(&vdev->msi_vectors[i].kvm_interrupt); @@ -557,12 +566,14 @@ static int vfio_enable_vectors(VFIOPCIDevice *vdev, bool msix) void vfio_pci_add_kvm_msi_virq(VFIOPCIDevice *vdev, VFIOMSIVector *vector, int vector_n, bool msix) { + PCIDevice *pdev = PCI_DEVICE(vdev); + if ((msix && vdev->no_kvm_msix) || (!msix && vdev->no_kvm_msi)) { return; } vector->virq = kvm_irqchip_add_msi_route(&vfio_route_change, - vector_n, &vdev->pdev); + vector_n, pdev); } static void vfio_connect_kvm_msi_virq(VFIOMSIVector *vector, int nr) @@ -631,7 +642,7 @@ static void set_irq_signalling(VFIODevice *vbasedev, VFIOMSIVector *vector, void vfio_pci_vector_init(VFIOPCIDevice *vdev, int nr) { VFIOMSIVector *vector = &vdev->msi_vectors[nr]; - PCIDevice *pdev = &vdev->pdev; + PCIDevice *pdev = PCI_DEVICE(vdev); Error *local_err = NULL; vector->vdev = vdev; @@ -720,7 +731,7 @@ static int vfio_msix_vector_do_use(PCIDevice *pdev, unsigned int nr, clear_bit(nr, vdev->msix->pending); if (find_first_bit(vdev->msix->pending, vdev->nr_vectors) == vdev->nr_vectors) { - memory_region_set_enabled(&vdev->pdev.msix_pba_mmio, false); + memory_region_set_enabled(&pdev->msix_pba_mmio, false); trace_vfio_msix_pba_disable(vdev->vbasedev.name); } @@ -771,7 +782,9 @@ static void vfio_msix_vector_release(PCIDevice *pdev, unsigned int nr) void vfio_pci_msix_set_notifiers(VFIOPCIDevice *vdev) { - msix_set_vector_notifiers(&vdev->pdev, vfio_msix_vector_use, + PCIDevice *pdev = PCI_DEVICE(vdev); + + msix_set_vector_notifiers(pdev, vfio_msix_vector_use, vfio_msix_vector_release, NULL); } @@ -798,6 +811,7 @@ void vfio_pci_commit_kvm_msi_virq_batch(VFIOPCIDevice *vdev) static void vfio_msix_enable(VFIOPCIDevice *vdev) { + PCIDevice *pdev = PCI_DEVICE(vdev); int ret; vfio_disable_interrupts(vdev); @@ -814,7 +828,7 @@ static void vfio_msix_enable(VFIOPCIDevice *vdev) */ vfio_pci_prepare_kvm_msi_virq_batch(vdev); - if (msix_set_vector_notifiers(&vdev->pdev, vfio_msix_vector_use, + if (msix_set_vector_notifiers(pdev, vfio_msix_vector_use, vfio_msix_vector_release, NULL)) { error_report("vfio: msix_set_vector_notifiers failed"); } @@ -852,11 +866,12 @@ static void vfio_msix_enable(VFIOPCIDevice *vdev) static void vfio_msi_enable(VFIOPCIDevice *vdev) { + PCIDevice *pdev = PCI_DEVICE(vdev); int ret, i; vfio_disable_interrupts(vdev); - vdev->nr_vectors = msi_nr_vectors_allocated(&vdev->pdev); + vdev->nr_vectors = msi_nr_vectors_allocated(pdev); retry: /* * Setting vector notifiers needs to enable route for each vector. @@ -949,10 +964,11 @@ static void vfio_msi_disable_common(VFIOPCIDevice *vdev) static void vfio_msix_disable(VFIOPCIDevice *vdev) { + PCIDevice *pdev = PCI_DEVICE(vdev); Error *err = NULL; int i; - msix_unset_vector_notifiers(&vdev->pdev); + msix_unset_vector_notifiers(pdev); /* * MSI-X will only release vectors if MSI-X is still enabled on the @@ -960,8 +976,8 @@ static void vfio_msix_disable(VFIOPCIDevice *vdev) */ for (i = 0; i < vdev->nr_vectors; i++) { if (vdev->msi_vectors[i].use) { - vfio_msix_vector_release(&vdev->pdev, i); - msix_vector_unuse(&vdev->pdev, i); + vfio_msix_vector_release(pdev, i); + msix_vector_unuse(pdev, i); } } @@ -998,6 +1014,7 @@ static void vfio_msi_disable(VFIOPCIDevice *vdev) static void vfio_update_msi(VFIOPCIDevice *vdev) { + PCIDevice *pdev = PCI_DEVICE(vdev); int i; for (i = 0; i < vdev->nr_vectors; i++) { @@ -1008,8 +1025,8 @@ static void vfio_update_msi(VFIOPCIDevice *vdev) continue; } - msg = msi_get_message(&vdev->pdev, i); - vfio_update_kvm_msi_virq(vector, msg, &vdev->pdev); + msg = msi_get_message(pdev, i); + vfio_update_kvm_msi_virq(vector, msg, pdev); } } @@ -1171,13 +1188,14 @@ static const MemoryRegionOps vfio_rom_ops = { static void vfio_pci_size_rom(VFIOPCIDevice *vdev) { + PCIDevice *pdev = PCI_DEVICE(vdev); VFIODevice *vbasedev = &vdev->vbasedev; uint32_t orig, size = cpu_to_le32((uint32_t)PCI_ROM_ADDRESS_MASK); char *name; - if (vdev->pdev.romfile || !vdev->pdev.rom_bar) { + if (pdev->romfile || !pdev->rom_bar) { /* Since pci handles romfile, just print a message and return */ - if (vfio_opt_rom_in_denylist(vdev) && vdev->pdev.romfile) { + if (vfio_opt_rom_in_denylist(vdev) && pdev->romfile) { warn_report("Device at %s is known to cause system instability" " issues during option rom execution", vdev->vbasedev.name); @@ -1206,7 +1224,7 @@ static void vfio_pci_size_rom(VFIOPCIDevice *vdev) } if (vfio_opt_rom_in_denylist(vdev)) { - if (vdev->pdev.rom_bar > 0) { + if (pdev->rom_bar > 0) { warn_report("Device at %s is known to cause system instability" " issues during option rom execution", vdev->vbasedev.name); @@ -1225,12 +1243,12 @@ static void vfio_pci_size_rom(VFIOPCIDevice *vdev) name = g_strdup_printf("vfio[%s].rom", vdev->vbasedev.name); - memory_region_init_io(&vdev->pdev.rom, OBJECT(vdev), + memory_region_init_io(&pdev->rom, OBJECT(vdev), &vfio_rom_ops, vdev, name, size); g_free(name); - pci_register_bar(&vdev->pdev, PCI_ROM_SLOT, - PCI_BASE_ADDRESS_SPACE_MEMORY, &vdev->pdev.rom); + pci_register_bar(pdev, PCI_ROM_SLOT, + PCI_BASE_ADDRESS_SPACE_MEMORY, &pdev->rom); vdev->rom_read_failed = false; } @@ -1503,6 +1521,7 @@ static void vfio_disable_interrupts(VFIOPCIDevice *vdev) static bool vfio_msi_setup(VFIOPCIDevice *vdev, int pos, Error **errp) { + PCIDevice *pdev = PCI_DEVICE(vdev); uint16_t ctrl; bool msi_64bit, msi_maskbit; int ret, entries; @@ -1523,7 +1542,7 @@ static bool vfio_msi_setup(VFIOPCIDevice *vdev, int pos, Error **errp) trace_vfio_msi_setup(vdev->vbasedev.name, pos); - ret = msi_init(&vdev->pdev, pos, entries, msi_64bit, msi_maskbit, &err); + ret = msi_init(pdev, pos, entries, msi_64bit, msi_maskbit, &err); if (ret < 0) { if (ret == -ENOTSUP) { return true; @@ -1716,6 +1735,7 @@ static bool vfio_pci_relocate_msix(VFIOPCIDevice *vdev, Error **errp) */ static bool vfio_msix_early_setup(VFIOPCIDevice *vdev, Error **errp) { + PCIDevice *pdev = PCI_DEVICE(vdev); uint8_t pos; uint16_t ctrl; uint32_t table, pba; @@ -1723,7 +1743,7 @@ static bool vfio_msix_early_setup(VFIOPCIDevice *vdev, Error **errp) VFIOMSIXInfo *msix; int ret; - pos = pci_find_capability(&vdev->pdev, PCI_CAP_ID_MSIX); + pos = pci_find_capability(pdev, PCI_CAP_ID_MSIX); if (!pos) { return true; } @@ -1815,12 +1835,13 @@ static bool vfio_msix_early_setup(VFIOPCIDevice *vdev, Error **errp) static bool vfio_msix_setup(VFIOPCIDevice *vdev, int pos, Error **errp) { + PCIDevice *pdev = PCI_DEVICE(vdev); int ret; Error *err = NULL; vdev->msix->pending = g_new0(unsigned long, BITS_TO_LONGS(vdev->msix->entries)); - ret = msix_init(&vdev->pdev, vdev->msix->entries, + ret = msix_init(pdev, vdev->msix->entries, vdev->bars[vdev->msix->table_bar].mr, vdev->msix->table_bar, vdev->msix->table_offset, vdev->bars[vdev->msix->pba_bar].mr, @@ -1852,7 +1873,7 @@ static bool vfio_msix_setup(VFIOPCIDevice *vdev, int pos, Error **errp) * vector-use notifier is called, which occurs on unmask, we test whether * PBA emulation is needed and again disable if not. */ - memory_region_set_enabled(&vdev->pdev.msix_pba_mmio, false); + memory_region_set_enabled(&pdev->msix_pba_mmio, false); /* * The emulated machine may provide a paravirt interface for MSIX setup @@ -1864,7 +1885,7 @@ static bool vfio_msix_setup(VFIOPCIDevice *vdev, int pos, Error **errp) */ if (object_property_get_bool(OBJECT(qdev_get_machine()), "vfio-no-msix-emulation", NULL)) { - memory_region_set_enabled(&vdev->pdev.msix_table_mmio, false); + memory_region_set_enabled(&pdev->msix_table_mmio, false); } return true; @@ -1872,10 +1893,12 @@ static bool vfio_msix_setup(VFIOPCIDevice *vdev, int pos, Error **errp) void vfio_pci_teardown_msi(VFIOPCIDevice *vdev) { - msi_uninit(&vdev->pdev); + PCIDevice *pdev = PCI_DEVICE(vdev); + + msi_uninit(pdev); if (vdev->msix) { - msix_uninit(&vdev->pdev, + msix_uninit(pdev, vdev->bars[vdev->msix->table_bar].mr, vdev->bars[vdev->msix->pba_bar].mr); g_free(vdev->msix->pending); @@ -1936,6 +1959,7 @@ static void vfio_bars_prepare(VFIOPCIDevice *vdev) static void vfio_bar_register(VFIOPCIDevice *vdev, int nr) { + PCIDevice *pdev = PCI_DEVICE(vdev); VFIOBAR *bar = &vdev->bars[nr]; char *name; @@ -1957,7 +1981,7 @@ static void vfio_bar_register(VFIOPCIDevice *vdev, int nr) } } - pci_register_bar(&vdev->pdev, nr, bar->type, bar->mr); + pci_register_bar(pdev, nr, bar->type, bar->mr); } static void vfio_bars_register(VFIOPCIDevice *vdev) @@ -1971,6 +1995,7 @@ static void vfio_bars_register(VFIOPCIDevice *vdev) void vfio_pci_bars_exit(VFIOPCIDevice *vdev) { + PCIDevice *pdev = PCI_DEVICE(vdev); int i; for (i = 0; i < PCI_ROM_SLOT; i++) { @@ -1984,7 +2009,7 @@ void vfio_pci_bars_exit(VFIOPCIDevice *vdev) } if (vdev->vga) { - pci_unregister_vga(&vdev->pdev); + pci_unregister_vga(pdev); vfio_vga_quirk_exit(vdev); } } @@ -2056,8 +2081,10 @@ static void vfio_set_word_bits(uint8_t *buf, uint16_t val, uint16_t mask) static void vfio_add_emulated_word(VFIOPCIDevice *vdev, int pos, uint16_t val, uint16_t mask) { - vfio_set_word_bits(vdev->pdev.config + pos, val, mask); - vfio_set_word_bits(vdev->pdev.wmask + pos, ~mask, mask); + PCIDevice *pdev = PCI_DEVICE(vdev); + + vfio_set_word_bits(pdev->config + pos, val, mask); + vfio_set_word_bits(pdev->wmask + pos, ~mask, mask); vfio_set_word_bits(vdev->emulated_config_bits + pos, mask, mask); } @@ -2069,8 +2096,10 @@ static void vfio_set_long_bits(uint8_t *buf, uint32_t val, uint32_t mask) static void vfio_add_emulated_long(VFIOPCIDevice *vdev, int pos, uint32_t val, uint32_t mask) { - vfio_set_long_bits(vdev->pdev.config + pos, val, mask); - vfio_set_long_bits(vdev->pdev.wmask + pos, ~mask, mask); + PCIDevice *pdev = PCI_DEVICE(vdev); + + vfio_set_long_bits(pdev->config + pos, val, mask); + vfio_set_long_bits(pdev->wmask + pos, ~mask, mask); vfio_set_long_bits(vdev->emulated_config_bits + pos, mask, mask); } @@ -2078,7 +2107,8 @@ static void vfio_pci_enable_rp_atomics(VFIOPCIDevice *vdev) { struct vfio_device_info_cap_pci_atomic_comp *cap; g_autofree struct vfio_device_info *info = NULL; - PCIBus *bus = pci_get_bus(&vdev->pdev); + PCIDevice *pdev = PCI_DEVICE(vdev); + PCIBus *bus = pci_get_bus(pdev); PCIDevice *parent = bus->parent_dev; struct vfio_info_cap_header *hdr; uint32_t mask = 0; @@ -2094,8 +2124,8 @@ static void vfio_pci_enable_rp_atomics(VFIOPCIDevice *vdev) if (pci_bus_is_root(bus) || !parent || !parent->exp.exp_cap || pcie_cap_get_type(parent) != PCI_EXP_TYPE_ROOT_PORT || pcie_cap_get_version(parent) != PCI_EXP_FLAGS_VER2 || - vdev->pdev.devfn || - vdev->pdev.cap_present & QEMU_PCI_CAP_MULTIFUNCTION) { + pdev->devfn || + pdev->cap_present & QEMU_PCI_CAP_MULTIFUNCTION) { return; } @@ -2139,8 +2169,10 @@ static void vfio_pci_enable_rp_atomics(VFIOPCIDevice *vdev) static void vfio_pci_disable_rp_atomics(VFIOPCIDevice *vdev) { + PCIDevice *pdev = PCI_DEVICE(vdev); + if (vdev->clear_parent_atomics_on_exit) { - PCIDevice *parent = pci_get_bus(&vdev->pdev)->parent_dev; + PCIDevice *parent = pci_get_bus(pdev)->parent_dev; uint8_t *pos = parent->config + parent->exp.exp_cap + PCI_EXP_DEVCAP2; pci_long_test_and_clear_mask(pos, PCI_EXP_DEVCAP2_ATOMIC_COMP32 | @@ -2152,10 +2184,11 @@ static void vfio_pci_disable_rp_atomics(VFIOPCIDevice *vdev) static bool vfio_setup_pcie_cap(VFIOPCIDevice *vdev, int pos, uint8_t size, Error **errp) { + PCIDevice *pdev = PCI_DEVICE(vdev); uint16_t flags; uint8_t type; - flags = pci_get_word(vdev->pdev.config + pos + PCI_CAP_FLAGS); + flags = pci_get_word(pdev->config + pos + PCI_CAP_FLAGS); type = (flags & PCI_EXP_FLAGS_TYPE) >> 4; if (type != PCI_EXP_TYPE_ENDPOINT && @@ -2167,8 +2200,8 @@ static bool vfio_setup_pcie_cap(VFIOPCIDevice *vdev, int pos, uint8_t size, return false; } - if (!pci_bus_is_express(pci_get_bus(&vdev->pdev))) { - PCIBus *bus = pci_get_bus(&vdev->pdev); + if (!pci_bus_is_express(pci_get_bus(pdev))) { + PCIBus *bus = pci_get_bus(pdev); PCIDevice *bridge; /* @@ -2200,7 +2233,7 @@ static bool vfio_setup_pcie_cap(VFIOPCIDevice *vdev, int pos, uint8_t size, return true; } - } else if (pci_bus_is_root(pci_get_bus(&vdev->pdev))) { + } else if (pci_bus_is_root(pci_get_bus(pdev))) { /* * On a Root Complex bus Endpoints become Root Complex Integrated * Endpoints, which changes the type and clears the LNK & LNK2 fields. @@ -2268,20 +2301,20 @@ static bool vfio_setup_pcie_cap(VFIOPCIDevice *vdev, int pos, uint8_t size, 1, PCI_EXP_FLAGS_VERS); } - pos = pci_add_capability(&vdev->pdev, PCI_CAP_ID_EXP, pos, size, - errp); + pos = pci_add_capability(pdev, PCI_CAP_ID_EXP, pos, size, errp); if (pos < 0) { return false; } - vdev->pdev.exp.exp_cap = pos; + pdev->exp.exp_cap = pos; return true; } static void vfio_check_pcie_flr(VFIOPCIDevice *vdev, uint8_t pos) { - uint32_t cap = pci_get_long(vdev->pdev.config + pos + PCI_EXP_DEVCAP); + PCIDevice *pdev = PCI_DEVICE(vdev); + uint32_t cap = pci_get_long(pdev->config + pos + PCI_EXP_DEVCAP); if (cap & PCI_EXP_DEVCAP_FLR) { trace_vfio_check_pcie_flr(vdev->vbasedev.name); @@ -2291,7 +2324,8 @@ static void vfio_check_pcie_flr(VFIOPCIDevice *vdev, uint8_t pos) static void vfio_check_pm_reset(VFIOPCIDevice *vdev, uint8_t pos) { - uint16_t csr = pci_get_word(vdev->pdev.config + pos + PCI_PM_CTRL); + PCIDevice *pdev = PCI_DEVICE(vdev); + uint16_t csr = pci_get_word(pdev->config + pos + PCI_PM_CTRL); if (!(csr & PCI_PM_CTRL_NO_SOFT_RESET)) { trace_vfio_check_pm_reset(vdev->vbasedev.name); @@ -2301,7 +2335,8 @@ static void vfio_check_pm_reset(VFIOPCIDevice *vdev, uint8_t pos) static void vfio_check_af_flr(VFIOPCIDevice *vdev, uint8_t pos) { - uint8_t cap = pci_get_byte(vdev->pdev.config + pos + PCI_AF_CAP); + PCIDevice *pdev = PCI_DEVICE(vdev); + uint8_t cap = pci_get_byte(pdev->config + pos + PCI_AF_CAP); if ((cap & PCI_AF_CAP_TP) && (cap & PCI_AF_CAP_FLR)) { trace_vfio_check_af_flr(vdev->vbasedev.name); @@ -2312,7 +2347,7 @@ static void vfio_check_af_flr(VFIOPCIDevice *vdev, uint8_t pos) static bool vfio_add_vendor_specific_cap(VFIOPCIDevice *vdev, int pos, uint8_t size, Error **errp) { - PCIDevice *pdev = &vdev->pdev; + PCIDevice *pdev = PCI_DEVICE(vdev); pos = pci_add_capability(pdev, PCI_CAP_ID_VNDR, pos, size, errp); if (pos < 0) { @@ -2334,7 +2369,7 @@ static bool vfio_add_vendor_specific_cap(VFIOPCIDevice *vdev, int pos, static bool vfio_add_std_cap(VFIOPCIDevice *vdev, uint8_t pos, Error **errp) { ERRP_GUARD(); - PCIDevice *pdev = &vdev->pdev; + PCIDevice *pdev = PCI_DEVICE(vdev); uint8_t cap_id, next, size; bool ret; @@ -2420,17 +2455,18 @@ static bool vfio_add_std_cap(VFIOPCIDevice *vdev, uint8_t pos, Error **errp) static int vfio_setup_rebar_ecap(VFIOPCIDevice *vdev, uint16_t pos) { + PCIDevice *pdev = PCI_DEVICE(vdev); uint32_t ctrl; int i, nbar; - ctrl = pci_get_long(vdev->pdev.config + pos + PCI_REBAR_CTRL); + ctrl = pci_get_long(pdev->config + pos + PCI_REBAR_CTRL); nbar = (ctrl & PCI_REBAR_CTRL_NBAR_MASK) >> PCI_REBAR_CTRL_NBAR_SHIFT; for (i = 0; i < nbar; i++) { uint32_t cap; int size; - ctrl = pci_get_long(vdev->pdev.config + pos + PCI_REBAR_CTRL + (i * 8)); + ctrl = pci_get_long(pdev->config + pos + PCI_REBAR_CTRL + (i * 8)); size = (ctrl & PCI_REBAR_CTRL_BAR_SIZE) >> PCI_REBAR_CTRL_BAR_SHIFT; /* The cap register reports sizes 1MB to 128TB, with 4 reserved bits */ @@ -2468,7 +2504,7 @@ static int vfio_setup_rebar_ecap(VFIOPCIDevice *vdev, uint16_t pos) static void vfio_add_ext_cap(VFIOPCIDevice *vdev) { - PCIDevice *pdev = &vdev->pdev; + PCIDevice *pdev = PCI_DEVICE(vdev); uint32_t header; uint16_t cap_id, next, size; uint8_t cap_ver; @@ -2562,7 +2598,7 @@ static void vfio_add_ext_cap(VFIOPCIDevice *vdev) bool vfio_pci_add_capabilities(VFIOPCIDevice *vdev, Error **errp) { - PCIDevice *pdev = &vdev->pdev; + PCIDevice *pdev = PCI_DEVICE(vdev); if (!(pdev->config[PCI_STATUS] & PCI_STATUS_CAP_LIST) || !pdev->config[PCI_CAPABILITY_LIST]) { @@ -2579,7 +2615,7 @@ bool vfio_pci_add_capabilities(VFIOPCIDevice *vdev, Error **errp) void vfio_pci_pre_reset(VFIOPCIDevice *vdev) { - PCIDevice *pdev = &vdev->pdev; + PCIDevice *pdev = PCI_DEVICE(vdev); uint16_t cmd; vfio_disable_interrupts(vdev); @@ -2796,7 +2832,7 @@ static int vfio_pci_save_config(VFIODevice *vbasedev, QEMUFile *f, Error **errp) static int vfio_pci_load_config(VFIODevice *vbasedev, QEMUFile *f) { VFIOPCIDevice *vdev = container_of(vbasedev, VFIOPCIDevice, vbasedev); - PCIDevice *pdev = &vdev->pdev; + PCIDevice *pdev = PCI_DEVICE(vdev); pcibus_t old_addr[PCI_NUM_REGIONS - 1]; int bar, ret; @@ -2844,7 +2880,7 @@ VFIOPCIDevice *vfio_pci_from_vfio_device(VFIODevice *vbasedev) void vfio_sub_page_bar_update_mappings(VFIOPCIDevice *vdev) { - PCIDevice *pdev = &vdev->pdev; + PCIDevice *pdev = PCI_DEVICE(vdev); int page_size = qemu_real_host_page_size(); int bar; @@ -2928,6 +2964,7 @@ bool vfio_populate_vga(VFIOPCIDevice *vdev, Error **errp) bool vfio_pci_populate_device(VFIOPCIDevice *vdev, Error **errp) { + PCIDevice *pdev = PCI_DEVICE(vdev); VFIODevice *vbasedev = &vdev->vbasedev; struct vfio_region_info *reg_info = NULL; struct vfio_irq_info irq_info; @@ -2979,7 +3016,7 @@ bool vfio_pci_populate_device(VFIOPCIDevice *vdev, Error **errp) vdev->config_size = reg_info->size; if (vdev->config_size == PCI_CONFIG_SPACE_SIZE) { - vdev->pdev.cap_present &= ~QEMU_PCI_CAP_EXPRESS; + pdev->cap_present &= ~QEMU_PCI_CAP_EXPRESS; } vdev->config_offset = reg_info->offset; @@ -3183,25 +3220,26 @@ static void vfio_unregister_req_notifier(VFIOPCIDevice *vdev) void vfio_pci_config_register_vga(VFIOPCIDevice *vdev) { + PCIDevice *pdev = PCI_DEVICE(vdev); assert(vdev->vga != NULL); - pci_register_vga(&vdev->pdev, &vdev->vga->region[QEMU_PCI_VGA_MEM].mem, + pci_register_vga(pdev, &vdev->vga->region[QEMU_PCI_VGA_MEM].mem, &vdev->vga->region[QEMU_PCI_VGA_IO_LO].mem, &vdev->vga->region[QEMU_PCI_VGA_IO_HI].mem); } bool vfio_pci_config_setup(VFIOPCIDevice *vdev, Error **errp) { - PCIDevice *pdev = &vdev->pdev; + PCIDevice *pdev = PCI_DEVICE(vdev); VFIODevice *vbasedev = &vdev->vbasedev; uint32_t config_space_size; int ret; - config_space_size = MIN(pci_config_size(&vdev->pdev), vdev->config_size); + config_space_size = MIN(pci_config_size(pdev), vdev->config_size); /* Get a copy of config space */ ret = vfio_pci_config_space_read(vdev, 0, config_space_size, - vdev->pdev.config); + pdev->config); if (ret < (int)config_space_size) { ret = ret < 0 ? -ret : EFAULT; error_setg_errno(errp, ret, "failed to read device config space"); @@ -3286,10 +3324,10 @@ bool vfio_pci_config_setup(VFIOPCIDevice *vdev, Error **errp) PCI_HEADER_TYPE_MULTI_FUNCTION; /* Restore or clear multifunction, this is always controlled by QEMU */ - if (vdev->pdev.cap_present & QEMU_PCI_CAP_MULTIFUNCTION) { - vdev->pdev.config[PCI_HEADER_TYPE] |= PCI_HEADER_TYPE_MULTI_FUNCTION; + if (pdev->cap_present & QEMU_PCI_CAP_MULTIFUNCTION) { + pdev->config[PCI_HEADER_TYPE] |= PCI_HEADER_TYPE_MULTI_FUNCTION; } else { - vdev->pdev.config[PCI_HEADER_TYPE] &= ~PCI_HEADER_TYPE_MULTI_FUNCTION; + pdev->config[PCI_HEADER_TYPE] &= ~PCI_HEADER_TYPE_MULTI_FUNCTION; } /* @@ -3297,8 +3335,8 @@ bool vfio_pci_config_setup(VFIOPCIDevice *vdev, Error **errp) * BAR, such as might be the case with the option ROM, we can get * confusing, unwritable, residual addresses from the host here. */ - memset(&vdev->pdev.config[PCI_BASE_ADDRESS_0], 0, 24); - memset(&vdev->pdev.config[PCI_ROM_ADDRESS], 0, 4); + memset(&pdev->config[PCI_BASE_ADDRESS_0], 0, 24); + memset(&pdev->config[PCI_ROM_ADDRESS], 0, 4); vfio_pci_size_rom(vdev); @@ -3319,7 +3357,7 @@ bool vfio_pci_config_setup(VFIOPCIDevice *vdev, Error **errp) bool vfio_pci_interrupt_setup(VFIOPCIDevice *vdev, Error **errp) { - PCIDevice *pdev = &vdev->pdev; + PCIDevice *pdev = PCI_DEVICE(vdev); /* QEMU emulates all of MSI & MSIX */ if (pdev->cap_present & QEMU_PCI_CAP_MSIX) { @@ -3332,10 +3370,10 @@ bool vfio_pci_interrupt_setup(VFIOPCIDevice *vdev, Error **errp) vdev->msi_cap_size); } - if (vfio_pci_read_config(&vdev->pdev, PCI_INTERRUPT_PIN, 1)) { + if (vfio_pci_read_config(pdev, PCI_INTERRUPT_PIN, 1)) { vdev->intx.mmap_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL, vfio_intx_mmap_enable, vdev); - pci_device_set_intx_routing_notifier(&vdev->pdev, + pci_device_set_intx_routing_notifier(pdev, vfio_intx_routing_notifier); vdev->irqchip_change_notifier.notify = vfio_irqchip_change; kvm_irqchip_add_change_notifier(&vdev->irqchip_change_notifier); @@ -3347,7 +3385,7 @@ bool vfio_pci_interrupt_setup(VFIOPCIDevice *vdev, Error **errp) */ if (!cpr_is_incoming() && !vfio_intx_enable(vdev, errp)) { timer_free(vdev->intx.mmap_timer); - pci_device_set_intx_routing_notifier(&vdev->pdev, NULL); + pci_device_set_intx_routing_notifier(pdev, NULL); kvm_irqchip_remove_change_notifier(&vdev->irqchip_change_notifier); return false; } @@ -3498,7 +3536,7 @@ out_deregister: if (vdev->interrupt == VFIO_INT_INTx) { vfio_intx_disable(vdev); } - pci_device_set_intx_routing_notifier(&vdev->pdev, NULL); + pci_device_set_intx_routing_notifier(pdev, NULL); if (vdev->irqchip_change_notifier.notify) { kvm_irqchip_remove_change_notifier(&vdev->irqchip_change_notifier); } @@ -3530,7 +3568,7 @@ static void vfio_exitfn(PCIDevice *pdev) vfio_unregister_req_notifier(vdev); vfio_unregister_err_notifier(vdev); - pci_device_set_intx_routing_notifier(&vdev->pdev, NULL); + pci_device_set_intx_routing_notifier(pdev, NULL); if (vdev->irqchip_change_notifier.notify) { kvm_irqchip_remove_change_notifier(&vdev->irqchip_change_notifier); } From 31bfd70ef02045692d04bf53461399d3b81c0ea3 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Tue, 15 Jul 2025 10:25:57 +0100 Subject: [PATCH 0351/2396] vfio/pci-quirks.c: use QOM casts where appropriate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use QOM casts to convert between VFIOPCIDevice and PCIDevice instead of accessing pdev directly. Signed-off-by: Mark Cave-Ayland Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250715093110.107317-18-mark.caveayland@nutanix.com Signed-off-by: Cédric Le Goater --- hw/vfio/pci-quirks.c | 48 ++++++++++++++++++++++++++------------------ 1 file changed, 29 insertions(+), 19 deletions(-) diff --git a/hw/vfio/pci-quirks.c b/hw/vfio/pci-quirks.c index 3f002252ac..c97606dbf1 100644 --- a/hw/vfio/pci-quirks.c +++ b/hw/vfio/pci-quirks.c @@ -113,6 +113,7 @@ static uint64_t vfio_generic_window_quirk_data_read(void *opaque, { VFIOConfigWindowQuirk *window = opaque; VFIOPCIDevice *vdev = window->vdev; + PCIDevice *pdev = PCI_DEVICE(vdev); uint64_t data; /* Always read data reg, discard if window enabled */ @@ -120,7 +121,7 @@ static uint64_t vfio_generic_window_quirk_data_read(void *opaque, addr + window->data_offset, size); if (window->window_enabled) { - data = vfio_pci_read_config(&vdev->pdev, window->address_val, size); + data = vfio_pci_read_config(pdev, window->address_val, size); trace_vfio_quirk_generic_window_data_read(vdev->vbasedev.name, memory_region_name(window->data_mem), data); } @@ -133,9 +134,10 @@ static void vfio_generic_window_quirk_data_write(void *opaque, hwaddr addr, { VFIOConfigWindowQuirk *window = opaque; VFIOPCIDevice *vdev = window->vdev; + PCIDevice *pdev = PCI_DEVICE(vdev); if (window->window_enabled) { - vfio_pci_write_config(&vdev->pdev, window->address_val, data, size); + vfio_pci_write_config(pdev, window->address_val, data, size); trace_vfio_quirk_generic_window_data_write(vdev->vbasedev.name, memory_region_name(window->data_mem), data); return; @@ -156,6 +158,7 @@ static uint64_t vfio_generic_quirk_mirror_read(void *opaque, { VFIOConfigMirrorQuirk *mirror = opaque; VFIOPCIDevice *vdev = mirror->vdev; + PCIDevice *pdev = PCI_DEVICE(vdev); uint64_t data; /* Read and discard in case the hardware cares */ @@ -163,7 +166,7 @@ static uint64_t vfio_generic_quirk_mirror_read(void *opaque, addr + mirror->offset, size); addr += mirror->config_offset; - data = vfio_pci_read_config(&vdev->pdev, addr, size); + data = vfio_pci_read_config(pdev, addr, size); trace_vfio_quirk_generic_mirror_read(vdev->vbasedev.name, memory_region_name(mirror->mem), addr, data); @@ -175,9 +178,10 @@ static void vfio_generic_quirk_mirror_write(void *opaque, hwaddr addr, { VFIOConfigMirrorQuirk *mirror = opaque; VFIOPCIDevice *vdev = mirror->vdev; + PCIDevice *pdev = PCI_DEVICE(vdev); addr += mirror->config_offset; - vfio_pci_write_config(&vdev->pdev, addr, data, size); + vfio_pci_write_config(pdev, addr, data, size); trace_vfio_quirk_generic_mirror_write(vdev->vbasedev.name, memory_region_name(mirror->mem), addr, data); @@ -211,7 +215,8 @@ static uint64_t vfio_ati_3c3_quirk_read(void *opaque, hwaddr addr, unsigned size) { VFIOPCIDevice *vdev = opaque; - uint64_t data = vfio_pci_read_config(&vdev->pdev, + PCIDevice *pdev = PCI_DEVICE(vdev); + uint64_t data = vfio_pci_read_config(pdev, PCI_BASE_ADDRESS_4 + 1, size); trace_vfio_quirk_ati_3c3_read(vdev->vbasedev.name, data); @@ -563,6 +568,7 @@ static uint64_t vfio_nvidia_3d0_quirk_read(void *opaque, { VFIONvidia3d0Quirk *quirk = opaque; VFIOPCIDevice *vdev = quirk->vdev; + PCIDevice *pdev = PCI_DEVICE(vdev); VFIONvidia3d0State old_state = quirk->state; uint64_t data = vfio_vga_read(&vdev->vga->region[QEMU_PCI_VGA_IO_HI], addr + 0x10, size); @@ -573,7 +579,7 @@ static uint64_t vfio_nvidia_3d0_quirk_read(void *opaque, (quirk->offset & ~(PCI_CONFIG_SPACE_SIZE - 1)) == 0x1800) { uint8_t offset = quirk->offset & (PCI_CONFIG_SPACE_SIZE - 1); - data = vfio_pci_read_config(&vdev->pdev, offset, size); + data = vfio_pci_read_config(pdev, offset, size); trace_vfio_quirk_nvidia_3d0_read(vdev->vbasedev.name, offset, size, data); } @@ -586,6 +592,7 @@ static void vfio_nvidia_3d0_quirk_write(void *opaque, hwaddr addr, { VFIONvidia3d0Quirk *quirk = opaque; VFIOPCIDevice *vdev = quirk->vdev; + PCIDevice *pdev = PCI_DEVICE(vdev); VFIONvidia3d0State old_state = quirk->state; quirk->state = NONE; @@ -599,7 +606,7 @@ static void vfio_nvidia_3d0_quirk_write(void *opaque, hwaddr addr, if ((quirk->offset & ~(PCI_CONFIG_SPACE_SIZE - 1)) == 0x1800) { uint8_t offset = quirk->offset & (PCI_CONFIG_SPACE_SIZE - 1); - vfio_pci_write_config(&vdev->pdev, offset, data, size); + vfio_pci_write_config(pdev, offset, data, size); trace_vfio_quirk_nvidia_3d0_write(vdev->vbasedev.name, offset, data, size); return; @@ -815,7 +822,7 @@ static void vfio_nvidia_quirk_mirror_write(void *opaque, hwaddr addr, { VFIOConfigMirrorQuirk *mirror = opaque; VFIOPCIDevice *vdev = mirror->vdev; - PCIDevice *pdev = &vdev->pdev; + PCIDevice *pdev = PCI_DEVICE(vdev); LastDataSet *last = (LastDataSet *)&mirror->data; vfio_generic_quirk_mirror_write(opaque, addr, data, size); @@ -1005,6 +1012,7 @@ static void vfio_rtl8168_quirk_address_write(void *opaque, hwaddr addr, { VFIOrtl8168Quirk *rtl = opaque; VFIOPCIDevice *vdev = rtl->vdev; + PCIDevice *pdev = PCI_DEVICE(vdev); rtl->enabled = false; @@ -1013,7 +1021,7 @@ static void vfio_rtl8168_quirk_address_write(void *opaque, hwaddr addr, rtl->addr = (uint32_t)data; if (data & 0x80000000U) { /* Do write */ - if (vdev->pdev.cap_present & QEMU_PCI_CAP_MSIX) { + if (pdev->cap_present & QEMU_PCI_CAP_MSIX) { hwaddr offset = data & 0xfff; uint64_t val = rtl->data; @@ -1021,7 +1029,7 @@ static void vfio_rtl8168_quirk_address_write(void *opaque, hwaddr addr, (uint16_t)offset, val); /* Write to the proper guest MSI-X table instead */ - memory_region_dispatch_write(&vdev->pdev.msix_table_mmio, + memory_region_dispatch_write(&pdev->msix_table_mmio, offset, val, size_memop(size) | MO_LE, MEMTXATTRS_UNSPECIFIED); @@ -1049,11 +1057,12 @@ static uint64_t vfio_rtl8168_quirk_data_read(void *opaque, { VFIOrtl8168Quirk *rtl = opaque; VFIOPCIDevice *vdev = rtl->vdev; + PCIDevice *pdev = PCI_DEVICE(vdev); uint64_t data = vfio_region_read(&vdev->bars[2].region, addr + 0x70, size); - if (rtl->enabled && (vdev->pdev.cap_present & QEMU_PCI_CAP_MSIX)) { + if (rtl->enabled && (pdev->cap_present & QEMU_PCI_CAP_MSIX)) { hwaddr offset = rtl->addr & 0xfff; - memory_region_dispatch_read(&vdev->pdev.msix_table_mmio, offset, + memory_region_dispatch_read(&pdev->msix_table_mmio, offset, &data, size_memop(size) | MO_LE, MEMTXATTRS_UNSPECIFIED); trace_vfio_quirk_rtl8168_msix_read(vdev->vbasedev.name, offset, data); @@ -1297,7 +1306,7 @@ static void vfio_radeon_set_gfx_only_reset(VFIOPCIDevice *vdev) static int vfio_radeon_reset(VFIOPCIDevice *vdev) { - PCIDevice *pdev = &vdev->pdev; + PCIDevice *pdev = PCI_DEVICE(vdev); int i, ret = 0; uint32_t data; @@ -1454,7 +1463,7 @@ static bool is_valid_std_cap_offset(uint8_t pos) static bool vfio_add_nv_gpudirect_cap(VFIOPCIDevice *vdev, Error **errp) { ERRP_GUARD(); - PCIDevice *pdev = &vdev->pdev; + PCIDevice *pdev = PCI_DEVICE(vdev); int ret, pos; bool c8_conflict = false, d4_conflict = false; uint8_t tmp; @@ -1547,6 +1556,7 @@ static bool vfio_add_nv_gpudirect_cap(VFIOPCIDevice *vdev, Error **errp) static bool vfio_add_vmd_shadow_cap(VFIOPCIDevice *vdev, Error **errp) { ERRP_GUARD(); + PCIDevice *pdev = PCI_DEVICE(vdev); uint8_t membar_phys[16]; int ret, pos = 0xE8; @@ -1565,7 +1575,7 @@ static bool vfio_add_vmd_shadow_cap(VFIOPCIDevice *vdev, Error **errp) return false; } - ret = pci_add_capability(&vdev->pdev, PCI_CAP_ID_VNDR, pos, + ret = pci_add_capability(pdev, PCI_CAP_ID_VNDR, pos, VMD_SHADOW_CAP_LEN, errp); if (ret < 0) { error_prepend(errp, "Failed to add VMD MEMBAR Shadow cap: "); @@ -1574,10 +1584,10 @@ static bool vfio_add_vmd_shadow_cap(VFIOPCIDevice *vdev, Error **errp) memset(vdev->emulated_config_bits + pos, 0xFF, VMD_SHADOW_CAP_LEN); pos += PCI_CAP_FLAGS; - pci_set_byte(vdev->pdev.config + pos++, VMD_SHADOW_CAP_LEN); - pci_set_byte(vdev->pdev.config + pos++, VMD_SHADOW_CAP_VER); - pci_set_long(vdev->pdev.config + pos, 0x53484457); /* SHDW */ - memcpy(vdev->pdev.config + pos + 4, membar_phys, 16); + pci_set_byte(pdev->config + pos++, VMD_SHADOW_CAP_LEN); + pci_set_byte(pdev->config + pos++, VMD_SHADOW_CAP_VER); + pci_set_long(pdev->config + pos, 0x53484457); /* SHDW */ + memcpy(pdev->config + pos + 4, membar_phys, 16); return true; } From 54a3eb315023f59db0426d85604f4527bf7b1aaa Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Tue, 15 Jul 2025 10:25:58 +0100 Subject: [PATCH 0352/2396] vfio/cpr.c: use QOM casts where appropriate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use QOM casts to convert between VFIOPCIDevice and PCIDevice instead of accessing pdev directly. Signed-off-by: Mark Cave-Ayland Reviewed-by: Steve Sistare Link: https://lore.kernel.org/qemu-devel/20250715093110.107317-19-mark.caveayland@nutanix.com [ clg: Updated vfio_cpr_set_msi_virq() ] Signed-off-by: Cédric Le Goater --- hw/vfio/cpr.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/hw/vfio/cpr.c b/hw/vfio/cpr.c index a831243e02..f911988add 100644 --- a/hw/vfio/cpr.c +++ b/hw/vfio/cpr.c @@ -56,7 +56,7 @@ static void vfio_cpr_claim_vectors(VFIOPCIDevice *vdev, int nr_vectors, { int i, fd; bool pending = false; - PCIDevice *pdev = &vdev->pdev; + PCIDevice *pdev = PCI_DEVICE(vdev); vdev->nr_vectors = nr_vectors; vdev->msi_vectors = g_new0(VFIOMSIVector, nr_vectors); @@ -99,7 +99,7 @@ static void vfio_cpr_claim_vectors(VFIOPCIDevice *vdev, int nr_vectors, static int vfio_cpr_pci_pre_load(void *opaque) { VFIOPCIDevice *vdev = opaque; - PCIDevice *pdev = &vdev->pdev; + PCIDevice *pdev = PCI_DEVICE(vdev); int size = MIN(pci_config_size(pdev), vdev->config_size); int i; @@ -113,7 +113,7 @@ static int vfio_cpr_pci_pre_load(void *opaque) static int vfio_cpr_pci_post_load(void *opaque, int version_id) { VFIOPCIDevice *vdev = opaque; - PCIDevice *pdev = &vdev->pdev; + PCIDevice *pdev = PCI_DEVICE(vdev); int nr_vectors; vfio_sub_page_bar_update_mappings(vdev); @@ -214,7 +214,7 @@ static int set_irqfd_notifier_gsi(KVMState *s, EventNotifier *n, static int vfio_cpr_set_msi_virq(VFIOPCIDevice *vdev, Error **errp, bool enable) { const char *op = (enable ? "enable" : "disable"); - PCIDevice *pdev = &vdev->pdev; + PCIDevice *pdev = PCI_DEVICE(vdev); int i, nr_vectors, ret = 0; if (msix_enabled(pdev)) { From 8d3776dd6d8740fb7c91c7c1c25b60fc7edfd19c Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Tue, 15 Jul 2025 10:25:59 +0100 Subject: [PATCH 0353/2396] vfio/igd.c: use QOM casts where appropriate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use QOM casts to convert between VFIOPCIDevice and PCIDevice instead of accessing pdev directly. Signed-off-by: Mark Cave-Ayland Reviewed-by: Tomita Moeko Link: https://lore.kernel.org/qemu-devel/20250715093110.107317-20-mark.caveayland@nutanix.com Signed-off-by: Cédric Le Goater --- hw/vfio/igd.c | 38 +++++++++++++++++++++----------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/hw/vfio/igd.c b/hw/vfio/igd.c index f116c40ccd..4bfa2e0fcd 100644 --- a/hw/vfio/igd.c +++ b/hw/vfio/igd.c @@ -200,7 +200,7 @@ static bool vfio_pci_igd_opregion_detect(VFIOPCIDevice *vdev, } /* Hotplugging is not supported for opregion access */ - if (vdev->pdev.qdev.hotplugged) { + if (DEVICE(vdev)->hotplugged) { warn_report("IGD device detected, but OpRegion is not supported " "on hotplugged device."); return false; @@ -260,11 +260,12 @@ static int vfio_pci_igd_copy(VFIOPCIDevice *vdev, PCIDevice *pdev, static int vfio_pci_igd_host_init(VFIOPCIDevice *vdev, struct vfio_region_info *info) { + PCIDevice *pdev = PCI_DEVICE(vdev); PCIBus *bus; PCIDevice *host_bridge; int ret; - bus = pci_device_root_bus(&vdev->pdev); + bus = pci_device_root_bus(pdev); host_bridge = pci_find_device(bus, 0, PCI_DEVFN(0, 0)); if (!host_bridge) { @@ -327,13 +328,14 @@ type_init(vfio_pci_igd_register_types) static int vfio_pci_igd_lpc_init(VFIOPCIDevice *vdev, struct vfio_region_info *info) { + PCIDevice *pdev = PCI_DEVICE(vdev); PCIDevice *lpc_bridge; int ret; - lpc_bridge = pci_find_device(pci_device_root_bus(&vdev->pdev), + lpc_bridge = pci_find_device(pci_device_root_bus(pdev), 0, PCI_DEVFN(0x1f, 0)); if (!lpc_bridge) { - lpc_bridge = pci_create_simple(pci_device_root_bus(&vdev->pdev), + lpc_bridge = pci_create_simple(pci_device_root_bus(pdev), PCI_DEVFN(0x1f, 0), "vfio-pci-igd-lpc-bridge"); } @@ -350,13 +352,14 @@ static bool vfio_pci_igd_setup_lpc_bridge(VFIOPCIDevice *vdev, Error **errp) { struct vfio_region_info *host = NULL; struct vfio_region_info *lpc = NULL; + PCIDevice *pdev = PCI_DEVICE(vdev); PCIDevice *lpc_bridge; int ret; /* * Copying IDs or creating new devices are not supported on hotplug */ - if (vdev->pdev.qdev.hotplugged) { + if (DEVICE(vdev)->hotplugged) { error_setg(errp, "IGD LPC is not supported on hotplugged device"); return false; } @@ -366,7 +369,7 @@ static bool vfio_pci_igd_setup_lpc_bridge(VFIOPCIDevice *vdev, Error **errp) * can stuff host values into, so if there's already one there and it's not * one we can hack on, this quirk is no-go. Sorry Q35. */ - lpc_bridge = pci_find_device(pci_device_root_bus(&vdev->pdev), + lpc_bridge = pci_find_device(pci_device_root_bus(pdev), 0, PCI_DEVFN(0x1f, 0)); if (lpc_bridge && !object_dynamic_cast(OBJECT(lpc_bridge), "vfio-pci-igd-lpc-bridge")) { @@ -510,6 +513,7 @@ void vfio_probe_igd_bar0_quirk(VFIOPCIDevice *vdev, int nr) static bool vfio_pci_igd_config_quirk(VFIOPCIDevice *vdev, Error **errp) { struct vfio_region_info *opregion = NULL; + PCIDevice *pdev = PCI_DEVICE(vdev); int ret, gen; uint64_t gms_size = 0; uint64_t *bdsm_size; @@ -529,7 +533,7 @@ static bool vfio_pci_igd_config_quirk(VFIOPCIDevice *vdev, Error **errp) info_report("OpRegion detected on Intel display %x.", vdev->device_id); gen = igd_gen(vdev); - gmch = vfio_pci_read_config(&vdev->pdev, IGD_GMCH, 4); + gmch = vfio_pci_read_config(pdev, IGD_GMCH, 4); /* * For backward compatibility, enable legacy mode when @@ -544,7 +548,7 @@ static bool vfio_pci_igd_config_quirk(VFIOPCIDevice *vdev, Error **errp) (gen >= 6 && gen <= 9) && !(gmch & IGD_GMCH_VGA_DISABLE) && !strcmp(MACHINE_GET_CLASS(qdev_get_machine())->family, "pc_piix") && - (&vdev->pdev == pci_find_device(pci_device_root_bus(&vdev->pdev), + (pdev == pci_find_device(pci_device_root_bus(pdev), 0, PCI_DEVFN(0x2, 0)))) { /* * IGD legacy mode requires: @@ -566,7 +570,7 @@ static bool vfio_pci_igd_config_quirk(VFIOPCIDevice *vdev, Error **errp) */ ret = vfio_device_get_region_info(&vdev->vbasedev, VFIO_PCI_ROM_REGION_INDEX, &rom); - if ((ret || !rom->size) && !vdev->pdev.romfile) { + if ((ret || !rom->size) && !pdev->romfile) { error_setg(&err, "Device has no ROM"); goto error; } @@ -611,8 +615,8 @@ static bool vfio_pci_igd_config_quirk(VFIOPCIDevice *vdev, Error **errp) * ASLS (OpRegion address) is read-only, emulated * It contains HPA, guest firmware need to reprogram it with GPA. */ - pci_set_long(vdev->pdev.config + IGD_ASLS, 0); - pci_set_long(vdev->pdev.wmask + IGD_ASLS, ~0); + pci_set_long(pdev->config + IGD_ASLS, 0); + pci_set_long(pdev->wmask + IGD_ASLS, ~0); pci_set_long(vdev->emulated_config_bits + IGD_ASLS, ~0); /* @@ -626,8 +630,8 @@ static bool vfio_pci_igd_config_quirk(VFIOPCIDevice *vdev, Error **errp) } /* GMCH is read-only, emulated */ - pci_set_long(vdev->pdev.config + IGD_GMCH, gmch); - pci_set_long(vdev->pdev.wmask + IGD_GMCH, 0); + pci_set_long(pdev->config + IGD_GMCH, gmch); + pci_set_long(pdev->wmask + IGD_GMCH, 0); pci_set_long(vdev->emulated_config_bits + IGD_GMCH, ~0); } @@ -636,12 +640,12 @@ static bool vfio_pci_igd_config_quirk(VFIOPCIDevice *vdev, Error **errp) /* BDSM is read-write, emulated. BIOS needs to be able to write it */ if (gen < 11) { - pci_set_long(vdev->pdev.config + IGD_BDSM, 0); - pci_set_long(vdev->pdev.wmask + IGD_BDSM, ~0); + pci_set_long(pdev->config + IGD_BDSM, 0); + pci_set_long(pdev->wmask + IGD_BDSM, ~0); pci_set_long(vdev->emulated_config_bits + IGD_BDSM, ~0); } else { - pci_set_quad(vdev->pdev.config + IGD_BDSM_GEN11, 0); - pci_set_quad(vdev->pdev.wmask + IGD_BDSM_GEN11, ~0); + pci_set_quad(pdev->config + IGD_BDSM_GEN11, 0); + pci_set_quad(pdev->wmask + IGD_BDSM_GEN11, ~0); pci_set_quad(vdev->emulated_config_bits + IGD_BDSM_GEN11, ~0); } } From a49ef7a467c3ced0be048b02189092031e325d01 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Tue, 15 Jul 2025 10:26:00 +0100 Subject: [PATCH 0354/2396] vfio-user/pci.c: use QOM casts where appropriate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use QOM casts to convert between VFIOPCIDevice and PCIDevice instead of accessing pdev directly. Signed-off-by: Mark Cave-Ayland Reviewed-by: John Levon Link: https://lore.kernel.org/qemu-devel/20250715093110.107317-21-mark.caveayland@nutanix.com Signed-off-by: Cédric Le Goater --- hw/vfio-user/pci.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hw/vfio-user/pci.c b/hw/vfio-user/pci.c index c3947a8f2e..e2c309784f 100644 --- a/hw/vfio-user/pci.c +++ b/hw/vfio-user/pci.c @@ -65,7 +65,7 @@ static void vfio_user_msix_setup(VFIOPCIDevice *vdev) vdev->msix->pba_region = pba_reg; vfio_reg = vdev->bars[vdev->msix->pba_bar].mr; - msix_reg = &vdev->pdev.msix_pba_mmio; + msix_reg = &PCI_DEVICE(vdev)->msix_pba_mmio; memory_region_init_io(pba_reg, OBJECT(vdev), &vfio_user_pba_ops, vdev, "VFIO MSIX PBA", int128_get64(msix_reg->size)); memory_region_add_subregion_overlap(vfio_reg, vdev->msix->pba_offset, @@ -86,7 +86,7 @@ static void vfio_user_msix_teardown(VFIOPCIDevice *vdev) static void vfio_user_dma_read(VFIOPCIDevice *vdev, VFIOUserDMARW *msg) { - PCIDevice *pdev = &vdev->pdev; + PCIDevice *pdev = PCI_DEVICE(vdev); VFIOUserProxy *proxy = vdev->vbasedev.proxy; VFIOUserDMARW *res; MemTxResult r; @@ -134,7 +134,7 @@ static void vfio_user_dma_read(VFIOPCIDevice *vdev, VFIOUserDMARW *msg) static void vfio_user_dma_write(VFIOPCIDevice *vdev, VFIOUserDMARW *msg) { - PCIDevice *pdev = &vdev->pdev; + PCIDevice *pdev = PCI_DEVICE(vdev); VFIOUserProxy *proxy = vdev->vbasedev.proxy; MemTxResult r; From e2827210d6a9c56c1b14b00b414dfa9eb7843711 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Tue, 15 Jul 2025 10:26:01 +0100 Subject: [PATCH 0355/2396] s390x/s390-pci-vfio.c: use QOM casts where appropriate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use QOM casts to cast to VFIOPCIDevice instead of using container_of(). Signed-off-by: Mark Cave-Ayland Reviewed-by: Matthew Rosato Reviewed-by: Eric Farman Link: https://lore.kernel.org/qemu-devel/20250715093110.107317-22-mark.caveayland@nutanix.com Signed-off-by: Cédric Le Goater --- hw/s390x/s390-pci-vfio.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/hw/s390x/s390-pci-vfio.c b/hw/s390x/s390-pci-vfio.c index aaf91319b4..938a551171 100644 --- a/hw/s390x/s390-pci-vfio.c +++ b/hw/s390x/s390-pci-vfio.c @@ -62,7 +62,7 @@ S390PCIDMACount *s390_pci_start_dma_count(S390pciState *s, { S390PCIDMACount *cnt; uint32_t avail; - VFIOPCIDevice *vpdev = container_of(pbdev->pdev, VFIOPCIDevice, pdev); + VFIOPCIDevice *vpdev = VFIO_PCI_BASE(pbdev->pdev); int id; assert(vpdev); @@ -108,7 +108,7 @@ static void s390_pci_read_base(S390PCIBusDevice *pbdev, { struct vfio_info_cap_header *hdr; struct vfio_device_info_cap_zpci_base *cap; - VFIOPCIDevice *vpci = container_of(pbdev->pdev, VFIOPCIDevice, pdev); + VFIOPCIDevice *vpci = VFIO_PCI_BASE(pbdev->pdev); uint64_t vfio_size; hdr = vfio_get_device_info_cap(info, VFIO_DEVICE_INFO_CAP_ZPCI_BASE); @@ -162,7 +162,7 @@ static bool get_host_fh(S390PCIBusDevice *pbdev, struct vfio_device_info *info, { struct vfio_info_cap_header *hdr; struct vfio_device_info_cap_zpci_base *cap; - VFIOPCIDevice *vpci = container_of(pbdev->pdev, VFIOPCIDevice, pdev); + VFIOPCIDevice *vpci = VFIO_PCI_BASE(pbdev->pdev); hdr = vfio_get_device_info_cap(info, VFIO_DEVICE_INFO_CAP_ZPCI_BASE); @@ -185,7 +185,7 @@ static void s390_pci_read_group(S390PCIBusDevice *pbdev, struct vfio_device_info_cap_zpci_group *cap; S390pciState *s = s390_get_phb(); ClpRspQueryPciGrp *resgrp; - VFIOPCIDevice *vpci = container_of(pbdev->pdev, VFIOPCIDevice, pdev); + VFIOPCIDevice *vpci = VFIO_PCI_BASE(pbdev->pdev); uint8_t start_gid = pbdev->zpci_fn.pfgid; hdr = vfio_get_device_info_cap(info, VFIO_DEVICE_INFO_CAP_ZPCI_GROUP); @@ -264,7 +264,7 @@ static void s390_pci_read_util(S390PCIBusDevice *pbdev, { struct vfio_info_cap_header *hdr; struct vfio_device_info_cap_zpci_util *cap; - VFIOPCIDevice *vpci = container_of(pbdev->pdev, VFIOPCIDevice, pdev); + VFIOPCIDevice *vpci = VFIO_PCI_BASE(pbdev->pdev); hdr = vfio_get_device_info_cap(info, VFIO_DEVICE_INFO_CAP_ZPCI_UTIL); @@ -291,7 +291,7 @@ static void s390_pci_read_pfip(S390PCIBusDevice *pbdev, { struct vfio_info_cap_header *hdr; struct vfio_device_info_cap_zpci_pfip *cap; - VFIOPCIDevice *vpci = container_of(pbdev->pdev, VFIOPCIDevice, pdev); + VFIOPCIDevice *vpci = VFIO_PCI_BASE(pbdev->pdev); hdr = vfio_get_device_info_cap(info, VFIO_DEVICE_INFO_CAP_ZPCI_PFIP); @@ -314,7 +314,7 @@ static void s390_pci_read_pfip(S390PCIBusDevice *pbdev, static struct vfio_device_info *get_device_info(S390PCIBusDevice *pbdev) { - VFIOPCIDevice *vfio_pci = container_of(pbdev->pdev, VFIOPCIDevice, pdev); + VFIOPCIDevice *vfio_pci = VFIO_PCI_BASE(pbdev->pdev); return vfio_get_device_info(vfio_pci->vbasedev.fd); } From bb986792a968ee51cda72cd4cc05822198495375 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Tue, 15 Jul 2025 10:26:02 +0100 Subject: [PATCH 0356/2396] vfio/pci.h: rename VFIOPCIDevice pdev field to parent_obj MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that nothing accesses the pdev field directly, rename pdev to parent_obj as per our current coding guidelines. Signed-off-by: Mark Cave-Ayland Reviewed-by: Cédric Le Goater Reviewed-by: Steve Sistare Reviewed-by: Philippe Mathieu-Daudé Link: https://lore.kernel.org/qemu-devel/20250715093110.107317-23-mark.caveayland@nutanix.com Signed-off-by: Cédric Le Goater --- hw/vfio/cpr.c | 4 ++-- hw/vfio/pci.c | 4 ++-- hw/vfio/pci.h | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/hw/vfio/cpr.c b/hw/vfio/cpr.c index f911988add..2c71fc1e8e 100644 --- a/hw/vfio/cpr.c +++ b/hw/vfio/cpr.c @@ -173,8 +173,8 @@ const VMStateDescription vfio_cpr_pci_vmstate = { .post_load = vfio_cpr_pci_post_load, .needed = cpr_incoming_needed, .fields = (VMStateField[]) { - VMSTATE_PCI_DEVICE(pdev, VFIOPCIDevice), - VMSTATE_MSIX_TEST(pdev, VFIOPCIDevice, pci_msix_present), + VMSTATE_PCI_DEVICE(parent_obj, VFIOPCIDevice), + VMSTATE_MSIX_TEST(parent_obj, VFIOPCIDevice, pci_msix_present), VMSTATE_VFIO_INTX(intx, VFIOPCIDevice), VMSTATE_END_OF_LIST() } diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index 052591d2c9..d14e96b2f8 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -2811,8 +2811,8 @@ static const VMStateDescription vmstate_vfio_pci_config = { .version_id = 1, .minimum_version_id = 1, .fields = (const VMStateField[]) { - VMSTATE_PCI_DEVICE(pdev, VFIOPCIDevice), - VMSTATE_MSIX_TEST(pdev, VFIOPCIDevice, vfio_msix_present), + VMSTATE_PCI_DEVICE(parent_obj, VFIOPCIDevice), + VMSTATE_MSIX_TEST(parent_obj, VFIOPCIDevice, vfio_msix_present), VMSTATE_END_OF_LIST() }, .subsections = (const VMStateDescription * const []) { diff --git a/hw/vfio/pci.h b/hw/vfio/pci.h index 2db76b6f4f..e0aef82a89 100644 --- a/hw/vfio/pci.h +++ b/hw/vfio/pci.h @@ -123,7 +123,7 @@ typedef struct VFIOMSIXInfo { OBJECT_DECLARE_SIMPLE_TYPE(VFIOPCIDevice, VFIO_PCI_BASE) struct VFIOPCIDevice { - PCIDevice pdev; + PCIDevice parent_obj; VFIODevice vbasedev; VFIOINTx intx; From 180c65c3467f49565ffe26f663a0482717f7d4ea Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Thu, 8 May 2025 16:41:17 +0200 Subject: [PATCH 0357/2396] hw/display/bcm2835_fb: Move inclusion of console.h to the .c file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The definitions from console.h are not needed in the bcm2835_fb.h header file yet, so let's move it to the place that really needs its definitions, i.e. into the bcm2835_fb.c file. This way the header can also be used by code that is not compiled with the CFLAGS that are required for pixman or OpenGL (in case their headers do not reside under /usr/include). Reviewed-by: Daniel P. Berrangé Signed-off-by: Thomas Huth Message-ID: <20250508144120.163009-3-thuth@redhat.com> --- hw/display/bcm2835_fb.c | 1 + include/hw/display/bcm2835_fb.h | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/display/bcm2835_fb.c b/hw/display/bcm2835_fb.c index 820e67ac8b..1bb2ee45a0 100644 --- a/hw/display/bcm2835_fb.c +++ b/hw/display/bcm2835_fb.c @@ -27,6 +27,7 @@ #include "hw/display/bcm2835_fb.h" #include "hw/hw.h" #include "hw/irq.h" +#include "ui/console.h" #include "framebuffer.h" #include "ui/pixel_ops.h" #include "hw/misc/bcm2835_mbox_defs.h" diff --git a/include/hw/display/bcm2835_fb.h b/include/hw/display/bcm2835_fb.h index 49541bf08f..acc9230b6a 100644 --- a/include/hw/display/bcm2835_fb.h +++ b/include/hw/display/bcm2835_fb.h @@ -13,7 +13,6 @@ #define BCM2835_FB_H #include "hw/sysbus.h" -#include "ui/console.h" #include "qom/object.h" #define UPPER_RAM_BASE 0x40000000 From b3a51bb30bc72f88231e3b6a63c04eef5c36433c Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Thu, 8 May 2025 16:41:20 +0200 Subject: [PATCH 0358/2396] Revert "meson.build: Disable -fzero-call-used-regs on OpenBSD" This reverts commit 2d6d995709482cc8b6a76dbb5334a28001a14a9a. OpenBSD 7.7 fixed the problem with the -fzero-call-used-regs on OpenBSD, see https://github.com/openbsd/src/commit/03eca72d1e030b7a542cd6aec1 for the fix there. Suggested-by: Brad Smith Signed-off-by: Thomas Huth Message-ID: <20250508144120.163009-6-thuth@redhat.com> --- meson.build | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/meson.build b/meson.build index fa6186db33..3d73873356 100644 --- a/meson.build +++ b/meson.build @@ -709,11 +709,7 @@ hardening_flags = [ # # NB: Clang 17 is broken and SEGVs # https://github.com/llvm/llvm-project/issues/75168 -# -# NB2: This clashes with the "retguard" extension of OpenBSD's Clang -# https://gitlab.com/qemu-project/qemu/-/issues/2278 -if host_os != 'openbsd' and \ - cc.compiles('extern struct { void (*cb)(void); } s; void f(void) { s.cb(); }', +if cc.compiles('extern struct { void (*cb)(void); } s; void f(void) { s.cb(); }', name: '-fzero-call-used-regs=used-gpr', args: ['-O2', '-fzero-call-used-regs=used-gpr']) hardening_flags += '-fzero-call-used-regs=used-gpr' From 7af325c23ef5e94b77864d2d2ca64da4a5a35f30 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Mon, 28 Jul 2025 13:51:51 +0200 Subject: [PATCH 0359/2396] hw/mips/malta: Silence warning from ubsan MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When compiling QEMU with --enable-ubsan there is a undefined behavior warning when using the malta machine: hw/mips/malta.c:1200:32: runtime error: addition of unsigned offset to 0x7fb620600000 overflowed to 0x7fb6205fffff SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior hw/mips/malta.c:1200:32 To fix the issue, check the bios_size whether we really loaded the firmware before trying to byte-swap the instructions here. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Thomas Huth Message-ID: <20250728115152.187728-1-thuth@redhat.com> --- hw/mips/malta.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/mips/malta.c b/hw/mips/malta.c index 344dc8ca76..02da629b5a 100644 --- a/hw/mips/malta.c +++ b/hw/mips/malta.c @@ -1191,7 +1191,7 @@ void mips_malta_init(MachineState *machine) * In little endian mode the 32bit words in the bios are swapped, * a neat trick which allows bi-endian firmware. */ - if (!TARGET_BIG_ENDIAN) { + if (!TARGET_BIG_ENDIAN && bios_size > 0) { uint32_t *end, *addr; const size_t swapsize = MIN(bios_size, 0x3e0000); addr = rom_ptr(FLASH_ADDRESS, swapsize); From 2dbaf58bbe78f415ec867dc238f90321ed8a3f62 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Mon, 28 Jul 2025 19:25:45 +0200 Subject: [PATCH 0360/2396] system/physmem: Silence warning from ubsan When compiling QEMU with --enable-ubsan there is a undefined behavior warning when running the bios-tables-test for example: .../system/physmem.c:3243:13: runtime error: applying non-zero offset 262144 to null pointer #0 0x55ac1df5fbc4 in address_space_write_rom_internal .../system/physmem.c:3243:13 The problem is that buf is indeed NULL if the function is e.g. called with type == FLUSH_CACHE. Add a check to fix the issue. Reviewed-by: David Hildenbrand Signed-off-by: Thomas Huth Message-ID: <20250728172545.314178-1-thuth@redhat.com> --- system/physmem.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/system/physmem.c b/system/physmem.c index f498572fc8..311011156c 100644 --- a/system/physmem.c +++ b/system/physmem.c @@ -3231,8 +3231,10 @@ static inline MemTxResult address_space_write_rom_internal(AddressSpace *as, } } len -= l; - buf += l; addr += l; + if (buf) { + buf += l; + } } return MEMTX_OK; } From 38dd513263d814dc3cf554b899c118a46ca77577 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Thu, 21 Aug 2025 16:51:30 +0200 Subject: [PATCH 0361/2396] ui/vnc: Fix crash when specifying [vnc] without id in the config file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit QEMU currently crashes when there is a [vnc] section in the config file that does not have an "id = ..." line: $ echo "[vnc]" > /tmp/qemu.conf $ ./qemu-system-x86_64 -readconfig /tmp/qemu.conf qemu-system-x86_64: ../../devel/qemu/ui/vnc.c:4347: vnc_init_func: Assertion `id' failed. Aborted (core dumped) The required "id" is only set up automatically while parsing the command line, but not when reading the options from the config file. Thus let's move code that automatically adds the id (if it does not exist yet) to the init function that needs the id for the first time, replacing the assert() statement there. Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2836 Reviewed-by: Marc-André Lureau Signed-off-by: Thomas Huth Message-ID: <20250821145130.845104-1-thuth@redhat.com> --- ui/vnc.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/ui/vnc.c b/ui/vnc.c index 68ca4a68e7..9054fc8125 100644 --- a/ui/vnc.c +++ b/ui/vnc.c @@ -4309,8 +4309,9 @@ void vnc_display_add_client(const char *id, int csock, bool skipauth) } } -static void vnc_auto_assign_id(QemuOptsList *olist, QemuOpts *opts) +static char *vnc_auto_assign_id(QemuOpts *opts) { + QemuOptsList *olist = qemu_find_opts("vnc"); int i = 2; char *id; @@ -4320,23 +4321,18 @@ static void vnc_auto_assign_id(QemuOptsList *olist, QemuOpts *opts) id = g_strdup_printf("vnc%d", i++); } qemu_opts_set_id(opts, id); + + return id; } void vnc_parse(const char *str) { QemuOptsList *olist = qemu_find_opts("vnc"); QemuOpts *opts = qemu_opts_parse_noisily(olist, str, !is_help_option(str)); - const char *id; if (!opts) { exit(1); } - - id = qemu_opts_id(opts); - if (!id) { - /* auto-assign id if not present */ - vnc_auto_assign_id(olist, opts); - } } int vnc_init_func(void *opaque, QemuOpts *opts, Error **errp) @@ -4344,7 +4340,11 @@ int vnc_init_func(void *opaque, QemuOpts *opts, Error **errp) Error *local_err = NULL; char *id = (char *)qemu_opts_id(opts); - assert(id); + if (!id) { + /* auto-assign id if not present */ + id = vnc_auto_assign_id(opts); + } + vnc_display_init(id, &local_err); if (local_err) { error_propagate(errp, local_err); From 5f81033e5b3e55eda2a1d06f959c05b61e719d68 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Fri, 29 Aug 2025 16:20:00 +0200 Subject: [PATCH 0362/2396] tests/functional/m68k: Avoid ResourceWarning in the nextcube test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since commit c3fd296cf7b1 ("functional: always enable all python warnings") we enabled more warnings for the functional tests. This triggers now a warning in the nextcube test: tests/functional/m68k/test_nextcube.py:47: ResourceWarning: unclosed file <_io.BufferedReader name='tests/functional/m68k/test_nextcube.NextCubeMachine.test_bootrom_framebuffer_size/scratch/dump.ppm'> width, height = Image.open(screenshot_path).size Use a proper "with" context to avoid it. Reviewed-by: Daniel P. Berrangé Signed-off-by: Thomas Huth Message-ID: <20250829142000.62320-1-thuth@redhat.com> --- tests/functional/m68k/test_nextcube.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/functional/m68k/test_nextcube.py b/tests/functional/m68k/test_nextcube.py index 13c72bd136..c1610e5845 100755 --- a/tests/functional/m68k/test_nextcube.py +++ b/tests/functional/m68k/test_nextcube.py @@ -44,7 +44,8 @@ class NextCubeMachine(QemuSystemTest): self.check_bootrom_framebuffer(screenshot_path) from PIL import Image - width, height = Image.open(screenshot_path).size + with Image.open(screenshot_path) as image: + width, height = image.size self.assertEqual(width, 1120) self.assertEqual(height, 832) From 9f80f3695d305015f5271d946304e5cffee607c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Fri, 29 Aug 2025 15:26:14 +0100 Subject: [PATCH 0363/2396] tests/functional: enable force refresh of cached assets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If the 'QEMU_TEST_REFRESH_CACHE' environment variable is set, then ignore any existing cached asset and download a fresh copy. This can be used to selectively refresh assets if set before running a single test script. Reviewed-by: Thomas Huth Signed-off-by: Daniel P. Berrangé Reviewed-by: Richard Henderson Message-ID: <20250829142616.2633254-2-berrange@redhat.com> Signed-off-by: Thomas Huth --- docs/devel/testing/functional.rst | 3 +++ tests/functional/qemu_test/asset.py | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/docs/devel/testing/functional.rst b/docs/devel/testing/functional.rst index 3728bab6c0..fdeaebaadc 100644 --- a/docs/devel/testing/functional.rst +++ b/docs/devel/testing/functional.rst @@ -312,6 +312,9 @@ The cache is populated in the ``~/.cache/qemu/download`` directory by default, but the location can be changed by setting the ``QEMU_TEST_CACHE_DIR`` environment variable. +To force the test suite to re-download the cache, even if still valid, +set the ``QEMU_TEST_REFRESH_CACHE`` environment variable. + Skipping tests -------------- diff --git a/tests/functional/qemu_test/asset.py b/tests/functional/qemu_test/asset.py index 704b84d0ea..b5a6136d36 100644 --- a/tests/functional/qemu_test/asset.py +++ b/tests/functional/qemu_test/asset.py @@ -72,6 +72,10 @@ class Asset: return self.hash == hl.hexdigest() def valid(self): + if os.getenv("QEMU_TEST_REFRESH_CACHE", None) is not None: + self.log.info("Force refresh of asset %s", self.url) + return False + return self.cache_file.exists() and self._check(self.cache_file) def fetchable(self): From 124ab930ba38c41a86533dbfabb7a3b3b270ef98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Fri, 29 Aug 2025 15:26:15 +0100 Subject: [PATCH 0364/2396] tests/functional: fix formatting of exception args MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The catch-all exception handler forgot the placeholder for the exception details. Signed-off-by: Daniel P. Berrangé Reviewed-by: Thomas Huth Reviewed-by: Richard Henderson Message-ID: <20250829142616.2633254-3-berrange@redhat.com> Signed-off-by: Thomas Huth --- tests/functional/qemu_test/asset.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/functional/qemu_test/asset.py b/tests/functional/qemu_test/asset.py index b5a6136d36..5c74adf224 100644 --- a/tests/functional/qemu_test/asset.py +++ b/tests/functional/qemu_test/asset.py @@ -173,7 +173,7 @@ class Asset: continue except Exception as e: tmp_cache_file.unlink() - raise AssetError(self, "Unable to download: " % e) + raise AssetError(self, "Unable to download: %s" % e) if not os.path.exists(tmp_cache_file): raise AssetError(self, "Download retries exceeded", transient=True) From 335da23abec85cd2f6d10f1fe36b28a02088e723 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Fri, 29 Aug 2025 15:26:16 +0100 Subject: [PATCH 0365/2396] tests/functional: handle URLError when fetching assets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We treat most HTTP errors as non-fatal when fetching assets, but forgot to handle network level errors. This adds catching of URLError so that we retry on failure, and will ultimately trigger graceful skipping in the pre-cache task. Signed-off-by: Daniel P. Berrangé Reviewed-by: Richard Henderson Reviewed-by: Thomas Huth Message-ID: <20250829142616.2633254-4-berrange@redhat.com> Signed-off-by: Thomas Huth --- tests/functional/qemu_test/asset.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/tests/functional/qemu_test/asset.py b/tests/functional/qemu_test/asset.py index 5c74adf224..2dd32bf28d 100644 --- a/tests/functional/qemu_test/asset.py +++ b/tests/functional/qemu_test/asset.py @@ -15,7 +15,7 @@ import urllib.request from time import sleep from pathlib import Path from shutil import copyfileobj -from urllib.error import HTTPError +from urllib.error import HTTPError, URLError class AssetError(Exception): def __init__(self, asset, msg, transient=False): @@ -171,6 +171,14 @@ class Asset: raise AssetError(self, "Unable to download: " "HTTP error %d" % e.code) continue + except URLError as e: + # This is typically a network/service level error + # eg urlopen error [Errno 110] Connection timed out> + tmp_cache_file.unlink() + self.log.error("Unable to download %s: URL error %s", + self.url, e.reason) + raise AssetError(self, "Unable to download: URL error %s" % + e.reason, transient=True) except Exception as e: tmp_cache_file.unlink() raise AssetError(self, "Unable to download: %s" % e) From 4effdc3c39f93e65396b46cc8432bbb37a8219e8 Mon Sep 17 00:00:00 2001 From: Kane-Chen-AS Date: Thu, 4 Sep 2025 18:05:52 +0800 Subject: [PATCH 0366/2396] tests/functional/arm: Update test ASPEED SDK v03.02 for AST1030 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Kane-Chen-AS Reviewed-by: Cédric Le Goater Message-ID: <20250904100556.1729604-2-kane_chen@aspeedtech.com> Signed-off-by: Thomas Huth --- tests/functional/arm/test_aspeed_ast1030.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/functional/arm/test_aspeed_ast1030.py b/tests/functional/arm/test_aspeed_ast1030.py index 77037f0179..42126b514f 100755 --- a/tests/functional/arm/test_aspeed_ast1030.py +++ b/tests/functional/arm/test_aspeed_ast1030.py @@ -12,17 +12,17 @@ from qemu_test import exec_command_and_wait_for_pattern class AST1030Machine(LinuxKernelTest): - ASSET_ZEPHYR_3_00 = Asset( + ASSET_ZEPHYR_3_02 = Asset( ('https://github.com/AspeedTech-BMC' - '/zephyr/releases/download/v00.03.00/ast1030-evb-demo.zip'), - '37fe3ecd4a1b9d620971a15b96492a81093435396eeac69b6f3e384262ff555f') + '/zephyr/releases/download/v00.03.02/ast1030-evb-demo.zip'), + '1ec83caab3ddd5d09481772801be7210e222cb015ce22ec6fffb8a76956dcd4f') - def test_ast1030_zephyros_3_00(self): + def test_ast1030_zephyros_3_02(self): self.set_machine('ast1030-evb') - kernel_name = "ast1030-evb-demo/zephyr.elf" + kernel_name = "ast1030-evb-demo-3/zephyr.elf" kernel_file = self.archive_extract( - self.ASSET_ZEPHYR_3_00, member=kernel_name) + self.ASSET_ZEPHYR_3_02, member=kernel_name) self.vm.set_console() self.vm.add_args('-kernel', kernel_file, '-nographic') From bab31635d76fcbd9da8fff2c6c99906e86b449ec Mon Sep 17 00:00:00 2001 From: Kane-Chen-AS Date: Thu, 4 Sep 2025 18:05:53 +0800 Subject: [PATCH 0367/2396] tests/functional/arm: Update test ASPEED SDK v09.07 for AST2500 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Kane-Chen-AS Reviewed-by: Cédric Le Goater Message-ID: <20250904100556.1729604-3-kane_chen@aspeedtech.com> Signed-off-by: Thomas Huth --- tests/functional/arm/test_aspeed_ast2500.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/functional/arm/test_aspeed_ast2500.py b/tests/functional/arm/test_aspeed_ast2500.py index 6923fe8701..4fdd81e2f9 100755 --- a/tests/functional/arm/test_aspeed_ast2500.py +++ b/tests/functional/arm/test_aspeed_ast2500.py @@ -37,14 +37,14 @@ class AST2500Machine(AspeedTest): 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') + ASSET_SDK_V907_AST2500 = Asset( + 'https://github.com/AspeedTech-BMC/openbmc/releases/download/v09.07/ast2500-default-obmc.tar.gz', + 'd52bcc279a37c8d7679b3e4ef22cc77c36f0f6624c687b37334f798828afb077') def test_arm_ast2500_evb_sdk(self): self.set_machine('ast2500-evb') - self.archive_extract(self.ASSET_SDK_V906_AST2500) + self.archive_extract(self.ASSET_SDK_V907_AST2500) self.do_test_arm_aspeed_sdk_start( self.scratch_file("ast2500-default", "image-bmc")) From f57d0872694bc0de03bc6654db72f2ec7281f1ae Mon Sep 17 00:00:00 2001 From: Kane-Chen-AS Date: Thu, 4 Sep 2025 18:05:54 +0800 Subject: [PATCH 0368/2396] tests/functional/arm: Update test ASPEED SDK v09.07 for AST2600 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Kane-Chen-AS Reviewed-by: Cédric Le Goater Message-ID: <20250904100556.1729604-4-kane_chen@aspeedtech.com> Signed-off-by: Thomas Huth --- tests/functional/arm/test_aspeed_ast2600.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/functional/arm/test_aspeed_ast2600.py b/tests/functional/arm/test_aspeed_ast2600.py index fdae4c939d..129695ca4e 100755 --- a/tests/functional/arm/test_aspeed_ast2600.py +++ b/tests/functional/arm/test_aspeed_ast2600.py @@ -97,14 +97,14 @@ class AST2600Machine(AspeedTest): 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') + ASSET_SDK_V907_AST2600 = Asset( + 'https://github.com/AspeedTech-BMC/openbmc/releases/download/v09.07/ast2600-default-obmc.tar.gz', + 'cb6c08595bcbba1672ce716b068ba4e48eda1ed9abe78a07b30392ba2278feba') def test_arm_ast2600_evb_sdk(self): self.set_machine('ast2600-evb') - self.archive_extract(self.ASSET_SDK_V906_AST2600) + self.archive_extract(self.ASSET_SDK_V907_AST2600) self.vm.add_args('-device', 'tmp105,bus=aspeed.i2c.bus.5,address=0x4d,id=tmp-test') From 8cdcabe129cdd3270fd5c21708833464396fbf9b Mon Sep 17 00:00:00 2001 From: Kane-Chen-AS Date: Thu, 4 Sep 2025 18:05:55 +0800 Subject: [PATCH 0369/2396] tests/functional/arm: Update test ASPEED SDK v09.07 for AST2700 vbootrom MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Kane-Chen-AS Reviewed-by: Cédric Le Goater Message-ID: <20250904100556.1729604-5-kane_chen@aspeedtech.com> Signed-off-by: Thomas Huth --- tests/functional/aarch64/test_aspeed_ast2700.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tests/functional/aarch64/test_aspeed_ast2700.py b/tests/functional/aarch64/test_aspeed_ast2700.py index d02dc7991c..8a08bc4688 100755 --- a/tests/functional/aarch64/test_aspeed_ast2700.py +++ b/tests/functional/aarch64/test_aspeed_ast2700.py @@ -54,6 +54,10 @@ class AST2x00MachineSDK(QemuSystemTest): 'https://github.com/AspeedTech-BMC/openbmc/releases/download/v09.06/ast2700-default-obmc.tar.gz', 'f1d53e0be8a404ecce3e105f72bc50fa4e090ad13160ffa91b10a6e0233a9dc6') + ASSET_SDK_V907_AST2700A1_VBOOROM = Asset( + 'https://github.com/AspeedTech-BMC/openbmc/releases/download/v09.07/ast2700-default-obmc.tar.gz', + '6e9e0c4b13e0f26040eca3f4a7f17cf09fc0f5c37c820500ff79370cc3c44add') + 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 ', @@ -127,10 +131,10 @@ class AST2x00MachineSDK(QemuSystemTest): self.verify_openbmc_boot_and_login('ast2700-default') self.do_ast2700_i2c_test() - def test_aarch64_ast2700a1_evb_sdk_vbootrom_v09_06(self): + def test_aarch64_ast2700a1_evb_sdk_vbootrom_v09_07(self): self.set_machine('ast2700a1-evb') - self.archive_extract(self.ASSET_SDK_V906_AST2700A1) + self.archive_extract(self.ASSET_SDK_V907_AST2700A1_VBOOROM) self.start_ast2700_test_vbootrom('ast2700-default') self.verify_vbootrom_firmware_flow() self.verify_openbmc_boot_and_login('ast2700-default') From 8e22db01a1f45b108f55860514f8896952af550e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Mon, 8 Sep 2025 20:08:58 +0100 Subject: [PATCH 0370/2396] gitlab: replace avocado results files with meson results files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 'results.xml' file and 'test-results' directory were both outputs of the avovcado test runner. Since we're now using meson with the new functional test framework, we must reference meson results files as the CI artifacts. Signed-off-by: Daniel P. Berrangé Reviewed-by: Alex Bennée Message-ID: <20250908190901.3571859-2-berrange@redhat.com> Signed-off-by: Thomas Huth --- .gitlab-ci.d/buildtest-template.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.gitlab-ci.d/buildtest-template.yml b/.gitlab-ci.d/buildtest-template.yml index 038c3c9540..4672298214 100644 --- a/.gitlab-ci.d/buildtest-template.yml +++ b/.gitlab-ci.d/buildtest-template.yml @@ -104,11 +104,10 @@ when: always expire_in: 7 days paths: - - build/tests/results/latest/results.xml - - build/tests/results/latest/test-results + - build/meson-logs/testlog.txt - build/tests/functional/*/*/*.log reports: - junit: build/tests/results/latest/results.xml + junit: build/meson-logs/testlog.junit.xml before_script: - export QEMU_TEST_ALLOW_UNTRUSTED_CODE=1 - export QEMU_TEST_CACHE_DIR=${CI_PROJECT_DIR}/functional-cache From ce1a7cd4cf090e21e0929566400fb759752e28c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Mon, 8 Sep 2025 20:08:59 +0100 Subject: [PATCH 0371/2396] gitlab: always include entire of meson-logs directory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There are files besides testlog.txt that may be useful as published CI artifacts. Signed-off-by: Daniel P. Berrangé Reviewed-by: Alex Bennée Message-ID: <20250908190901.3571859-3-berrange@redhat.com> Signed-off-by: Thomas Huth --- .gitlab-ci.d/buildtest-template.yml | 4 ++-- .gitlab-ci.d/buildtest.yml | 2 +- .gitlab-ci.d/crossbuild-template.yml | 2 +- .gitlab-ci.d/windows.yml | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.gitlab-ci.d/buildtest-template.yml b/.gitlab-ci.d/buildtest-template.yml index 4672298214..4cc5b20790 100644 --- a/.gitlab-ci.d/buildtest-template.yml +++ b/.gitlab-ci.d/buildtest-template.yml @@ -88,7 +88,7 @@ when: always expire_in: 7 days paths: - - build/meson-logs/testlog.txt + - build/meson-logs reports: junit: build/meson-logs/testlog.junit.xml @@ -104,7 +104,7 @@ when: always expire_in: 7 days paths: - - build/meson-logs/testlog.txt + - build/meson-logs - build/tests/functional/*/*/*.log reports: junit: build/meson-logs/testlog.junit.xml diff --git a/.gitlab-ci.d/buildtest.yml b/.gitlab-ci.d/buildtest.yml index d888a60063..778289267f 100644 --- a/.gitlab-ci.d/buildtest.yml +++ b/.gitlab-ci.d/buildtest.yml @@ -613,7 +613,7 @@ gcov: when: always expire_in: 2 days paths: - - build/meson-logs/testlog.txt + - build/meson-logs reports: junit: build/meson-logs/testlog.junit.xml coverage_report: diff --git a/.gitlab-ci.d/crossbuild-template.yml b/.gitlab-ci.d/crossbuild-template.yml index 303943f818..7e70376cfc 100644 --- a/.gitlab-ci.d/crossbuild-template.yml +++ b/.gitlab-ci.d/crossbuild-template.yml @@ -128,6 +128,6 @@ when: always expire_in: 7 days paths: - - build/meson-logs/testlog.txt + - build/meson-logs reports: junit: build/meson-logs/testlog.junit.xml diff --git a/.gitlab-ci.d/windows.yml b/.gitlab-ci.d/windows.yml index beac39e5bd..f14e9ca134 100644 --- a/.gitlab-ci.d/windows.yml +++ b/.gitlab-ci.d/windows.yml @@ -24,7 +24,7 @@ msys2-64bit: name: "$CI_JOB_NAME-$CI_COMMIT_REF_SLUG" expire_in: 7 days paths: - - build/meson-logs/testlog.txt + - build/meson-logs reports: junit: "build/meson-logs/testlog.junit.xml" before_script: From c65b3d49b96f2d2da30dba5fb96e07f79e73bd88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Mon, 8 Sep 2025 20:09:00 +0100 Subject: [PATCH 0372/2396] gitlab: include all junit XML files from meson MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The junit XML file produced by meson does not always have the name 'testlog.junit.xml' - in the case of 'make check-functional' there is a 'testlog-thorough.junit.xml' file too. Improve CI debugging robustness by capturing all junit files that meson produces. Signed-off-by: Daniel P. Berrangé Reviewed-by: Alex Bennée Message-ID: <20250908190901.3571859-4-berrange@redhat.com> Signed-off-by: Thomas Huth --- .gitlab-ci.d/buildtest-template.yml | 4 ++-- .gitlab-ci.d/buildtest.yml | 2 +- .gitlab-ci.d/crossbuild-template.yml | 2 +- .gitlab-ci.d/custom-runners.yml | 2 +- .gitlab-ci.d/windows.yml | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.gitlab-ci.d/buildtest-template.yml b/.gitlab-ci.d/buildtest-template.yml index 4cc5b20790..308490a35a 100644 --- a/.gitlab-ci.d/buildtest-template.yml +++ b/.gitlab-ci.d/buildtest-template.yml @@ -90,7 +90,7 @@ paths: - build/meson-logs reports: - junit: build/meson-logs/testlog.junit.xml + junit: build/meson-logs/*.junit.xml .functional_test_job_template: extends: .common_test_job_template @@ -107,7 +107,7 @@ - build/meson-logs - build/tests/functional/*/*/*.log reports: - junit: build/meson-logs/testlog.junit.xml + junit: build/meson-logs/*.junit.xml before_script: - export QEMU_TEST_ALLOW_UNTRUSTED_CODE=1 - export QEMU_TEST_CACHE_DIR=${CI_PROJECT_DIR}/functional-cache diff --git a/.gitlab-ci.d/buildtest.yml b/.gitlab-ci.d/buildtest.yml index 778289267f..83c2867295 100644 --- a/.gitlab-ci.d/buildtest.yml +++ b/.gitlab-ci.d/buildtest.yml @@ -615,7 +615,7 @@ gcov: paths: - build/meson-logs reports: - junit: build/meson-logs/testlog.junit.xml + junit: build/meson-logs/*.junit.xml coverage_report: coverage_format: cobertura path: build/coverage.xml diff --git a/.gitlab-ci.d/crossbuild-template.yml b/.gitlab-ci.d/crossbuild-template.yml index 7e70376cfc..58136d06e4 100644 --- a/.gitlab-ci.d/crossbuild-template.yml +++ b/.gitlab-ci.d/crossbuild-template.yml @@ -130,4 +130,4 @@ paths: - build/meson-logs reports: - junit: build/meson-logs/testlog.junit.xml + junit: build/meson-logs/*.junit.xml diff --git a/.gitlab-ci.d/custom-runners.yml b/.gitlab-ci.d/custom-runners.yml index 1aa3c60efe..2d493f70f7 100644 --- a/.gitlab-ci.d/custom-runners.yml +++ b/.gitlab-ci.d/custom-runners.yml @@ -26,7 +26,7 @@ - build/build.ninja - build/meson-logs reports: - junit: build/meson-logs/testlog.junit.xml + junit: build/meson-logs/*.junit.xml include: - local: '/.gitlab-ci.d/custom-runners/ubuntu-22.04-s390x.yml' diff --git a/.gitlab-ci.d/windows.yml b/.gitlab-ci.d/windows.yml index f14e9ca134..1e6a01bd9a 100644 --- a/.gitlab-ci.d/windows.yml +++ b/.gitlab-ci.d/windows.yml @@ -26,7 +26,7 @@ msys2-64bit: paths: - build/meson-logs reports: - junit: "build/meson-logs/testlog.junit.xml" + junit: build/meson-logs/*.junit.xml before_script: - Write-Output "Acquiring msys2.exe installer at $(Get-Date -Format u)" - If ( !(Test-Path -Path msys64\var\cache ) ) { From f547894f68f9b18e6f3e4404b0a2e8ad013191f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Mon, 8 Sep 2025 20:09:01 +0100 Subject: [PATCH 0373/2396] gitlab: prevent duplicated meson log artifacts in test jobs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The build jobs will populate build/meson-logs/ with various files that are added as artifacts. The test jobs preserve the state of the build jobs, so we must delete any pre-existing logs to prevent confusion from duplicate artifacts. Signed-off-by: Daniel P. Berrangé Reviewed-by: Alex Bennée Tested-by: Alex Bennée Message-ID: <20250908190901.3571859-5-berrange@redhat.com> Signed-off-by: Thomas Huth --- .gitlab-ci.d/buildtest-template.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.gitlab-ci.d/buildtest-template.yml b/.gitlab-ci.d/buildtest-template.yml index 308490a35a..d866cb12bb 100644 --- a/.gitlab-ci.d/buildtest-template.yml +++ b/.gitlab-ci.d/buildtest-template.yml @@ -83,6 +83,10 @@ .native_test_job_template: extends: .common_test_job_template + before_script: + # Prevent logs from the build job that run earlier + # from being duplicated in the test job artifacts + - rm -f build/meson-logs/* artifacts: name: "$CI_JOB_NAME-$CI_COMMIT_REF_SLUG" when: always @@ -111,6 +115,9 @@ before_script: - export QEMU_TEST_ALLOW_UNTRUSTED_CODE=1 - export QEMU_TEST_CACHE_DIR=${CI_PROJECT_DIR}/functional-cache + # Prevent logs from the build job that run earlier + # from being duplicated in the test job artifacts + - rm -f build/meson-logs/* after_script: - cd build - du -chs ${CI_PROJECT_DIR}/*-cache From 595ba81145e76dbbcae700fce39253509172f9ff Mon Sep 17 00:00:00 2001 From: John Levon Date: Wed, 3 Sep 2025 22:19:29 +0200 Subject: [PATCH 0374/2396] tests/functional: return output from cmd.py helpers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Tests might want to look at the whole output from a command execution, as well as just logging it. Add support for this. Signed-off-by: John Levon Reviewed-by: Daniel P. Berrangé Message-ID: <20250903201931.168317-2-john.levon@nutanix.com> Signed-off-by: Thomas Huth --- tests/functional/qemu_test/cmd.py | 53 +++++++++++++++++++++++++------ 1 file changed, 44 insertions(+), 9 deletions(-) diff --git a/tests/functional/qemu_test/cmd.py b/tests/functional/qemu_test/cmd.py index dc5f422b77..c19dfc577f 100644 --- a/tests/functional/qemu_test/cmd.py +++ b/tests/functional/qemu_test/cmd.py @@ -45,6 +45,9 @@ def is_readable_executable_file(path): # If end of line is seen, with neither @success or @failure # return False # +# In both cases, also return the contents of the line (in bytes) +# up to that point. +# # If @failure is seen, then mark @test as failed def _console_read_line_until_match(test, vm, success, failure): msg = bytes([]) @@ -76,10 +79,23 @@ def _console_read_line_until_match(test, vm, success, failure): except: console_logger.debug(msg) - return done + return done, msg def _console_interaction(test, success_message, failure_message, send_string, keep_sending=False, vm=None): + """ + Interact with the console until either message is seen. + + :param success_message: if this message appears, finish interaction + :param failure_message: if this message appears, test fails + :param send_string: a string to send to the console before trying + to read a new line + :param keep_sending: keep sending the send string each time + :param vm: the VM to interact with + + :return: The collected output (in bytes form). + """ + assert not keep_sending or send_string assert success_message or send_string @@ -101,6 +117,8 @@ def _console_interaction(test, success_message, failure_message, if failure_message is not None: failure_message_b = failure_message.encode() + out = bytes([]) + while True: if send_string: vm.console_socket.sendall(send_string.encode()) @@ -113,11 +131,17 @@ def _console_interaction(test, success_message, failure_message, break continue - if _console_read_line_until_match(test, vm, - success_message_b, - failure_message_b): + done, line = _console_read_line_until_match(test, vm, + success_message_b, + failure_message_b) + + out += line + + if done: break + return out + def interrupt_interactive_console_until_pattern(test, success_message, failure_message=None, interrupt_string='\r'): @@ -140,10 +164,12 @@ def interrupt_interactive_console_until_pattern(test, success_message, :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 + + :return: The collected output (in bytes form). """ assert success_message - _console_interaction(test, success_message, failure_message, - interrupt_string, True) + return _console_interaction(test, success_message, failure_message, + interrupt_string, True) def wait_for_console_pattern(test, success_message, failure_message=None, vm=None): @@ -155,9 +181,12 @@ def wait_for_console_pattern(test, success_message, failure_message=None, :type test: :class:`qemu_test.QemuSystemTest` :param success_message: if this message appears, test succeeds :param failure_message: if this message appears, test fails + + :return: The collected output (in bytes form). """ assert success_message - _console_interaction(test, success_message, failure_message, None, vm=vm) + return _console_interaction(test, success_message, failure_message, + None, vm=vm) def exec_command(test, command): """ @@ -168,8 +197,10 @@ def exec_command(test, command): :type test: :class:`qemu_test.QemuSystemTest` :param command: the command to send :type command: str + + :return: The collected output (in bytes form). """ - _console_interaction(test, None, None, command + '\r') + return _console_interaction(test, None, None, command + '\r') def exec_command_and_wait_for_pattern(test, command, success_message, failure_message=None): @@ -184,9 +215,13 @@ def exec_command_and_wait_for_pattern(test, command, :param command: the command to send :param success_message: if this message appears, test succeeds :param failure_message: if this message appears, test fails + + :return: The collected output (in bytes form). """ assert success_message - _console_interaction(test, success_message, failure_message, command + '\r') + + return _console_interaction(test, success_message, failure_message, + command + '\r') def get_qemu_img(test): test.log.debug('Looking for and selecting a qemu-img binary') From ba87a01e1af04599e1952cacfb7eb25f06e15da5 Mon Sep 17 00:00:00 2001 From: John Levon Date: Wed, 3 Sep 2025 22:19:30 +0200 Subject: [PATCH 0375/2396] tests/functional: add vm param to cmd.py helpers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extend the "vm" parameter of wait_for_console_pattern() to all the other utility functions; this allows them to be used on a VM other than test.vm. Signed-off-by: John Levon Reviewed-by: Daniel P. Berrangé Message-ID: <20250903201931.168317-3-john.levon@nutanix.com> Signed-off-by: Thomas Huth --- tests/functional/qemu_test/cmd.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/tests/functional/qemu_test/cmd.py b/tests/functional/qemu_test/cmd.py index c19dfc577f..8069c89730 100644 --- a/tests/functional/qemu_test/cmd.py +++ b/tests/functional/qemu_test/cmd.py @@ -144,7 +144,8 @@ def _console_interaction(test, success_message, failure_message, def interrupt_interactive_console_until_pattern(test, success_message, failure_message=None, - interrupt_string='\r'): + interrupt_string='\r', + vm=None): """ 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: @@ -164,12 +165,13 @@ def interrupt_interactive_console_until_pattern(test, success_message, :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 + :param vm: VM to use :return: The collected output (in bytes form). """ assert success_message return _console_interaction(test, success_message, failure_message, - interrupt_string, True) + interrupt_string, True, vm=vm) def wait_for_console_pattern(test, success_message, failure_message=None, vm=None): @@ -181,6 +183,7 @@ def wait_for_console_pattern(test, success_message, failure_message=None, :type test: :class:`qemu_test.QemuSystemTest` :param success_message: if this message appears, test succeeds :param failure_message: if this message appears, test fails + :param vm: VM to use :return: The collected output (in bytes form). """ @@ -188,7 +191,7 @@ def wait_for_console_pattern(test, success_message, failure_message=None, return _console_interaction(test, success_message, failure_message, None, vm=vm) -def exec_command(test, command): +def exec_command(test, command, vm=None): """ Send a command to a console (appending CRLF characters), while logging the content. @@ -196,14 +199,16 @@ def exec_command(test, command): :param test: a test containing a VM. :type test: :class:`qemu_test.QemuSystemTest` :param command: the command to send + :param vm: VM to use :type command: str :return: The collected output (in bytes form). """ - return _console_interaction(test, None, None, command + '\r') + return _console_interaction(test, None, None, command + '\r', vm=vm) def exec_command_and_wait_for_pattern(test, command, - success_message, failure_message=None): + success_message, failure_message=None, + vm=None): """ Send a command to a console (appending CRLF characters), then wait for success_message to appear on the console, while logging the. @@ -215,13 +220,14 @@ def exec_command_and_wait_for_pattern(test, command, :param command: the command to send :param success_message: if this message appears, test succeeds :param failure_message: if this message appears, test fails + :param vm: VM to use :return: The collected output (in bytes form). """ assert success_message return _console_interaction(test, success_message, failure_message, - command + '\r') + command + '\r', vm=vm) def get_qemu_img(test): test.log.debug('Looking for and selecting a qemu-img binary') From fb352b3c85a990ba81e41e4e8c7eb53ccc3059a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Mon, 8 Sep 2025 14:57:19 +0100 Subject: [PATCH 0376/2396] tests/functional: fix infinite loop on console EOF MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 'recv' method will return an empty byte array, not None, when the socket has EOF. Signed-off-by: Daniel P. Berrangé Reviewed-by: Thomas Huth Reviewed-by: Alex Bennée Message-ID: <20250908135722.3375580-2-berrange@redhat.com> Signed-off-by: Thomas Huth --- tests/functional/qemu_test/cmd.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/functional/qemu_test/cmd.py b/tests/functional/qemu_test/cmd.py index 8069c89730..f544566245 100644 --- a/tests/functional/qemu_test/cmd.py +++ b/tests/functional/qemu_test/cmd.py @@ -54,7 +54,7 @@ def _console_read_line_until_match(test, vm, success, failure): done = False while True: c = vm.console_socket.recv(1) - if c is None: + if not c: done = True test.fail( f"EOF in console, expected '{success}'") From e5381b041037f3918163f2b7f82415aee80cd64e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Mon, 8 Sep 2025 14:57:20 +0100 Subject: [PATCH 0377/2396] tests/functional: avoid duplicate messages on failures MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In some scenarios the same tests is mentioned in both the 'res.results.errors' and 'res.results.failures' array returned by unittest.main(). This was seen when the 'tearDown' method raised an exception. In such a case, we printed out the same information about where to find a log file twice for each test. Track which tests we have already reported on, to avoid the duplication. Signed-off-by: Daniel P. Berrangé Reviewed-by: Thomas Huth Message-ID: <20250908135722.3375580-3-berrange@redhat.com> Signed-off-by: Thomas Huth --- tests/functional/qemu_test/testcase.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/functional/qemu_test/testcase.py b/tests/functional/qemu_test/testcase.py index fbeb171058..82a7724404 100644 --- a/tests/functional/qemu_test/testcase.py +++ b/tests/functional/qemu_test/testcase.py @@ -251,13 +251,14 @@ class QemuBaseTest(unittest.TestCase): test_output_log = pycotap.LogMode.LogToError) res = unittest.main(module = None, testRunner = tr, exit = False, argv=[sys.argv[0], path] + sys.argv[1:]) + failed = {} for (test, message) in res.result.errors + res.result.failures: - - if hasattr(test, "log_filename"): + if hasattr(test, "log_filename") and not test.id() in failed: 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) + failed[test.id()] = True sys.exit(not res.result.wasSuccessful()) From 2ee187dcb55aee7f70b514db772090649235de70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Mon, 8 Sep 2025 14:57:21 +0100 Subject: [PATCH 0378/2396] tests/functional: avoid tearDown failure when QEMU dies MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In a QEMU process under test dies unexpectedly, the 'shutdown' method may well raise an exception. This causes the tearDown method to fail, which means any later cleanup code fails to get run. Most notably the log handlers don't get removed so the base.log file from an earlier test will get polluted with messages from any subsequent tests. The tearDown failure also results in pages of exceptions printed on the console, which obscures the real failure message / trace printed by the test. Ignore any shutdown failures in the tearDown method, since any test which cares about clean shutdown should have already cleaned up any running VMs. The tearDown method is just there as a safety net to cleanup resources. The base.log file will still containing log messages from the failed 'vm.shutdown' call too. Signed-off-by: Daniel P. Berrangé Reviewed-by: Thomas Huth Message-ID: <20250908135722.3375580-4-berrange@redhat.com> Signed-off-by: Thomas Huth --- tests/functional/qemu_test/testcase.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/functional/qemu_test/testcase.py b/tests/functional/qemu_test/testcase.py index 82a7724404..faa0a4f0db 100644 --- a/tests/functional/qemu_test/testcase.py +++ b/tests/functional/qemu_test/testcase.py @@ -404,7 +404,10 @@ class QemuSystemTest(QemuBaseTest): def tearDown(self): for vm in self._vms.values(): - vm.shutdown() + try: + vm.shutdown() + except Exception as ex: + self.log.error("Failed to teardown VM: %s" % ex) logging.getLogger('console').removeHandler(self._console_log_fh) self._console_log_fh.close() super().tearDown() From 2fc170bcdc4d2f05534c68572b4f72a7d18c2119 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Mon, 8 Sep 2025 14:57:22 +0100 Subject: [PATCH 0379/2396] tests/functional: purge scratch dir on test startup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The test suite purges the scratch dir in the tearDown method, but if python crashes (or is non-gracefully killed) this won't get run. Also the user can set QEMU_TEST_KEEP_SCRATCH to disable cleanup. Purging the scratch dir on startup ensures that tests always run from a clean state. Reported-by: Peter Maydell Signed-off-by: Daniel P. Berrangé Reviewed-by: Alex Bennée Message-ID: <20250908135722.3375580-5-berrange@redhat.com> Signed-off-by: Thomas Huth --- tests/functional/qemu_test/testcase.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/functional/qemu_test/testcase.py b/tests/functional/qemu_test/testcase.py index faa0a4f0db..2c0abde395 100644 --- a/tests/functional/qemu_test/testcase.py +++ b/tests/functional/qemu_test/testcase.py @@ -205,6 +205,10 @@ class QemuBaseTest(unittest.TestCase): self.outputdir = self.build_file('tests', 'functional', self.arch, self.id()) self.workdir = os.path.join(self.outputdir, 'scratch') + if os.path.exists(self.workdir): + # Purge as safety net in case of unclean termination of + # previous test, or use of QEMU_TEST_KEEP_SCRATCH + shutil.rmtree(self.workdir) os.makedirs(self.workdir, exist_ok=True) self.log_filename = self.log_file('base.log') From 769acb2a1e47b97ada8e0db6ff73e303b23764d8 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 9 Sep 2025 14:37:47 +0200 Subject: [PATCH 0380/2396] tests/functional/aarch64: Fix assets of test_hotplug_pci The old bookworm URLs don't work anymore, resulting in a 404 error now. Let's update the test to Debian Trixie to get it going again. Signed-off-by: Thomas Huth Signed-off-by: Peter Maydell --- tests/functional/aarch64/test_hotplug_pci.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/functional/aarch64/test_hotplug_pci.py b/tests/functional/aarch64/test_hotplug_pci.py index 0c67991b89..bf67720431 100755 --- a/tests/functional/aarch64/test_hotplug_pci.py +++ b/tests/functional/aarch64/test_hotplug_pci.py @@ -15,14 +15,14 @@ from qemu_test import BUILD_DIR class HotplugPCI(LinuxKernelTest): ASSET_KERNEL = Asset( - ('https://ftp.debian.org/debian/dists/bookworm/main/installer-arm64/' - '20230607+deb12u11/images/netboot/debian-installer/arm64/linux'), - 'd92a60392ce1e379ca198a1a820899f8f0d39a62d047c41ab79492f81541a9d9') + ('https://ftp.debian.org/debian/dists/trixie/main/installer-arm64/' + '20250803/images/netboot/debian-installer/arm64/linux'), + '93a6e4f9627d759375d28f863437a86a0659e125792a435f8e526dda006b7d5e') ASSET_INITRD = Asset( - ('https://ftp.debian.org/debian/dists/bookworm/main/installer-arm64/' - '20230607+deb12u11/images/netboot/debian-installer/arm64/initrd.gz'), - '9f817f76951f3237bca8216bee35267bfb826815687f4b2fcdd5e6c2a917790c') + ('https://ftp.debian.org/debian/dists/trixie/main/installer-arm64/' + '20250803/images/netboot/debian-installer/arm64/initrd.gz'), + 'f6c78af7078ca67638ef3a50c926cd3c1485673243f8b37952e6bd854d6ba007') def test_hotplug_pci(self): From c75847f9eb8fb09dde0c0f4fa4710c6bde11f5a9 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Wed, 3 Sep 2025 10:29:32 -0400 Subject: [PATCH 0381/2396] memory: Fix addr/len for flatview_access_allowed() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit flatview_access_allowed() should pass in the address offset of the memory region, rather than the global address space. Shouldn't be a major issue yet, since the addr is only used in an error log. Cc: Philippe Mathieu-Daudé Fixes: 3ab6fdc91b ("softmmu/physmem: Introduce MemTxAttrs::memory field and MEMTX_ACCESS_ERROR") Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: David Hildenbrand Link: https://lore.kernel.org/r/20250903142932.1038765-1-peterx@redhat.com Signed-off-by: Peter Xu --- system/physmem.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/system/physmem.c b/system/physmem.c index 311011156c..ddd58e9eb8 100644 --- a/system/physmem.c +++ b/system/physmem.c @@ -3027,7 +3027,7 @@ static MemTxResult flatview_write(FlatView *fv, hwaddr addr, MemTxAttrs attrs, l = len; mr = flatview_translate(fv, addr, &mr_addr, &l, true, attrs); - if (!flatview_access_allowed(mr, attrs, addr, len)) { + if (!flatview_access_allowed(mr, attrs, mr_addr, l)) { return MEMTX_ACCESS_ERROR; } return flatview_write_continue(fv, addr, attrs, buf, len, @@ -3118,7 +3118,7 @@ static MemTxResult flatview_read(FlatView *fv, hwaddr addr, l = len; mr = flatview_translate(fv, addr, &mr_addr, &l, false, attrs); - if (!flatview_access_allowed(mr, attrs, addr, len)) { + if (!flatview_access_allowed(mr, attrs, mr_addr, l)) { return MEMTX_ACCESS_ERROR; } return flatview_read_continue(fv, addr, attrs, buf, len, From ac7a892fd37ce4427d390ca8556203c9a2eb9d38 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Thu, 21 Aug 2025 12:59:02 -0400 Subject: [PATCH 0382/2396] memory: Fix leaks due to owner-shared MRs circular references MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, QEMU refcounts the MR by always taking it from the owner. It's common that one object will have multiple MR objects embeded in the object itself. All the MRs in this case share the same lifespan of the owner object. It's also common that in the instance_init() of an object, MR A can be a container of MR B, C, D, by using memory_region_add_subregion*() set of memory region APIs. Now we have a circular reference issue, as when adding subregions for MR A, we essentially incremented the owner's refcount within the instance_init(), meaning the object will be self-boosted and its refcount can never go down to zero if the MRs won't get detached properly before object's finalize(). Delete subregions within object's finalize() won't work either, because finalize() will be invoked only if the refcount goes to zero first. What is worse, object_finalize() will do object_property_del_all() first before object_deinit(). Since embeded MRs will be properties of the owner object, it means they'll be freed _before_ the owner's finalize(). To fix that, teach memory API to stop refcount on MRs that share the same owner. Because if they share the lifecycle of the owner, then they share the same lifecycle between themselves, hence the refcount doesn't help but only introduce troubles. Meanwhile, allow auto-detachments of MRs during finalize() of MRs even against its container, as long as they belong to the same owner. The latter is needed because now it's possible to have MRs' finalize() happen in any order when they share the same lifespan with a same owner. In this case, we should allow finalize() to happen in any order of either the parent or child MR. Loose the mr->container check in MR's finalize() to allow auto-detach. Double check it shares the same owner. Proper document this behavior in code. This patch is heavily based on the work done by Akihiko Odaki: https://lore.kernel.org/r/CAFEAcA8DV40fGsci76r4yeP1P-SP_QjNRDD2OzPxjx5wRs0GEg@mail.gmail.com Cc: Akihiko Odaki Cc: Paolo Bonzini Reviewed-by: Clément Mathieu--Drif Reviewed-by: Peter Maydell Tested-by: Peter Maydell Link: https://lore.kernel.org/r/20250826221750.285242-1-peterx@redhat.com Signed-off-by: Peter Xu --- docs/devel/memory.rst | 7 +++++-- system/memory.c | 46 ++++++++++++++++++++++++++++++++++--------- 2 files changed, 42 insertions(+), 11 deletions(-) diff --git a/docs/devel/memory.rst b/docs/devel/memory.rst index 57fb2aec76..42d3ca29c4 100644 --- a/docs/devel/memory.rst +++ b/docs/devel/memory.rst @@ -158,8 +158,11 @@ ioeventfd) can be changed during the region lifecycle. They take effect as soon as the region is made visible. This can be immediately, later, or never. -Destruction of a memory region happens automatically when the owner -object dies. +Destruction of a memory region happens automatically when the owner object +dies. When there are multiple memory regions under the same owner object, +the memory API will guarantee all memory regions will be properly detached +and finalized one by one. The order in which memory regions will be +finalized is not guaranteed. If however the memory region is part of a dynamically allocated data structure, you should call object_unparent() to destroy the memory region diff --git a/system/memory.c b/system/memory.c index 44701c465c..cf8cad6961 100644 --- a/system/memory.c +++ b/system/memory.c @@ -1796,16 +1796,37 @@ static void memory_region_finalize(Object *obj) { MemoryRegion *mr = MEMORY_REGION(obj); - assert(!mr->container); - - /* We know the region is not visible in any address space (it - * does not have a container and cannot be a root either because - * it has no references, so we can blindly clear mr->enabled. - * memory_region_set_enabled instead could trigger a transaction - * and cause an infinite loop. + /* + * Each memory region (that can be freed) must have an owner, and it + * always has the same lifecycle of its owner. It means when reaching + * here, the memory region's owner's refcount is zero. + * + * Here it is possible that the MR has: + * + * (1) mr->container set, which means this MR is a subregion of a + * container MR. In this case they must share the same owner as the + * container (otherwise the container should have kept a refcount + * of this MR's owner). + * + * (2) mr->subregions non-empty, which means this MR is a container of + * one or more other MRs (which might have the the owner as this + * MR, or a different owner). + * + * We know the MR, or any MR that is attached to this one as either + * container or children, is not visible in any address space, because + * otherwise the address space should have taken at least one refcount + * of this MR's owner. So we can blindly clear mr->enabled. + * + * memory_region_set_enabled instead could trigger a transaction and + * cause an infinite loop. */ mr->enabled = false; memory_region_transaction_begin(); + if (mr->container) { + /* Must share the owner; see above comments */ + assert(mr->container->owner == mr->owner); + memory_region_del_subregion(mr->container, mr); + } while (!QTAILQ_EMPTY(&mr->subregions)) { MemoryRegion *subregion = QTAILQ_FIRST(&mr->subregions); memory_region_del_subregion(mr, subregion); @@ -2640,7 +2661,10 @@ static void memory_region_update_container_subregions(MemoryRegion *subregion) memory_region_transaction_begin(); - memory_region_ref(subregion); + if (mr->owner != subregion->owner) { + memory_region_ref(subregion); + } + QTAILQ_FOREACH(other, &mr->subregions, subregions_link) { if (subregion->priority >= other->priority) { QTAILQ_INSERT_BEFORE(other, subregion, subregions_link); @@ -2698,7 +2722,11 @@ void memory_region_del_subregion(MemoryRegion *mr, assert(alias->mapped_via_alias >= 0); } QTAILQ_REMOVE(&mr->subregions, subregion, subregions_link); - memory_region_unref(subregion); + + if (mr->owner != subregion->owner) { + memory_region_unref(subregion); + } + memory_region_update_pending |= mr->enabled && subregion->enabled; memory_region_transaction_commit(); } From 2d26741fc5170e51621eb365a34460fadb5f969e Mon Sep 17 00:00:00 2001 From: John Snow Date: Fri, 22 Jul 2022 14:23:37 -0400 Subject: [PATCH 0383/2396] python: backport 'Change error classes to have better repr methods' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit By passing all of the arguments to the base class and overriding the __str__ method when we want a different "human readable" message that isn't just printing the list of arguments, we can ensure that all custom error classes have a reasonable __repr__ implementation. In the case of ExecuteError, the pseudo-field that isn't actually correlated to an input argument can be re-imagined as a read-only property; this forces consistency in the class and makes the repr output more obviously correct. Signed-off-by: John Snow cherry picked from commit python-qemu-qmp@afdb7893f3b34212da4259b7202973f9a8cb85b3 Reviewed-by: Daniel P. Berrangé --- python/qemu/qmp/error.py | 7 +++++-- python/qemu/qmp/message.py | 12 ++++++------ python/qemu/qmp/protocol.py | 7 +++++-- python/qemu/qmp/qmp_client.py | 20 +++++++++++++------- 4 files changed, 29 insertions(+), 17 deletions(-) diff --git a/python/qemu/qmp/error.py b/python/qemu/qmp/error.py index 24ba4d5054..c87b078f62 100644 --- a/python/qemu/qmp/error.py +++ b/python/qemu/qmp/error.py @@ -44,7 +44,10 @@ class ProtocolError(QMPError): :param error_message: Human-readable string describing the error. """ - def __init__(self, error_message: str): - super().__init__(error_message) + def __init__(self, error_message: str, *args: object): + super().__init__(error_message, *args) #: Human-readable error message, without any prefix. self.error_message: str = error_message + + def __str__(self) -> str: + return self.error_message diff --git a/python/qemu/qmp/message.py b/python/qemu/qmp/message.py index f76ccc9074..c2e9dd0dd5 100644 --- a/python/qemu/qmp/message.py +++ b/python/qemu/qmp/message.py @@ -178,15 +178,15 @@ class DeserializationError(ProtocolError): :param raw: The raw `bytes` that prompted the failure. """ def __init__(self, error_message: str, raw: bytes): - super().__init__(error_message) + super().__init__(error_message, raw) #: The raw `bytes` that were not understood as JSON. self.raw: bytes = raw def __str__(self) -> str: - return "\n".join([ + return "\n".join(( super().__str__(), f" raw bytes were: {str(self.raw)}", - ]) + )) class UnexpectedTypeError(ProtocolError): @@ -197,13 +197,13 @@ class UnexpectedTypeError(ProtocolError): :param value: The deserialized JSON value that wasn't an object. """ def __init__(self, error_message: str, value: object): - super().__init__(error_message) + super().__init__(error_message, value) #: The JSON value that was expected to be an object. self.value: object = value def __str__(self) -> str: strval = json.dumps(self.value, indent=2) - return "\n".join([ + return "\n".join(( super().__str__(), f" json value was: {strval}", - ]) + )) diff --git a/python/qemu/qmp/protocol.py b/python/qemu/qmp/protocol.py index a4ffdfad51..86e588881b 100644 --- a/python/qemu/qmp/protocol.py +++ b/python/qemu/qmp/protocol.py @@ -80,7 +80,7 @@ class ConnectError(QMPError): :param exc: The root-cause exception. """ def __init__(self, error_message: str, exc: Exception): - super().__init__(error_message) + super().__init__(error_message, exc) #: Human-readable error string self.error_message: str = error_message #: Wrapped root cause exception @@ -108,11 +108,14 @@ class StateError(QMPError): """ def __init__(self, error_message: str, state: Runstate, required: Runstate): - super().__init__(error_message) + super().__init__(error_message, state, required) self.error_message = error_message self.state = state self.required = required + def __str__(self) -> str: + return self.error_message + F = TypeVar('F', bound=Callable[..., Any]) # pylint: disable=invalid-name diff --git a/python/qemu/qmp/qmp_client.py b/python/qemu/qmp/qmp_client.py index 2a817f9db3..a87fb565ab 100644 --- a/python/qemu/qmp/qmp_client.py +++ b/python/qemu/qmp/qmp_client.py @@ -41,7 +41,7 @@ class _WrappedProtocolError(ProtocolError): :param exc: The root-cause exception. """ def __init__(self, error_message: str, exc: Exception): - super().__init__(error_message) + super().__init__(error_message, exc) self.exc = exc def __str__(self) -> str: @@ -76,15 +76,21 @@ class ExecuteError(QMPError): """ def __init__(self, error_response: ErrorResponse, sent: Message, received: Message): - super().__init__(error_response.error.desc) + super().__init__(error_response, sent, received) #: The sent `Message` that caused the failure self.sent: Message = sent #: The received `Message` that indicated failure self.received: Message = received #: The parsed error response self.error: ErrorResponse = error_response - #: The QMP error class - self.error_class: str = error_response.error.class_ + + @property + def error_class(self) -> str: + """The QMP error class""" + return self.error.error.class_ + + def __str__(self) -> str: + return self.error.error.desc class ExecInterruptedError(QMPError): @@ -110,8 +116,8 @@ class _MsgProtocolError(ProtocolError): :param error_message: Human-readable string describing the error. :param msg: The QMP `Message` that caused the error. """ - def __init__(self, error_message: str, msg: Message): - super().__init__(error_message) + def __init__(self, error_message: str, msg: Message, *args: object): + super().__init__(error_message, msg, *args) #: The received `Message` that caused the error. self.msg: Message = msg @@ -150,7 +156,7 @@ class BadReplyError(_MsgProtocolError): :param sent: The message that was sent that prompted the error. """ def __init__(self, error_message: str, msg: Message, sent: Message): - super().__init__(error_message, msg) + super().__init__(error_message, msg, sent) #: The sent `Message` that caused the failure self.sent = sent From cb0e43804038e47bbaf0179ce67df678ec13a392 Mon Sep 17 00:00:00 2001 From: John Snow Date: Fri, 22 Jul 2022 15:42:23 -0400 Subject: [PATCH 0384/2396] python: backport 'EventListener: add __repr__ method' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the object is not stateful, this repr method prints what you'd expect. In cases where there are pending events, the output is augmented to illustrate that. The object itself has no idea if it's "active" or not, so it cannot convey that information. Signed-off-by: John Snow cherry picked from commit python-qemu-qmp@8a6f2e136dae395fec8aa5fd77487cfe12d9e05e Reviewed-by: Daniel P. Berrangé --- python/qemu/qmp/events.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/python/qemu/qmp/events.py b/python/qemu/qmp/events.py index 6199776cc6..6658349619 100644 --- a/python/qemu/qmp/events.py +++ b/python/qemu/qmp/events.py @@ -497,6 +497,21 @@ class EventListener: #: Optional, secondary event filter. self.event_filter: Optional[EventFilter] = event_filter + def __repr__(self) -> str: + args: List[str] = [] + if self.names: + args.append(f"names={self.names!r}") + if self.event_filter: + args.append(f"event_filter={self.event_filter!r}") + + if self._queue.qsize(): + state = f"" + else: + state = '' + + argstr = ", ".join(args) + return f"{type(self).__name__}{state}({argstr})" + @property def history(self) -> Tuple[Message, ...]: """ From 1e343714bfc06cc982e68a290f3809117d6dfcd0 Mon Sep 17 00:00:00 2001 From: John Snow Date: Tue, 3 May 2022 14:07:10 -0400 Subject: [PATCH 0385/2396] python: backport 'kick event queue on legacy event_pull()' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This corrects an oversight in qmp-shell operation where new events will not accumulate in the event queue when pressing "enter" with an empty command buffer, so no new events show up. Reported-by: Jag Raman Signed-off-by: John Snow cherry picked from commit python-qemu-qmp@0443582d16cf9efd52b2c41a7b5be7af42c856cd Reviewed-by: Daniel P. Berrangé --- python/qemu/qmp/legacy.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/python/qemu/qmp/legacy.py b/python/qemu/qmp/legacy.py index 22a2b5616e..c8d0a29b56 100644 --- a/python/qemu/qmp/legacy.py +++ b/python/qemu/qmp/legacy.py @@ -231,6 +231,9 @@ class QEMUMonitorProtocol: :return: The first available QMP event, or None. """ + # Kick the event loop to allow events to accumulate + self._sync(asyncio.sleep(0)) + if not wait: # wait is False/0: "do not wait, do not except." if self._qmp.events.empty(): From 094ded5227dc4e8dde2c78a9788bf6c90771ad85 Mon Sep 17 00:00:00 2001 From: John Snow Date: Fri, 22 Jul 2022 16:30:05 -0400 Subject: [PATCH 0386/2396] python: backport 'protocol: adjust logging name when changing client name' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The client name is mutable, so the logging name should also change to reflect it when it changes. Signed-off-by: John Snow cherry picked from commit python-qemu-qmp@e10b73c633ce138ba30bc8beccd2ab31989eaf3d Reviewed-by: Daniel P. Berrangé --- python/qemu/qmp/protocol.py | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/python/qemu/qmp/protocol.py b/python/qemu/qmp/protocol.py index 86e588881b..ec4762c567 100644 --- a/python/qemu/qmp/protocol.py +++ b/python/qemu/qmp/protocol.py @@ -217,10 +217,8 @@ class AsyncProtocol(Generic[T]): # ------------------------- def __init__(self, name: Optional[str] = None) -> None: - #: The nickname for this connection, if any. - self.name: Optional[str] = name - if self.name is not None: - self.logger = self.logger.getChild(self.name) + self._name: Optional[str] + self.name = name # stream I/O self._reader: Optional[StreamReader] = None @@ -257,6 +255,24 @@ class AsyncProtocol(Generic[T]): tokens.append(f"runstate={self.runstate.name}") return f"<{cls_name} {' '.join(tokens)}>" + @property + def name(self) -> Optional[str]: + """ + The nickname for this connection, if any. + + This name is used for differentiating instances in debug output. + """ + return self._name + + @name.setter + def name(self, name: Optional[str]) -> None: + logger = logging.getLogger(__name__) + if name: + self.logger = logger.getChild(name) + else: + self.logger = logger + self._name = name + @property # @upper_half def runstate(self) -> Runstate: """The current `Runstate` of the connection.""" From f9d2e0a3bd7ba2a693a892881f91cf53fa90cc71 Mon Sep 17 00:00:00 2001 From: John Snow Date: Tue, 6 Jun 2023 13:19:11 -0400 Subject: [PATCH 0387/2396] python: backport 'drop Python3.6 workarounds' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that the minimum version is 3.7, drop some of the 3.6-specific hacks we've been carrying. A single remaining compatibility hack concerning 3.6's lack of @asynccontextmanager is addressed in the following commit. Signed-off-by: John Snow cherry picked from commit python-qemu-qmp@3e8e34e594cfc6b707e6f67959166acde4b421b8 Signed-off-by: John Snow Reviewed-by: Daniel P. Berrangé --- python/qemu/qmp/protocol.py | 13 ++--- python/qemu/qmp/qmp_tui.py | 8 +-- python/qemu/qmp/util.py | 107 ++---------------------------------- python/tests/protocol.py | 8 +-- 4 files changed, 17 insertions(+), 119 deletions(-) diff --git a/python/qemu/qmp/protocol.py b/python/qemu/qmp/protocol.py index ec4762c567..208bdec5c8 100644 --- a/python/qemu/qmp/protocol.py +++ b/python/qemu/qmp/protocol.py @@ -36,13 +36,10 @@ from typing import ( from .error import QMPError from .util import ( bottom_half, - create_task, exception_summary, flush, - is_closing, pretty_traceback, upper_half, - wait_closed, ) @@ -682,8 +679,8 @@ class AsyncProtocol(Generic[T]): reader_coro = self._bh_loop_forever(self._bh_recv_message, 'Reader') writer_coro = self._bh_loop_forever(self._bh_send_message, 'Writer') - self._reader_task = create_task(reader_coro) - self._writer_task = create_task(writer_coro) + self._reader_task = asyncio.create_task(reader_coro) + self._writer_task = asyncio.create_task(writer_coro) self._bh_tasks = asyncio.gather( self._reader_task, @@ -708,7 +705,7 @@ class AsyncProtocol(Generic[T]): if not self._dc_task: self._set_state(Runstate.DISCONNECTING) self.logger.debug("Scheduling disconnect.") - self._dc_task = create_task(self._bh_disconnect()) + self._dc_task = asyncio.create_task(self._bh_disconnect()) @upper_half async def _wait_disconnect(self) -> None: @@ -844,13 +841,13 @@ class AsyncProtocol(Generic[T]): if not self._writer: return - if not is_closing(self._writer): + if not self._writer.is_closing(): self.logger.debug("Closing StreamWriter.") self._writer.close() self.logger.debug("Waiting for StreamWriter to close ...") try: - await wait_closed(self._writer) + await self._writer.wait_closed() except Exception: # pylint: disable=broad-except # It's hard to tell if the Stream is already closed or # not. Even if one of the tasks has failed, it may have diff --git a/python/qemu/qmp/qmp_tui.py b/python/qemu/qmp/qmp_tui.py index 2d9ebbd20b..562be008d5 100644 --- a/python/qemu/qmp/qmp_tui.py +++ b/python/qemu/qmp/qmp_tui.py @@ -40,7 +40,7 @@ from .legacy import QEMUMonitorProtocol, QMPBadPortError from .message import DeserializationError, Message, UnexpectedTypeError from .protocol import ConnectError, Runstate from .qmp_client import ExecInterruptedError, QMPClient -from .util import create_task, pretty_traceback +from .util import pretty_traceback # The name of the signal that is used to update the history list @@ -225,7 +225,7 @@ class App(QMPClient): """ try: msg = Message(bytes(raw_msg, encoding='utf-8')) - create_task(self._send_to_server(msg)) + asyncio.create_task(self._send_to_server(msg)) except (DeserializationError, UnexpectedTypeError) as err: raw_msg = format_json(raw_msg) logging.info('Invalid message: %s', err.error_message) @@ -246,7 +246,7 @@ class App(QMPClient): Initiates killing of app. A bridge between asynchronous and synchronous code. """ - create_task(self._kill_app()) + asyncio.create_task(self._kill_app()) async def _kill_app(self) -> None: """ @@ -393,7 +393,7 @@ class App(QMPClient): handle_mouse=True, event_loop=event_loop) - create_task(self.manage_connection(), self.aloop) + self.aloop.create_task(self.manage_connection()) try: main_loop.run() except Exception as err: diff --git a/python/qemu/qmp/util.py b/python/qemu/qmp/util.py index ca6225e9cd..0b3e781373 100644 --- a/python/qemu/qmp/util.py +++ b/python/qemu/qmp/util.py @@ -1,25 +1,15 @@ """ Miscellaneous Utilities -This module provides asyncio utilities and compatibility wrappers for -Python 3.6 to provide some features that otherwise become available in -Python 3.7+. - -Various logging and debugging utilities are also provided, such as -`exception_summary()` and `pretty_traceback()`, used primarily for -adding information into the logging stream. +This module provides asyncio and various logging and debugging +utilities, such as `exception_summary()` and `pretty_traceback()`, used +primarily for adding information into the logging stream. """ import asyncio import sys import traceback -from typing import ( - Any, - Coroutine, - Optional, - TypeVar, - cast, -) +from typing import TypeVar, cast T = TypeVar('T') @@ -79,95 +69,6 @@ def bottom_half(func: T) -> T: return func -# ------------------------------- -# Section: Compatibility Wrappers -# ------------------------------- - - -def create_task(coro: Coroutine[Any, Any, T], - loop: Optional[asyncio.AbstractEventLoop] = None - ) -> 'asyncio.Future[T]': - """ - Python 3.6-compatible `asyncio.create_task` wrapper. - - :param coro: The coroutine to execute in a task. - :param loop: Optionally, the loop to create the task in. - - :return: An `asyncio.Future` object. - """ - if sys.version_info >= (3, 7): - if loop is not None: - return loop.create_task(coro) - return asyncio.create_task(coro) # pylint: disable=no-member - - # Python 3.6: - return asyncio.ensure_future(coro, loop=loop) - - -def is_closing(writer: asyncio.StreamWriter) -> bool: - """ - Python 3.6-compatible `asyncio.StreamWriter.is_closing` wrapper. - - :param writer: The `asyncio.StreamWriter` object. - :return: `True` if the writer is closing, or closed. - """ - if sys.version_info >= (3, 7): - return writer.is_closing() - - # Python 3.6: - transport = writer.transport - assert isinstance(transport, asyncio.WriteTransport) - return transport.is_closing() - - -async def wait_closed(writer: asyncio.StreamWriter) -> None: - """ - Python 3.6-compatible `asyncio.StreamWriter.wait_closed` wrapper. - - :param writer: The `asyncio.StreamWriter` to wait on. - """ - if sys.version_info >= (3, 7): - await writer.wait_closed() - return - - # Python 3.6 - transport = writer.transport - assert isinstance(transport, asyncio.WriteTransport) - - while not transport.is_closing(): - await asyncio.sleep(0) - - # This is an ugly workaround, but it's the best I can come up with. - sock = transport.get_extra_info('socket') - - if sock is None: - # Our transport doesn't have a socket? ... - # Nothing we can reasonably do. - return - - while sock.fileno() != -1: - await asyncio.sleep(0) - - -def asyncio_run(coro: Coroutine[Any, Any, T], *, debug: bool = False) -> T: - """ - Python 3.6-compatible `asyncio.run` wrapper. - - :param coro: A coroutine to execute now. - :return: The return value from the coroutine. - """ - if sys.version_info >= (3, 7): - return asyncio.run(coro, debug=debug) - - # Python 3.6 - loop = asyncio.get_event_loop() - loop.set_debug(debug) - ret = loop.run_until_complete(coro) - loop.close() - - return ret - - # ---------------------------- # Section: Logging & Debugging # ---------------------------- diff --git a/python/tests/protocol.py b/python/tests/protocol.py index 56c4d441f9..c254c77b17 100644 --- a/python/tests/protocol.py +++ b/python/tests/protocol.py @@ -8,7 +8,6 @@ import avocado from qemu.qmp import ConnectError, Runstate from qemu.qmp.protocol import AsyncProtocol, StateError -from qemu.qmp.util import asyncio_run, create_task class NullProtocol(AsyncProtocol[None]): @@ -124,7 +123,7 @@ def run_as_task(coro, allow_cancellation=False): if allow_cancellation: return raise - return create_task(_runner()) + return asyncio.create_task(_runner()) @contextmanager @@ -271,7 +270,7 @@ class TestBase(avocado.Test): msg=f"Expected state '{state.name}'", ) - self.runstate_watcher = create_task(_watcher()) + self.runstate_watcher = asyncio.create_task(_watcher()) # Kick the loop and force the task to block on the event. await asyncio.sleep(0) @@ -589,7 +588,8 @@ class SimpleSession(TestBase): async def testSmoke(self): with TemporaryDirectory(suffix='.qmp') as tmpdir: sock = os.path.join(tmpdir, type(self.proto).__name__ + ".sock") - server_task = create_task(self.server.start_server_and_accept(sock)) + server_task = asyncio.create_task( + self.server.start_server_and_accept(sock)) # give the server a chance to start listening [...] await asyncio.sleep(0) From 0408b8d7a086486f5c1887798be744b2d73bcda9 Mon Sep 17 00:00:00 2001 From: John Snow Date: Tue, 6 Jun 2023 13:45:44 -0400 Subject: [PATCH 0388/2396] python: backport 'Use @asynciocontextmanager' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This removes a non-idiomatic use of a "coroutine callback" in favor of something a bit more standardized. Signed-off-by: John Snow cherry picked from commit python-qemu-qmp@commit 97f7ffa3be17a50544b52767d14b6fd478c07b9e Signed-off-by: John Snow Reviewed-by: Daniel P. Berrangé --- python/qemu/qmp/protocol.py | 35 ++++++++++++++++------------------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/python/qemu/qmp/protocol.py b/python/qemu/qmp/protocol.py index 208bdec5c8..958aeca08a 100644 --- a/python/qemu/qmp/protocol.py +++ b/python/qemu/qmp/protocol.py @@ -15,6 +15,7 @@ class. import asyncio from asyncio import StreamReader, StreamWriter +from contextlib import asynccontextmanager from enum import Enum from functools import wraps import logging @@ -22,6 +23,7 @@ import socket from ssl import SSLContext from typing import ( Any, + AsyncGenerator, Awaitable, Callable, Generic, @@ -337,9 +339,8 @@ class AsyncProtocol(Generic[T]): This exception will wrap a more concrete one. In most cases, the wrapped exception will be `OSError`. """ - await self._session_guard( - self._do_start_server(address, ssl), - 'Failed to establish connection') + async with self._session_guard('Failed to establish connection'): + await self._do_start_server(address, ssl) assert self.runstate == Runstate.CONNECTING @upper_half @@ -362,12 +363,10 @@ class AsyncProtocol(Generic[T]): """ if self._accepted is None: raise QMPError("Cannot call accept() before start_server().") - await self._session_guard( - self._do_accept(), - 'Failed to establish connection') - await self._session_guard( - self._establish_session(), - 'Failed to establish session') + async with self._session_guard('Failed to establish connection'): + await self._do_accept() + async with self._session_guard('Failed to establish session'): + await self._establish_session() assert self.runstate == Runstate.RUNNING @upper_half @@ -392,12 +391,10 @@ class AsyncProtocol(Generic[T]): protocol-level failure occurs while establishing a new session, the wrapped error may also be an `QMPError`. """ - await self._session_guard( - self._do_connect(address, ssl), - 'Failed to establish connection') - await self._session_guard( - self._establish_session(), - 'Failed to establish session') + async with self._session_guard('Failed to establish connection'): + await self._do_connect(address, ssl) + async with self._session_guard('Failed to establish session'): + await self._establish_session() assert self.runstate == Runstate.RUNNING @upper_half @@ -418,7 +415,8 @@ class AsyncProtocol(Generic[T]): # Section: Session machinery # -------------------------- - async def _session_guard(self, coro: Awaitable[None], emsg: str) -> None: + @asynccontextmanager + async def _session_guard(self, emsg: str) -> AsyncGenerator[None, None]: """ Async guard function used to roll back to `IDLE` on any error. @@ -435,10 +433,9 @@ class AsyncProtocol(Generic[T]): :raise ConnectError: When any other error is encountered in the guarded block. """ - # Note: After Python 3.6 support is removed, this should be an - # @asynccontextmanager instead of accepting a callback. try: - await coro + # Caller's code runs here. + yield except BaseException as err: self.logger.error("%s: %s", emsg, exception_summary(err)) self.logger.debug("%s:\n%s\n", emsg, pretty_traceback()) From 8fd9ccebd905dfe3afdee03732ba09e46a3a1d49 Mon Sep 17 00:00:00 2001 From: John Snow Date: Fri, 22 Jul 2022 13:59:19 -0400 Subject: [PATCH 0389/2396] python: backport 'qmp-shell: add common_parser()' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: John Snow cherry picked from commit python-qemu-qmp@20a88c2471f37d10520b2409046d59e1d0f1e905 Signed-off-by: John Snow Signed-off-by: Daniel P. Berrangé --- python/qemu/qmp/qmp_shell.py | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/python/qemu/qmp/qmp_shell.py b/python/qemu/qmp/qmp_shell.py index 98e684e9e8..02028e94b5 100644 --- a/python/qemu/qmp/qmp_shell.py +++ b/python/qemu/qmp/qmp_shell.py @@ -514,21 +514,27 @@ def die(msg: str) -> NoReturn: sys.exit(1) -def main() -> None: - """ - qmp-shell entry point: parse command line arguments and start the REPL. - """ +def common_parser() -> argparse.ArgumentParser: + """Build common parsing options used by qmp-shell and qmp-shell-wrap.""" parser = argparse.ArgumentParser() parser.add_argument('-H', '--hmp', action='store_true', help='Use HMP interface') - parser.add_argument('-N', '--skip-negotiation', action='store_true', - help='Skip negotiate (for qemu-ga)') parser.add_argument('-v', '--verbose', action='store_true', help='Verbose (echo commands sent and received)') parser.add_argument('-p', '--pretty', action='store_true', help='Pretty-print JSON') parser.add_argument('-l', '--logfile', help='Save log of all QMP messages to PATH') + return parser + + +def main() -> None: + """ + qmp-shell entry point: parse command line arguments and start the REPL. + """ + parser = common_parser() + parser.add_argument('-N', '--skip-negotiation', action='store_true', + help='Skip negotiate (for qemu-ga)') default_server = os.environ.get('QMP_SOCKET') parser.add_argument('qmp_server', action='store', @@ -564,16 +570,7 @@ def main_wrap() -> None: qmp-shell-wrap entry point: parse command line arguments and start the REPL. """ - parser = argparse.ArgumentParser() - parser.add_argument('-H', '--hmp', action='store_true', - help='Use HMP interface') - parser.add_argument('-v', '--verbose', action='store_true', - help='Verbose (echo commands sent and received)') - parser.add_argument('-p', '--pretty', action='store_true', - help='Pretty-print JSON') - parser.add_argument('-l', '--logfile', - help='Save log of all QMP messages to PATH') - + parser = common_parser() parser.add_argument('command', nargs=argparse.REMAINDER, help='QEMU command line to invoke') From 653f501434889b23a5062b3fe00488d140eb79fd Mon Sep 17 00:00:00 2001 From: Adam Dorsey Date: Mon, 14 Apr 2025 14:30:14 -0400 Subject: [PATCH 0390/2396] python: backport 'feat: allow setting read buffer limit' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Expose the limit parameter of the underlying StreamReader and StreamWriter instances. This is helpful for the use case of transferring files in and out of a VM via the QEMU guest agent's guest-file-open, guest-file-read, guest-file-write, and guest-file-close methods, as it allows pushing the buffer size up to the guest agent's limit of 48MB per transfer. Signed-off-by: Adam Dorsey cherry picked from commit python-qemu-qmp@9ba6a698344eb3b570fa4864e906c54042824cd6 cherry picked from commit python-qemu-qmp@e4d0d3f835d82283ee0e48438d1b154e18303491 [Squashed in linter fixups. --js] Signed-off-by: John Snow Reviewed-by: Daniel P. Berrangé --- python/qemu/qmp/protocol.py | 25 ++++++++++++++++--------- python/qemu/qmp/qmp_client.py | 18 ++++++++++++++---- 2 files changed, 30 insertions(+), 13 deletions(-) diff --git a/python/qemu/qmp/protocol.py b/python/qemu/qmp/protocol.py index 958aeca08a..3d5eb553aa 100644 --- a/python/qemu/qmp/protocol.py +++ b/python/qemu/qmp/protocol.py @@ -53,6 +53,9 @@ InternetAddrT = Tuple[str, int] UnixAddrT = str SocketAddrT = Union[UnixAddrT, InternetAddrT] +# Maximum allowable size of read buffer, default +_DEFAULT_READBUFLEN = 64 * 1024 + class Runstate(Enum): """Protocol session runstate.""" @@ -202,22 +205,26 @@ class AsyncProtocol(Generic[T]): will log to 'qemu.qmp.protocol', but each individual connection can be given its own logger by giving it a name; messages will then log to 'qemu.qmp.protocol.${name}'. + :param readbuflen: + The maximum read buffer length of the underlying StreamReader + instance. """ # pylint: disable=too-many-instance-attributes #: Logger object for debugging messages from this connection. logger = logging.getLogger(__name__) - # Maximum allowable size of read buffer - _limit = 64 * 1024 - # ------------------------- # Section: Public interface # ------------------------- - def __init__(self, name: Optional[str] = None) -> None: + def __init__( + self, name: Optional[str] = None, + readbuflen: int = _DEFAULT_READBUFLEN + ) -> None: self._name: Optional[str] self.name = name + self.readbuflen = readbuflen # stream I/O self._reader: Optional[StreamReader] = None @@ -574,7 +581,7 @@ class AsyncProtocol(Generic[T]): port=address[1], ssl=ssl, backlog=1, - limit=self._limit, + limit=self.readbuflen, ) else: coro = asyncio.start_unix_server( @@ -582,7 +589,7 @@ class AsyncProtocol(Generic[T]): path=address, ssl=ssl, backlog=1, - limit=self._limit, + limit=self.readbuflen, ) # Allow runstate watchers to witness 'CONNECTING' state; some @@ -637,7 +644,7 @@ class AsyncProtocol(Generic[T]): "fd=%d, family=%r, type=%r", address.fileno(), address.family, address.type) connect = asyncio.open_connection( - limit=self._limit, + limit=self.readbuflen, ssl=ssl, sock=address, ) @@ -647,14 +654,14 @@ class AsyncProtocol(Generic[T]): address[0], address[1], ssl=ssl, - limit=self._limit, + limit=self.readbuflen, ) else: self.logger.debug("Connecting to file://%s ...", address) connect = asyncio.open_unix_connection( path=address, ssl=ssl, - limit=self._limit, + limit=self.readbuflen, ) self._reader, self._writer = await connect diff --git a/python/qemu/qmp/qmp_client.py b/python/qemu/qmp/qmp_client.py index a87fb565ab..d826331b6d 100644 --- a/python/qemu/qmp/qmp_client.py +++ b/python/qemu/qmp/qmp_client.py @@ -170,6 +170,12 @@ class QMPClient(AsyncProtocol[Message], Events): :param name: Optional nickname for the connection, used for logging. + :param readbuflen: + The maximum buffer length for reads and writes to and from the QMP + server, in bytes. Default is 10MB. If `QMPClient` is used to + connect to a guest agent to transfer files via ``guest-file-read``/ + ``guest-file-write``, increasing this value may be required. + Basic script-style usage looks like this:: qmp = QMPClient('my_virtual_machine_name') @@ -203,14 +209,18 @@ class QMPClient(AsyncProtocol[Message], Events): #: Logger object used for debugging messages. logger = logging.getLogger(__name__) - # Read buffer limit; 10MB like libvirt default - _limit = 10 * 1024 * 1024 + # Read buffer default limit; 10MB like libvirt default + _readbuflen = 10 * 1024 * 1024 # Type alias for pending execute() result items _PendingT = Union[Message, ExecInterruptedError] - def __init__(self, name: Optional[str] = None) -> None: - super().__init__(name) + def __init__( + self, + name: Optional[str] = None, + readbuflen: int = _readbuflen + ) -> None: + super().__init__(name, readbuflen) Events.__init__(self) #: Whether or not to await a greeting after establishing a connection. From a50b8572f0e3873db64dd6660cea047f067ca5f7 Mon Sep 17 00:00:00 2001 From: John Snow Date: Mon, 2 May 2022 18:54:20 -0400 Subject: [PATCH 0391/2396] python: backport 'make require() preserve async-ness' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is not strictly needed functionality-wise, but doing this allows sphinx to see which decorated methods are async. Without this, sphinx misses the "async" classifier on generated docs, which ... for an async library, isn't great. It does make an already gnarly function even gnarlier, though. So, what's going on here? A synchronous function (like require() before this patch) can return a coroutine that can be awaited on, for example: def some_func(): return asyncio.task(asyncio.sleep(5)) async def some_async_func(): await some_func() However, this function is not considered to be an "async" function in the eyes of the abstract syntax tree. Specifically, some_func.__code__.co_flags will not be set with CO_COROUTINE. The interpreter uses this flag to know if it's legal to use "await" from within the body of the function. Since this function is just wrapping another function, it doesn't matter much for the decorator, but sphinx uses the stdlib inspect.iscoroutinefunction() to determine when to add the "async" prefix in generated output. This function uses the presence of CO_COROUTINE. So, in order to preserve the "async" flag for docs, the require() decorator needs to differentiate based on whether it is decorating a sync or async function and use a different wrapping mechanism accordingly. Phew. Signed-off-by: John Snow cherry picked from commit python-qemu-qmp@40aa9699d619849f528032aa456dd061a4afa957 Signed-off-by: John Snow Reviewed-by: Daniel P. Berrangé --- python/qemu/qmp/protocol.py | 53 ++++++++++++++++++++++--------------- 1 file changed, 32 insertions(+), 21 deletions(-) diff --git a/python/qemu/qmp/protocol.py b/python/qemu/qmp/protocol.py index 3d5eb553aa..4d8a39f014 100644 --- a/python/qemu/qmp/protocol.py +++ b/python/qemu/qmp/protocol.py @@ -18,6 +18,7 @@ from asyncio import StreamReader, StreamWriter from contextlib import asynccontextmanager from enum import Enum from functools import wraps +from inspect import iscoroutinefunction import logging import socket from ssl import SSLContext @@ -130,6 +131,25 @@ def require(required_state: Runstate) -> Callable[[F], F]: :param required_state: The `Runstate` required to invoke this method. :raise StateError: When the required `Runstate` is not met. """ + def _check(proto: 'AsyncProtocol[Any]') -> None: + name = type(proto).__name__ + if proto.runstate == required_state: + return + + if proto.runstate == Runstate.CONNECTING: + emsg = f"{name} is currently connecting." + elif proto.runstate == Runstate.DISCONNECTING: + emsg = (f"{name} is disconnecting." + " Call disconnect() to return to IDLE state.") + elif proto.runstate == Runstate.RUNNING: + emsg = f"{name} is already connected and running." + elif proto.runstate == Runstate.IDLE: + emsg = f"{name} is disconnected and idle." + else: + assert False + + raise StateError(emsg, proto.runstate, required_state) + def _decorator(func: F) -> F: # _decorator is the decorator that is built by calling the # require() decorator factory; e.g.: @@ -140,29 +160,20 @@ def require(required_state: Runstate) -> Callable[[F], F]: @wraps(func) def _wrapper(proto: 'AsyncProtocol[Any]', *args: Any, **kwargs: Any) -> Any: - # _wrapper is the function that gets executed prior to the - # decorated method. - - name = type(proto).__name__ - - if proto.runstate != required_state: - if proto.runstate == Runstate.CONNECTING: - emsg = f"{name} is currently connecting." - elif proto.runstate == Runstate.DISCONNECTING: - emsg = (f"{name} is disconnecting." - " Call disconnect() to return to IDLE state.") - elif proto.runstate == Runstate.RUNNING: - emsg = f"{name} is already connected and running." - elif proto.runstate == Runstate.IDLE: - emsg = f"{name} is disconnected and idle." - else: - assert False - raise StateError(emsg, proto.runstate, required_state) - # No StateError, so call the wrapped method. + _check(proto) return func(proto, *args, **kwargs) - # Return the decorated method; - # Transforming Func to Decorated[Func]. + @wraps(func) + async def _async_wrapper(proto: 'AsyncProtocol[Any]', + *args: Any, **kwargs: Any) -> Any: + _check(proto) + return await func(proto, *args, **kwargs) + + # Return the decorated method; F => Decorated[F] + # Use an async version when applicable, which + # preserves async signature generation in sphinx. + if iscoroutinefunction(func): + return cast(F, _async_wrapper) return cast(F, _wrapper) # Return the decorator instance from the decorator factory. Phew! From fcaeeb7653d2c6f38183170e1cae5729adb7875c Mon Sep 17 00:00:00 2001 From: John Snow Date: Fri, 22 Jul 2022 14:13:45 -0400 Subject: [PATCH 0392/2396] python: backport 'qmp-shell-wrap: handle missing binary gracefully' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: John Snow cherry picked from commit python-qemu-qmp@9c889dcbd58817b0c917a9d2dd16161f48ac8203 Signed-off-by: John Snow Reviewed-by: Daniel P. Berrangé --- python/qemu/qmp/qmp_shell.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/python/qemu/qmp/qmp_shell.py b/python/qemu/qmp/qmp_shell.py index 02028e94b5..c923ff09e1 100644 --- a/python/qemu/qmp/qmp_shell.py +++ b/python/qemu/qmp/qmp_shell.py @@ -607,6 +607,8 @@ def main_wrap() -> None: for _ in qemu.repl(): pass + except FileNotFoundError: + sys.stderr.write(f"ERROR: QEMU executable '{cmd[0]}' not found.\n") finally: os.unlink(sockpath) From fd0ed46d4effbf2700804657bad9c6db086527c4 Mon Sep 17 00:00:00 2001 From: John Snow Date: Fri, 22 Jul 2022 15:55:45 -0400 Subject: [PATCH 0393/2396] python: backport 'qmp-tui: Do not crash if optional dependencies are not met' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Based on the discussion at https://github.com/pypa/pip/issues/9726 - even though the setuptools documentation implies that it is possible to guard script execution with optional dependency groups, this is not true in practice with the scripts generated by pip. Just do the simple thing and guard the import statements. Signed-off-by: John Snow cherry picked from commit python-qemu-qmp@df520dcacf9a75dd4c82ab1129768de4128b554c Signed-off-by: John Snow Reviewed-by: Daniel P. Berrangé --- python/qemu/qmp/qmp_tui.py | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/python/qemu/qmp/qmp_tui.py b/python/qemu/qmp/qmp_tui.py index 562be008d5..53ea6c59a7 100644 --- a/python/qemu/qmp/qmp_tui.py +++ b/python/qemu/qmp/qmp_tui.py @@ -21,6 +21,7 @@ import json import logging from logging import Handler, LogRecord import signal +import sys from typing import ( List, Optional, @@ -30,10 +31,20 @@ from typing import ( cast, ) -from pygments import lexers -from pygments import token as Token -import urwid -import urwid_readline + +try: + from pygments import lexers + from pygments import token as Token + import urwid + import urwid_readline +except ModuleNotFoundError as exc: + print( + f"Module '{exc.name}' not found.", + "You need the optional 'tui' group: pip install qemu.qmp[tui]", + sep='\n', + file=sys.stderr, + ) + sys.exit(1) from .error import ProtocolError from .legacy import QEMUMonitorProtocol, QMPBadPortError From 5d99044d09db0fa8c2b3294e301927118f9effc9 Mon Sep 17 00:00:00 2001 From: John Snow Date: Tue, 13 Aug 2024 09:35:30 -0400 Subject: [PATCH 0394/2396] python: backport 'Remove deprecated get_event_loop calls' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This method was deprecated in 3.12 because it ordinarily should not be used from coroutines; if there is not a currently running event loop, this automatically creates a new event loop - which is usually not what you want from code that would ever run in the bottom half. In our case, we do want this behavior in two places: (1) The synchronous shim, for convenience: this allows fully sync programs to use QEMUMonitorProtocol() without needing to set up an event loop beforehand. This is intentional to fully box in the async complexities into the legacy sync shim. (2) The qmp_tui shell; instead of relying on asyncio.run to create and run an asyncio program, we need to be able to pass the current asyncio loop to urwid setup functions. For convenience, again, we create one if one is not present to simplify the creation of the TUI appliance. The remaining user of get_event_loop() was in fact one of the erroneous users that should not have been using this function: if there's no running event loop inside of a coroutine, you're in big trouble :) Signed-off-by: John Snow cherry picked from commit python-qemu-qmp@aa1ff9907603a3033296027e1bd021133df86ef1 Signed-off-by: John Snow Reviewed-by: Daniel P. Berrangé --- python/qemu/qmp/legacy.py | 9 ++++++++- python/qemu/qmp/qmp_tui.py | 7 ++++++- python/tests/protocol.py | 2 +- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/python/qemu/qmp/legacy.py b/python/qemu/qmp/legacy.py index c8d0a29b56..735d42971e 100644 --- a/python/qemu/qmp/legacy.py +++ b/python/qemu/qmp/legacy.py @@ -86,7 +86,14 @@ class QEMUMonitorProtocol: "server argument should be False when passing a socket") self._qmp = QMPClient(nickname) - self._aloop = asyncio.get_event_loop() + + try: + self._aloop = asyncio.get_running_loop() + except RuntimeError: + # No running loop; since this is a sync shim likely to be + # used in fully sync programs, create one if neccessary. + self._aloop = asyncio.get_event_loop_policy().get_event_loop() + self._address = address self._timeout: Optional[float] = None diff --git a/python/qemu/qmp/qmp_tui.py b/python/qemu/qmp/qmp_tui.py index 53ea6c59a7..12bdc17c99 100644 --- a/python/qemu/qmp/qmp_tui.py +++ b/python/qemu/qmp/qmp_tui.py @@ -388,7 +388,12 @@ class App(QMPClient): screen = urwid.raw_display.Screen() screen.set_terminal_properties(256) - self.aloop = asyncio.get_event_loop() + try: + self.aloop = asyncio.get_running_loop() + except RuntimeError: + # No running asyncio event loop. Create one if necessary. + self.aloop = asyncio.get_event_loop_policy().get_event_loop() + self.aloop.set_debug(debug) # Gracefully handle SIGTERM and SIGINT signals diff --git a/python/tests/protocol.py b/python/tests/protocol.py index c254c77b17..e565802516 100644 --- a/python/tests/protocol.py +++ b/python/tests/protocol.py @@ -227,7 +227,7 @@ class TestBase(avocado.Test): Decorator; adds SetUp and TearDown to async tests. """ async def _wrapper(self, *args, **kwargs): - loop = asyncio.get_event_loop() + loop = asyncio.get_running_loop() loop.set_debug(True) await self._asyncSetUp() From 85f223e5b031eb8ab63fbca314a4fb296a3a2632 Mon Sep 17 00:00:00 2001 From: John Snow Date: Wed, 3 Sep 2025 01:06:30 -0400 Subject: [PATCH 0395/2396] python: backport 'avoid creating additional event loops per thread' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit is two backports squashed into one to avoid regressions. python: *really* remove get_event_loop A prior commit, aa1ff990, switched away from using get_event_loop *by default*, but this is not good enough to avoid deprecation warnings as `asyncio.get_event_loop_policy().get_event_loop()` is *also* deprecated. Replace this mechanism with explicit calls to asyncio.get_new_loop() and revise the cleanup mechanisms in __del__ to match. python: avoid creating additional event loops per thread "Too hasty by far!", commit 21ce2ee4 attempted to avoid deprecated behavior altogether by calling new_event_loop() directly if there was no loop currently running, but this has the unfortunate side effect of potentially creating multiple event loops per thread if tests instantiate multiple QMP connections in a single thread. This behavior is apparently not well-defined and causes problems in some, but not all, combinations of Python interpreter version and platform environment. Partially revert to Daniel Berrange's original patch, which calls get_event_loop and simply suppresses the deprecation warning in Python<=3.13. This time, however, additionally register new loops created with new_event_loop() so that future calls to get_event_loop() will return the loop already created. Reported-by: Richard W.M. Jones Reported-by: Daniel P. Berrangé Signed-off-by: John Snow cherry picked from commit python-qemu-qmp@21ce2ee4f2df87efe84a27b9c5112487f4670622 cherry picked from commit python-qemu-qmp@c08fb82b38212956ccffc03fc6d015c3979f42fe Signed-off-by: John Snow Reviewed-by: Daniel P. Berrangé --- python/qemu/qmp/legacy.py | 46 +++++++++++++++++++++++--------------- python/qemu/qmp/qmp_tui.py | 10 ++------- python/qemu/qmp/util.py | 27 ++++++++++++++++++++++ 3 files changed, 57 insertions(+), 26 deletions(-) diff --git a/python/qemu/qmp/legacy.py b/python/qemu/qmp/legacy.py index 735d42971e..e46695ae2c 100644 --- a/python/qemu/qmp/legacy.py +++ b/python/qemu/qmp/legacy.py @@ -38,6 +38,7 @@ from typing import ( from .error import QMPError from .protocol import Runstate, SocketAddrT from .qmp_client import QMPClient +from .util import get_or_create_event_loop #: QMPMessage is an entire QMP message of any kind. @@ -86,17 +87,13 @@ class QEMUMonitorProtocol: "server argument should be False when passing a socket") self._qmp = QMPClient(nickname) - - try: - self._aloop = asyncio.get_running_loop() - except RuntimeError: - # No running loop; since this is a sync shim likely to be - # used in fully sync programs, create one if neccessary. - self._aloop = asyncio.get_event_loop_policy().get_event_loop() - self._address = address self._timeout: Optional[float] = None + # This is a sync shim intended for use in fully synchronous + # programs. Create and set an event loop if necessary. + self._aloop = get_or_create_event_loop() + if server: assert not isinstance(self._address, socket.socket) self._sync(self._qmp.start_server(self._address)) @@ -313,17 +310,30 @@ class QEMUMonitorProtocol: self._qmp.send_fd_scm(fd) def __del__(self) -> None: - if self._qmp.runstate == Runstate.IDLE: - return + if self._qmp.runstate != Runstate.IDLE: + self._qmp.logger.warning( + "QEMUMonitorProtocol object garbage collected without a prior " + "call to close()" + ) if not self._aloop.is_running(): - self.close() - else: - # Garbage collection ran while the event loop was running. - # Nothing we can do about it now, but if we don't raise our - # own error, the user will be treated to a lot of traceback - # they might not understand. + if self._qmp.runstate != Runstate.IDLE: + # If the user neglected to close the QMP session and we + # are not currently running in an asyncio context, we + # have the opportunity to close the QMP session. If we + # do not do this, the error messages presented over + # dangling async resources may not make any sense to the + # user. + self.close() + + if self._qmp.runstate != Runstate.IDLE: + # If QMP is still not quiesced, it means that the garbage + # collector ran from a context within the event loop and we + # are simply too late to take any corrective action. Raise + # our own error to give meaningful feedback to the user in + # order to prevent pages of asyncio stacktrace jargon. raise QMPError( - "QEMUMonitorProtocol.close()" - " was not called before object was garbage collected" + "QEMUMonitorProtocol.close() was not called before object was " + "garbage collected, and could not be closed due to GC running " + "in the event loop" ) diff --git a/python/qemu/qmp/qmp_tui.py b/python/qemu/qmp/qmp_tui.py index 12bdc17c99..d946c20513 100644 --- a/python/qemu/qmp/qmp_tui.py +++ b/python/qemu/qmp/qmp_tui.py @@ -51,7 +51,7 @@ from .legacy import QEMUMonitorProtocol, QMPBadPortError from .message import DeserializationError, Message, UnexpectedTypeError from .protocol import ConnectError, Runstate from .qmp_client import ExecInterruptedError, QMPClient -from .util import pretty_traceback +from .util import get_or_create_event_loop, pretty_traceback # The name of the signal that is used to update the history list @@ -387,13 +387,7 @@ class App(QMPClient): """ screen = urwid.raw_display.Screen() screen.set_terminal_properties(256) - - try: - self.aloop = asyncio.get_running_loop() - except RuntimeError: - # No running asyncio event loop. Create one if necessary. - self.aloop = asyncio.get_event_loop_policy().get_event_loop() - + self.aloop = get_or_create_event_loop() self.aloop.set_debug(debug) # Gracefully handle SIGTERM and SIGINT signals diff --git a/python/qemu/qmp/util.py b/python/qemu/qmp/util.py index 0b3e781373..47ec39a8b5 100644 --- a/python/qemu/qmp/util.py +++ b/python/qemu/qmp/util.py @@ -10,6 +10,7 @@ import asyncio import sys import traceback from typing import TypeVar, cast +import warnings T = TypeVar('T') @@ -20,6 +21,32 @@ T = TypeVar('T') # -------------------------- +def get_or_create_event_loop() -> asyncio.AbstractEventLoop: + """ + Return this thread's current event loop, or create a new one. + + This function behaves similarly to asyncio.get_event_loop() in + Python<=3.13, where if there is no event loop currently associated + with the current context, it will create and register one. It should + generally not be used in any asyncio-native applications. + """ + try: + with warnings.catch_warnings(): + # Python <= 3.13 will trigger deprecation warnings if no + # event loop is set, but will create and set a new loop. + warnings.simplefilter("ignore") + loop = asyncio.get_event_loop() + except RuntimeError: + # Python 3.14+: No event loop set for this thread, + # create and set one. + loop = asyncio.new_event_loop() + # Set this loop as the current thread's loop, to be returned + # by calls to get_event_loop() in the future. + asyncio.set_event_loop(loop) + + return loop + + async def flush(writer: asyncio.StreamWriter) -> None: """ Utility function to ensure a StreamWriter is *fully* drained. From f414048e32262830e06d50240b2f15b6e5857efe Mon Sep 17 00:00:00 2001 From: John Snow Date: Tue, 26 Aug 2025 13:04:50 -0400 Subject: [PATCH 0396/2396] python: synchronize qemu.qmp documentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch collects comments and documentation changes from many commits in the python-qemu-qmp repository; bringing the qemu.git copy in bit-identical alignment with the standalone library *except* for several copyright messages that reference the "LICENSE" file which is, for QEMU, named "COPYING" instead and are therefore left unchanged. Signed-off-by: John Snow Reviewed-by: Daniel P. Berrangé --- python/qemu/qmp/__init__.py | 3 +- python/qemu/qmp/events.py | 35 +++++++--- python/qemu/qmp/legacy.py | 4 +- python/qemu/qmp/message.py | 10 ++- python/qemu/qmp/models.py | 8 +-- python/qemu/qmp/protocol.py | 37 ++++++---- python/qemu/qmp/qmp_client.py | 117 +++++++++++++++++++++++-------- python/qemu/qmp/qmp_shell.py | 128 ++++++++++++++++++++++++++-------- python/qemu/qmp/util.py | 9 ++- 9 files changed, 264 insertions(+), 87 deletions(-) diff --git a/python/qemu/qmp/__init__.py b/python/qemu/qmp/__init__.py index 69190d057a..058139dc3c 100644 --- a/python/qemu/qmp/__init__.py +++ b/python/qemu/qmp/__init__.py @@ -39,7 +39,8 @@ from .qmp_client import ExecInterruptedError, ExecuteError, QMPClient logging.getLogger('qemu.qmp').addHandler(logging.NullHandler()) -# The order of these fields impact the Sphinx documentation order. +# IMPORTANT: When modifying this list, update the Sphinx overview docs. +# Anything visible in the qemu.qmp namespace should be on the overview page. __all__ = ( # Classes, most to least important 'QMPClient', diff --git a/python/qemu/qmp/events.py b/python/qemu/qmp/events.py index 6658349619..cfb5f0ac62 100644 --- a/python/qemu/qmp/events.py +++ b/python/qemu/qmp/events.py @@ -12,7 +12,14 @@ EventListener Tutorial ---------------------- In all of the following examples, we assume that we have a `QMPClient` -instantiated named ``qmp`` that is already connected. +instantiated named ``qmp`` that is already connected. For example: + +.. code:: python + + from qemu.qmp import QMPClient + + qmp = QMPClient('example-vm') + await qmp.connect('127.0.0.1', 1234) `listener()` context blocks with one name @@ -87,7 +94,9 @@ This is analogous to the following code: event = listener.get() print(f"Event arrived: {event['event']}") -This event stream will never end, so these blocks will never terminate. +This event stream will never end, so these blocks will never +terminate. Even if the QMP connection errors out prematurely, this +listener will go silent without raising an error. Using asyncio.Task to concurrently retrieve events @@ -227,16 +236,20 @@ Clearing listeners .. code:: python await qmp.execute('stop') - qmp.events.clear() + discarded = qmp.events.clear() await qmp.execute('cont') event = await qmp.events.get() assert event['event'] == 'RESUME' + assert discarded[0]['event'] == 'STOP' `EventListener` objects are FIFO queues. If events are not consumed, they will remain in the queue until they are witnessed or discarded via `clear()`. FIFO queues will be drained automatically upon leaving a context block, or when calling `remove_listener()`. +Any events removed from the queue in this fashion will be returned by +the clear call. + Accessing listener history ~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -350,6 +363,12 @@ While `listener()` is only capable of creating a single listener, break +Note that in the above example, we explicitly wait on jobA to conclude +first, and then wait for jobB to do the same. All we have guaranteed is +that the code that waits for jobA will not accidentally consume the +event intended for the jobB waiter. + + Extending the `EventListener` class ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -407,13 +426,13 @@ Experimental Interfaces & Design Issues These interfaces are not ones I am sure I will keep or otherwise modify heavily. -qmp.listener()’s type signature +qmp.listen()’s type signature ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -`listener()` does not return anything, because it was assumed the caller +`listen()` does not return anything, because it was assumed the caller already had a handle to the listener. However, for -``qmp.listener(EventListener())`` forms, the caller will not have saved -a handle to the listener. +``qmp.listen(EventListener())`` forms, the caller will not have saved a +handle to the listener. Because this function can accept *many* listeners, I found it hard to accurately type in a way where it could be used in both “one” or “many” @@ -633,7 +652,7 @@ class Events: def __init__(self) -> None: self._listeners: List[EventListener] = [] - #: Default, all-events `EventListener`. + #: Default, all-events `EventListener`. See `qmp.events` for more info. self.events: EventListener = EventListener() self.register_listener(self.events) diff --git a/python/qemu/qmp/legacy.py b/python/qemu/qmp/legacy.py index e46695ae2c..060ed0eb9d 100644 --- a/python/qemu/qmp/legacy.py +++ b/python/qemu/qmp/legacy.py @@ -293,8 +293,8 @@ class QEMUMonitorProtocol: """ Set the timeout for QMP RPC execution. - This timeout affects the `cmd`, `cmd_obj`, and `command` methods. - The `accept`, `pull_event` and `get_event` methods have their + This timeout affects the `cmd`, `cmd_obj`, and `cmd_raw` methods. + The `accept`, `pull_event` and `get_events` methods have their own configurable timeouts. :param timeout: diff --git a/python/qemu/qmp/message.py b/python/qemu/qmp/message.py index c2e9dd0dd5..dabb8ec360 100644 --- a/python/qemu/qmp/message.py +++ b/python/qemu/qmp/message.py @@ -28,7 +28,8 @@ class Message(MutableMapping[str, object]): be instantiated from either another mapping (like a `dict`), or from raw `bytes` that still need to be deserialized. - Once instantiated, it may be treated like any other MutableMapping:: + Once instantiated, it may be treated like any other + :py:obj:`~collections.abc.MutableMapping`:: >>> msg = Message(b'{"hello": "world"}') >>> assert msg['hello'] == 'world' @@ -50,12 +51,19 @@ class Message(MutableMapping[str, object]): >>> dict(msg) {'hello': 'world'} + Or pretty-printed:: + + >>> print(str(msg)) + { + "hello": "world" + } :param value: Initial value, if any. :param eager: When `True`, attempt to serialize or deserialize the initial value immediately, so that conversion exceptions are raised during the call to ``__init__()``. + """ # pylint: disable=too-many-ancestors diff --git a/python/qemu/qmp/models.py b/python/qemu/qmp/models.py index da52848d5a..7e0d0baf03 100644 --- a/python/qemu/qmp/models.py +++ b/python/qemu/qmp/models.py @@ -54,7 +54,7 @@ class Model: class Greeting(Model): """ - Defined in qmp-spec.rst, section "Server Greeting". + Defined in `interop/qmp-spec`, "Server Greeting" section. :param raw: The raw Greeting object. :raise KeyError: If any required fields are absent. @@ -82,7 +82,7 @@ class Greeting(Model): class QMPGreeting(Model): """ - Defined in qmp-spec.rst, section "Server Greeting". + Defined in `interop/qmp-spec`, "Server Greeting" section. :param raw: The raw QMPGreeting object. :raise KeyError: If any required fields are absent. @@ -104,7 +104,7 @@ class QMPGreeting(Model): class ErrorResponse(Model): """ - Defined in qmp-spec.rst, section "Error". + Defined in `interop/qmp-spec`, "Error" section. :param raw: The raw ErrorResponse object. :raise KeyError: If any required fields are absent. @@ -126,7 +126,7 @@ class ErrorResponse(Model): class ErrorInfo(Model): """ - Defined in qmp-spec.rst, section "Error". + Defined in `interop/qmp-spec`, "Error" section. :param raw: The raw ErrorInfo object. :raise KeyError: If any required fields are absent. diff --git a/python/qemu/qmp/protocol.py b/python/qemu/qmp/protocol.py index 4d8a39f014..219d092a79 100644 --- a/python/qemu/qmp/protocol.py +++ b/python/qemu/qmp/protocol.py @@ -79,6 +79,12 @@ class ConnectError(QMPError): This Exception always wraps a "root cause" exception that can be interrogated for additional information. + For example, when connecting to a non-existent socket:: + + await qmp.connect('not_found.sock') + # ConnectError: Failed to establish connection: + # [Errno 2] No such file or directory + :param error_message: Human-readable string describing the error. :param exc: The root-cause exception. """ @@ -102,8 +108,8 @@ class StateError(QMPError): An API command (connect, execute, etc) was issued at an inappropriate time. This error is raised when a command like - :py:meth:`~AsyncProtocol.connect()` is issued at an inappropriate - time. + :py:meth:`~AsyncProtocol.connect()` is called when the client is + already connected. :param error_message: Human-readable string describing the state violation. :param state: The actual `Runstate` seen at the time of the violation. @@ -298,7 +304,7 @@ class AsyncProtocol(Generic[T]): @upper_half async def runstate_changed(self) -> Runstate: """ - Wait for the `runstate` to change, then return that runstate. + Wait for the `runstate` to change, then return that `Runstate`. """ await self._runstate_event.wait() return self.runstate @@ -312,9 +318,9 @@ class AsyncProtocol(Generic[T]): """ Accept a connection and begin processing message queues. - If this call fails, `runstate` is guaranteed to be set back to `IDLE`. - This method is precisely equivalent to calling `start_server()` - followed by `accept()`. + If this call fails, `runstate` is guaranteed to be set back to + `IDLE`. This method is precisely equivalent to calling + `start_server()` followed by :py:meth:`~AsyncProtocol.accept()`. :param address: Address to listen on; UNIX socket path or TCP address/port. @@ -327,7 +333,8 @@ class AsyncProtocol(Generic[T]): This exception will wrap a more concrete one. In most cases, the wrapped exception will be `OSError` or `EOFError`. If a protocol-level failure occurs while establishing a new - session, the wrapped error may also be an `QMPError`. + session, the wrapped error may also be a `QMPError`. + """ await self.start_server(address, ssl) await self.accept() @@ -343,8 +350,8 @@ class AsyncProtocol(Generic[T]): This method starts listening for an incoming connection, but does not block waiting for a peer. This call will return immediately after binding and listening on a socket. A later - call to `accept()` must be made in order to finalize the - incoming connection. + call to :py:meth:`~AsyncProtocol.accept()` must be made in order + to finalize the incoming connection. :param address: Address to listen on; UNIX socket path or TCP address/port. @@ -367,10 +374,12 @@ class AsyncProtocol(Generic[T]): """ Accept an incoming connection and begin processing message queues. - If this call fails, `runstate` is guaranteed to be set back to `IDLE`. + Used after a previous call to `start_server()` to accept an + incoming connection. If this call fails, `runstate` is + guaranteed to be set back to `IDLE`. :raise StateError: When the `Runstate` is not `CONNECTING`. - :raise QMPError: When `start_server()` was not called yet. + :raise QMPError: When `start_server()` was not called first. :raise ConnectError: When a connection or session cannot be established. @@ -423,7 +432,11 @@ class AsyncProtocol(Generic[T]): If there was an exception that caused the reader/writers to terminate prematurely, it will be raised here. - :raise Exception: When the reader or writer terminate unexpectedly. + :raise Exception: + When the reader or writer terminate unexpectedly. You can + expect to see `EOFError` if the server hangs up, or + `OSError` for connection-related issues. If there was a QMP + protocol-level problem, `ProtocolError` will be seen. """ self.logger.debug("disconnect() called.") self._schedule_disconnect() diff --git a/python/qemu/qmp/qmp_client.py b/python/qemu/qmp/qmp_client.py index d826331b6d..8beccfe29d 100644 --- a/python/qemu/qmp/qmp_client.py +++ b/python/qemu/qmp/qmp_client.py @@ -70,6 +70,17 @@ class ExecuteError(QMPError): """ Exception raised by `QMPClient.execute()` on RPC failure. + This exception is raised when the server received, interpreted, and + replied to a command successfully; but the command itself returned a + failure status. + + For example:: + + await qmp.execute('block-dirty-bitmap-add', + {'node': 'foo', 'name': 'my_bitmap'}) + # qemu.qmp.qmp_client.ExecuteError: + # Cannot find device='foo' nor node-name='foo' + :param error_response: The RPC error response object. :param sent: The sent RPC message that caused the failure. :param received: The raw RPC error reply received. @@ -99,9 +110,22 @@ class ExecInterruptedError(QMPError): This error is raised when an `execute()` statement could not be completed. This can occur because the connection itself was - terminated before a reply was received. + terminated before a reply was received. The true cause of the + interruption will be available via `disconnect()`. - The true cause of the interruption will be available via `disconnect()`. + The QMP protocol does not make it possible to know if a command + succeeded or failed after such an event; the client will need to + query the server to determine the state of the server on a + case-by-case basis. + + For example, ECONNRESET might look like this:: + + try: + await qmp.execute('query-block') + # ExecInterruptedError: Disconnected + except ExecInterruptedError: + await qmp.disconnect() + # ConnectionResetError: [Errno 104] Connection reset by peer """ @@ -162,13 +186,14 @@ class BadReplyError(_MsgProtocolError): class QMPClient(AsyncProtocol[Message], Events): - """ - Implements a QMP client connection. + """Implements a QMP client connection. - QMP can be used to establish a connection as either the transport - client or server, though this class always acts as the QMP client. + `QMPClient` can be used to either connect or listen to a QMP server, + but always acts as the QMP client. - :param name: Optional nickname for the connection, used for logging. + :param name: + Optional nickname for the connection, used to differentiate + instances when logging. :param readbuflen: The maximum buffer length for reads and writes to and from the QMP @@ -178,14 +203,21 @@ class QMPClient(AsyncProtocol[Message], Events): Basic script-style usage looks like this:: - qmp = QMPClient('my_virtual_machine_name') - await qmp.connect(('127.0.0.1', 1234)) - ... - res = await qmp.execute('block-query') - ... - await qmp.disconnect() + import asyncio + from qemu.qmp import QMPClient - Basic async client-style usage looks like this:: + async def main(): + qmp = QMPClient('my_virtual_machine_name') + await qmp.connect(('127.0.0.1', 1234)) + ... + res = await qmp.execute('query-block') + ... + await qmp.disconnect() + + asyncio.run(main()) + + A more advanced example that starts to take advantage of asyncio + might look like this:: class Client: def __init__(self, name: str): @@ -205,6 +237,7 @@ class QMPClient(AsyncProtocol[Message], Events): await self.disconnect() See `qmp.events` for more detail on event handling patterns. + """ #: Logger object used for debugging messages. logger = logging.getLogger(__name__) @@ -224,10 +257,12 @@ class QMPClient(AsyncProtocol[Message], Events): Events.__init__(self) #: Whether or not to await a greeting after establishing a connection. + #: Defaults to True; QGA servers expect this to be False. self.await_greeting: bool = True - #: Whether or not to perform capabilities negotiation upon connection. - #: Implies `await_greeting`. + #: Whether or not to perform capabilities negotiation upon + #: connection. Implies `await_greeting`. Defaults to True; QGA + #: servers expect this to be False. self.negotiate: bool = True # Cached Greeting, if one was awaited. @@ -244,7 +279,13 @@ class QMPClient(AsyncProtocol[Message], Events): @property def greeting(self) -> Optional[Greeting]: - """The `Greeting` from the QMP server, if any.""" + """ + The `Greeting` from the QMP server, if any. + + Defaults to ``None``, and will be set after a greeting is + received during the connection process. It is reset at the start + of each connection attempt. + """ return self._greeting @upper_half @@ -385,7 +426,7 @@ class QMPClient(AsyncProtocol[Message], Events): # This is very likely a server parsing error. # It doesn't inherently belong to any pending execution. # Instead of performing clever recovery, just terminate. - # See "NOTE" in qmp-spec.rst, section "Error". + # See "NOTE" in interop/qmp-spec, "Error" section. raise ServerParseError( ("Server sent an error response without an ID, " "but there are no ID-less executions pending. " @@ -393,7 +434,7 @@ class QMPClient(AsyncProtocol[Message], Events): msg ) - # qmp-spec.rst, section "Commands Responses": + # qmp-spec.rst, "Commands Responses" section: # 'Clients should drop all the responses # that have an unknown "id" field.' self.logger.log( @@ -566,7 +607,7 @@ class QMPClient(AsyncProtocol[Message], Events): @require(Runstate.RUNNING) async def execute_msg(self, msg: Message) -> object: """ - Execute a QMP command and return its value. + Execute a QMP command on the server and return its value. :param msg: The QMP `Message` to execute. @@ -578,7 +619,9 @@ class QMPClient(AsyncProtocol[Message], Events): If the QMP `Message` does not have either the 'execute' or 'exec-oob' fields set. :raise ExecuteError: When the server returns an error response. - :raise ExecInterruptedError: if the connection was terminated early. + :raise ExecInterruptedError: + If the connection was disrupted before + receiving a reply from the server. """ if not ('execute' in msg or 'exec-oob' in msg): raise ValueError("Requires 'execute' or 'exec-oob' message") @@ -617,9 +660,11 @@ class QMPClient(AsyncProtocol[Message], Events): :param cmd: QMP command name. :param arguments: Arguments (if any). Must be JSON-serializable. - :param oob: If `True`, execute "out of band". + :param oob: + If `True`, execute "out of band". See `interop/qmp-spec` + section "Out-of-band execution". - :return: An executable QMP `Message`. + :return: A QMP `Message` that can be executed with `execute_msg()`. """ msg = Message({'exec-oob' if oob else 'execute': cmd}) if arguments is not None: @@ -631,18 +676,22 @@ class QMPClient(AsyncProtocol[Message], Events): arguments: Optional[Mapping[str, object]] = None, oob: bool = False) -> object: """ - Execute a QMP command and return its value. + Execute a QMP command on the server and return its value. :param cmd: QMP command name. :param arguments: Arguments (if any). Must be JSON-serializable. - :param oob: If `True`, execute "out of band". + :param oob: + If `True`, execute "out of band". See `interop/qmp-spec` + section "Out-of-band execution". :return: The command execution return value from the server. The type of object returned depends on the command that was issued, though most in QEMU return a `dict`. :raise ExecuteError: When the server returns an error response. - :raise ExecInterruptedError: if the connection was terminated early. + :raise ExecInterruptedError: + If the connection was disrupted before + receiving a reply from the server. """ msg = self.make_execute_msg(cmd, arguments, oob=oob) return await self.execute_msg(msg) @@ -650,8 +699,20 @@ class QMPClient(AsyncProtocol[Message], Events): @upper_half @require(Runstate.RUNNING) def send_fd_scm(self, fd: int) -> None: - """ - Send a file descriptor to the remote via SCM_RIGHTS. + """Send a file descriptor to the remote via SCM_RIGHTS. + + This method does not close the file descriptor. + + :param fd: The file descriptor to send to QEMU. + + This is an advanced feature of QEMU where file descriptors can + be passed from client to server. This is usually used as a + security measure to isolate the QEMU process from being able to + open its own files. See the QMP commands ``getfd`` and + ``add-fd`` for more information. + + See `socket.socket.sendmsg` for more information on the Python + implementation for sending file descriptors over a UNIX socket. """ assert self._writer is not None sock = self._writer.transport.get_extra_info('socket') diff --git a/python/qemu/qmp/qmp_shell.py b/python/qemu/qmp/qmp_shell.py index c923ff09e1..f818800568 100644 --- a/python/qemu/qmp/qmp_shell.py +++ b/python/qemu/qmp/qmp_shell.py @@ -10,9 +10,15 @@ # """ -Low-level QEMU shell on top of QMP. +qmp-shell - An interactive QEMU shell powered by QMP -usage: qmp-shell [-h] [-H] [-N] [-v] [-p] qmp_server +qmp-shell offers a simple shell with a convenient shorthand syntax as an +alternative to typing JSON by hand. This syntax is not standardized and +is not meant to be used as a scriptable interface. This shorthand *may* +change incompatibly in the future, and it is strongly encouraged to use +the QMP library to provide API-stable scripting when needed. + +usage: qmp-shell [-h] [-H] [-v] [-p] [-l LOGFILE] [-N] qmp_server positional arguments: qmp_server < UNIX socket path | TCP address:port > @@ -20,41 +26,52 @@ positional arguments: optional arguments: -h, --help show this help message and exit -H, --hmp Use HMP interface - -N, --skip-negotiation - Skip negotiate (for qemu-ga) -v, --verbose Verbose (echo commands sent and received) -p, --pretty Pretty-print JSON + -l LOGFILE, --logfile LOGFILE + Save log of all QMP messages to PATH + -N, --skip-negotiation + Skip negotiate (for qemu-ga) +Usage +----- -Start QEMU with: +First, start QEMU with:: -# qemu [...] -qmp unix:./qmp-sock,server + > qemu [...] -qmp unix:./qmp-sock,server=on[,wait=off] -Run the shell: +Then run the shell, passing the address of the socket:: -$ qmp-shell ./qmp-sock + > qmp-shell ./qmp-sock -Commands have the following format: +Syntax +------ - < command-name > [ arg-name1=arg1 ] ... [ arg-nameN=argN ] +Commands have the following format:: -For example: + < command-name > [ arg-name1=arg1 ] ... [ arg-nameN=argN ] -(QEMU) device_add driver=e1000 id=net1 -{'return': {}} -(QEMU) +For example, to add a network device:: -key=value pairs also support Python or JSON object literal subset notations, -without spaces. Dictionaries/objects {} are supported as are arrays []. + (QEMU) device_add driver=e1000 id=net1 + {'return': {}} + (QEMU) - example-command arg-name1={'key':'value','obj'={'prop':"value"}} +key=value pairs support either Python or JSON object literal notations, +**without spaces**. Dictionaries/objects ``{}`` are supported, as are +arrays ``[]``:: -Both JSON and Python formatting should work, including both styles of -string literal quotes. Both paradigms of literal values should work, -including null/true/false for JSON and None/True/False for Python. + example-command arg-name1={'key':'value','obj'={'prop':"value"}} +Either JSON or Python formatting for compound values works, including +both styles of string literal quotes (either single or double +quotes). Both paradigms of literal values are accepted, including +``null/true/false`` for JSON and ``None/True/False`` for Python. -Transactions have the following multi-line format: +Transactions +------------ + +Transactions have the following multi-line format:: transaction( action-name1 [ arg-name1=arg1 ] ... [arg-nameN=argN ] @@ -62,11 +79,11 @@ Transactions have the following multi-line format: action-nameN [ arg-name1=arg1 ] ... [arg-nameN=argN ] ) -One line transactions are also supported: +One line transactions are also supported:: transaction( action-name1 ... ) -For example: +For example:: (QEMU) transaction( TRANS> block-dirty-bitmap-add node=drive0 name=bitmap1 @@ -75,9 +92,35 @@ For example: {"return": {}} (QEMU) -Use the -v and -p options to activate the verbose and pretty-print options, -which will echo back the properly formatted JSON-compliant QMP that is being -sent to QEMU, which is useful for debugging and documentation generation. +Commands +-------- + +Autocomplete of command names using is supported. Pressing +at a blank CLI prompt will show you a list of all available commands +that the connected QEMU instance supports. + +For documentation on QMP commands and their arguments, please see +`qmp ref`. + +Events +------ + +qmp-shell will display events received from the server, but this version +does not do so asynchronously. To check for new events from the server, +press on a blank line:: + + (QEMU) ⏎ + {'timestamp': {'seconds': 1660071944, 'microseconds': 184667}, + 'event': 'STOP'} + +Display options +--------------- + +Use the -v and -p options to activate the verbose and pretty-print +options, which will echo back the properly formatted JSON-compliant QMP +that is being sent to QEMU. This is useful for debugging to see the +wire-level QMP data being exchanged, and generating output for use in +writing documentation for QEMU. """ import argparse @@ -525,6 +568,8 @@ def common_parser() -> argparse.ArgumentParser: help='Pretty-print JSON') parser.add_argument('-l', '--logfile', help='Save log of all QMP messages to PATH') + # NOTE: When changing arguments, update both this module docstring + # and the manpage synopsis in docs/man/qmp_shell.rst. return parser @@ -567,8 +612,35 @@ def main() -> None: def main_wrap() -> None: """ - qmp-shell-wrap entry point: parse command line arguments and - start the REPL. + qmp-shell-wrap - QEMU + qmp-shell launcher utility + + Launch QEMU and connect to it with `qmp-shell` in a single command. + CLI arguments will be forwarded to qemu, with additional arguments + added to allow `qmp-shell` to then connect to the recently launched + QEMU instance. + + usage: qmp-shell-wrap [-h] [-H] [-v] [-p] [-l LOGFILE] ... + + positional arguments: + command QEMU command line to invoke + + optional arguments: + -h, --help show this help message and exit + -H, --hmp Use HMP interface + -v, --verbose Verbose (echo commands sent and received) + -p, --pretty Pretty-print JSON + -l LOGFILE, --logfile LOGFILE + Save log of all QMP messages to PATH + + Usage + ----- + + Prepend "qmp-shell-wrap" to your usual QEMU command line:: + + > qmp-shell-wrap qemu-system-x86_64 -M q35 -m 4096 -display none + Welcome to the QMP low-level shell! + Connected + (QEMU) """ parser = common_parser() parser.add_argument('command', nargs=argparse.REMAINDER, diff --git a/python/qemu/qmp/util.py b/python/qemu/qmp/util.py index 47ec39a8b5..a8229e5524 100644 --- a/python/qemu/qmp/util.py +++ b/python/qemu/qmp/util.py @@ -49,7 +49,7 @@ def get_or_create_event_loop() -> asyncio.AbstractEventLoop: async def flush(writer: asyncio.StreamWriter) -> None: """ - Utility function to ensure a StreamWriter is *fully* drained. + Utility function to ensure an `asyncio.StreamWriter` is *fully* drained. `asyncio.StreamWriter.drain` only promises we will return to below the "high-water mark". This function ensures we flush the entire @@ -89,7 +89,7 @@ def bottom_half(func: T) -> T: These methods do not, in general, have the ability to directly report information to a caller’s context and will usually be - collected as a Task result instead. + collected as an `asyncio.Task` result instead. They must not call upper-half functions directly. """ @@ -105,8 +105,11 @@ def exception_summary(exc: BaseException) -> str: """ Return a summary string of an arbitrary exception. - It will be of the form "ExceptionType: Error Message", if the error + It will be of the form "ExceptionType: Error Message" if the error string is non-empty, and just "ExceptionType" otherwise. + + This code is based on CPython's implementation of + `traceback.TracebackException.format_exception_only`. """ name = type(exc).__qualname__ smod = type(exc).__module__ From 82c7cb93c750196f513a1b11cb85e0328bad444f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 15 Jul 2025 15:30:16 +0100 Subject: [PATCH 0397/2396] iotests: drop compat for old version context manager MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Our minimum python is now 3.9, so back compat with prior python versions is no longer required. Signed-off-by: Daniel P. Berrangé --- tests/qemu-iotests/testenv.py | 7 ++----- tests/qemu-iotests/testrunner.py | 9 ++------- 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/tests/qemu-iotests/testenv.py b/tests/qemu-iotests/testenv.py index 6326e46b7b..29caaa8a34 100644 --- a/tests/qemu-iotests/testenv.py +++ b/tests/qemu-iotests/testenv.py @@ -22,15 +22,12 @@ import tempfile from pathlib import Path import shutil import collections +import contextlib import random import subprocess import glob from typing import List, Dict, Any, Optional -if sys.version_info >= (3, 9): - from contextlib import AbstractContextManager as ContextManager -else: - from typing import ContextManager DEF_GDB_OPTIONS = 'localhost:12345' @@ -58,7 +55,7 @@ def get_default_machine(qemu_prog: str) -> str: return default_machine -class TestEnv(ContextManager['TestEnv']): +class TestEnv(contextlib.AbstractContextManager['TestEnv']): """ Manage system environment for running tests diff --git a/tests/qemu-iotests/testrunner.py b/tests/qemu-iotests/testrunner.py index 2e236c8fa3..14cc8492f9 100644 --- a/tests/qemu-iotests/testrunner.py +++ b/tests/qemu-iotests/testrunner.py @@ -30,11 +30,6 @@ from multiprocessing import Pool from typing import List, Optional, Any, Sequence, Dict from testenv import TestEnv -if sys.version_info >= (3, 9): - from contextlib import AbstractContextManager as ContextManager -else: - from typing import ContextManager - def silent_unlink(path: Path) -> None: try: @@ -57,7 +52,7 @@ def file_diff(file1: str, file2: str) -> List[str]: return res -class LastElapsedTime(ContextManager['LastElapsedTime']): +class LastElapsedTime(contextlib.AbstractContextManager['LastElapsedTime']): """ Cache for elapsed time for tests, to show it during new test run It is safe to use get() at any time. To use update(), you must either @@ -112,7 +107,7 @@ class TestResult: self.interrupted = interrupted -class TestRunner(ContextManager['TestRunner']): +class TestRunner(contextlib.AbstractContextManager['TestRunner']): shared_self = None @staticmethod From 6ccb48ffc19fe25511313a246d4a8bad51114ea9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 15 Jul 2025 15:30:20 +0100 Subject: [PATCH 0398/2396] python: ensure QEMUQtestProtocol closes its socket MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While QEMUQtestMachine closes the socket that was passed to QEMUQtestProtocol, the python resource leak manager still believes that the copy QEMUQtestProtocol holds is open. We must explicitly call close to avoid this leak warnnig. Signed-off-by: Daniel P. Berrangé --- python/qemu/machine/qtest.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/python/qemu/machine/qtest.py b/python/qemu/machine/qtest.py index 4f5ede85b2..781f674ffa 100644 --- a/python/qemu/machine/qtest.py +++ b/python/qemu/machine/qtest.py @@ -177,6 +177,8 @@ class QEMUQtestMachine(QEMUMachine): self._qtest_sock_pair[0].close() self._qtest_sock_pair[1].close() self._qtest_sock_pair = None + if self._qtest is not None: + self._qtest.close() super()._post_shutdown() def qtest(self, cmd: str) -> str: From d4d0ebfcc926c11d16320d0d5accf22e3441c115 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 15 Jul 2025 15:30:21 +0100 Subject: [PATCH 0399/2396] iotests/147: ensure temporary sockets are closed before exiting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This avoids the python resource leak detector from issuing warnings in the iotests. Signed-off-by: Daniel P. Berrangé --- tests/qemu-iotests/147 | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/qemu-iotests/147 b/tests/qemu-iotests/147 index 6d6f077a14..3e14bd389a 100755 --- a/tests/qemu-iotests/147 +++ b/tests/qemu-iotests/147 @@ -277,6 +277,7 @@ class BuiltinNBD(NBDBlockdevAddBase): } } self.client_test(filename, flatten_sock_addr(address), 'nbd-export') + sockfd.close() self._server_down() From 2b2fb25c2aaf5b2e8172d845db39cc50a951a12e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 15 Jul 2025 15:30:22 +0100 Subject: [PATCH 0400/2396] iotests/151: ensure subprocesses are cleaned up MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The iotest 151 creates a bunch of subprocesses, with their stdout connected to a pipe but never reads any data from them and does not gurantee the processes are killed on cleanup. This triggers resource leak warnings from python when the subprocess.Popen object is garbage collected. Signed-off-by: Daniel P. Berrangé --- tests/qemu-iotests/151 | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/qemu-iotests/151 b/tests/qemu-iotests/151 index f2ff9c5dac..06ee3585db 100755 --- a/tests/qemu-iotests/151 +++ b/tests/qemu-iotests/151 @@ -263,6 +263,11 @@ class TestThrottledWithNbdExportBase(iotests.QMPTestCase): break except subprocess.TimeoutExpired: self.vm.qtest(f'clock_step {1 * 1000 * 1000 * 1000}') + try: + p.kill() + p.stdout.close() + except: + pass except IndexError: pass From 9a494d83538680651197651031375c2b6fa2490b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 15 Jul 2025 15:30:23 +0100 Subject: [PATCH 0401/2396] iotests/check: always enable all python warnings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Of most importance is that this gives us a heads-up if anything we rely on has been deprecated. The default python behaviour only emits a warning if triggered from __main__ which is very limited. Setting the env variable further ensures that any python child processes will also display warnings. Signed-off-by: Daniel P. Berrangé --- tests/qemu-iotests/check | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/qemu-iotests/check b/tests/qemu-iotests/check index 545f9ec7bd..d9b7c1d598 100755 --- a/tests/qemu-iotests/check +++ b/tests/qemu-iotests/check @@ -21,6 +21,7 @@ import sys import argparse import shutil from pathlib import Path +import warnings from findtests import TestFinder from testenv import TestEnv @@ -137,6 +138,9 @@ def make_argparser() -> argparse.ArgumentParser: if __name__ == '__main__': + warnings.simplefilter("default") + os.environ["PYTHONWARNINGS"] = "default" + args = make_argparser().parse_args() env = TestEnv(source_dir=args.source_dir, From 424dc390ec68263b5fc82b88f0f81bc3f374ad44 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 11 Sep 2025 13:14:15 +0100 Subject: [PATCH 0402/2396] tests, scripts: Don't import print_function from __future__ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some of our Python scripts still include the line from __future__ import print_function which is intended to allow a Python 2 to handle the Python 3 print() syntax. This particular part of the future arrived many years ago, and our minimum Python version is 3.9, so we don't need to keep this line around. NB: the scripts in tests/tcg/*/gdbstub/ are run with whatever Python gdb was built against, but we can safely assume that that was a Python 3 because our supported distros are all on Python 3. In any case these are only run as part of "make check-tcg", not by end-users. Commit created with: sed -i -e '/import print_function/d' $(git grep -l 'from __future__') Signed-off-by: Peter Maydell Reviewed-by: Daniel P. Berrangé Reviewed-by: John Snow Reviewed-by: Philippe Mathieu-Daudé Message-id: 20250819102409.2117969-1-peter.maydell@linaro.org --- scripts/userfaultfd-wrlat.py | 1 - tests/guest-debug/test_gdbstub.py | 1 - tests/tcg/aarch64/gdbstub/test-mte.py | 1 - tests/tcg/aarch64/gdbstub/test-sve-ioctl.py | 1 - tests/tcg/aarch64/gdbstub/test-sve.py | 1 - tests/tcg/multiarch/gdbstub/interrupt.py | 1 - tests/tcg/multiarch/gdbstub/memory.py | 1 - tests/tcg/multiarch/gdbstub/sha1.py | 1 - tests/tcg/multiarch/gdbstub/test-proc-mappings.py | 1 - tests/tcg/multiarch/gdbstub/test-qxfer-auxv-read.py | 1 - tests/tcg/multiarch/gdbstub/test-qxfer-siginfo-read.py | 1 - tests/tcg/multiarch/gdbstub/test-thread-breakpoint.py | 1 - tests/tcg/s390x/gdbstub/test-signals-s390x.py | 1 - tests/tcg/s390x/gdbstub/test-svc.py | 1 - 14 files changed, 14 deletions(-) diff --git a/scripts/userfaultfd-wrlat.py b/scripts/userfaultfd-wrlat.py index 0684be4e04..a61a9abbfc 100755 --- a/scripts/userfaultfd-wrlat.py +++ b/scripts/userfaultfd-wrlat.py @@ -17,7 +17,6 @@ # 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 __future__ import print_function from bcc import BPF from ctypes import c_ushort, c_int, c_ulonglong from time import sleep diff --git a/tests/guest-debug/test_gdbstub.py b/tests/guest-debug/test_gdbstub.py index 4f08089e6a..e017ccb55d 100644 --- a/tests/guest-debug/test_gdbstub.py +++ b/tests/guest-debug/test_gdbstub.py @@ -1,7 +1,6 @@ """Helper functions for gdbstub testing """ -from __future__ import print_function import argparse import gdb import os diff --git a/tests/tcg/aarch64/gdbstub/test-mte.py b/tests/tcg/aarch64/gdbstub/test-mte.py index 9ad98e7a54..f4a7d7b446 100644 --- a/tests/tcg/aarch64/gdbstub/test-mte.py +++ b/tests/tcg/aarch64/gdbstub/test-mte.py @@ -1,4 +1,3 @@ -from __future__ import print_function # # Test GDB memory-tag commands that exercise the stubs for the qIsAddressTagged, # qMemTag, and QMemTag packets, which are used for manipulating allocation tags. diff --git a/tests/tcg/aarch64/gdbstub/test-sve-ioctl.py b/tests/tcg/aarch64/gdbstub/test-sve-ioctl.py index a78a3a2514..2c5c218031 100644 --- a/tests/tcg/aarch64/gdbstub/test-sve-ioctl.py +++ b/tests/tcg/aarch64/gdbstub/test-sve-ioctl.py @@ -1,4 +1,3 @@ -from __future__ import print_function # # Test the SVE ZReg reports the right amount of data. It uses the # sve-ioctl test and examines the register data each time the diff --git a/tests/tcg/aarch64/gdbstub/test-sve.py b/tests/tcg/aarch64/gdbstub/test-sve.py index 84cdcd4a32..7b0489a622 100644 --- a/tests/tcg/aarch64/gdbstub/test-sve.py +++ b/tests/tcg/aarch64/gdbstub/test-sve.py @@ -1,4 +1,3 @@ -from __future__ import print_function # # Test the SVE registers are visible and changeable via gdbstub # diff --git a/tests/tcg/multiarch/gdbstub/interrupt.py b/tests/tcg/multiarch/gdbstub/interrupt.py index 2d5654d154..4eccdb41b9 100644 --- a/tests/tcg/multiarch/gdbstub/interrupt.py +++ b/tests/tcg/multiarch/gdbstub/interrupt.py @@ -1,4 +1,3 @@ -from __future__ import print_function # # Test some of the system debug features with the multiarch memory # test. It is a port of the original vmlinux focused test case but diff --git a/tests/tcg/multiarch/gdbstub/memory.py b/tests/tcg/multiarch/gdbstub/memory.py index 532b92e7fb..76d75e5251 100644 --- a/tests/tcg/multiarch/gdbstub/memory.py +++ b/tests/tcg/multiarch/gdbstub/memory.py @@ -1,4 +1,3 @@ -from __future__ import print_function # # Test some of the system debug features with the multiarch memory # test. It is a port of the original vmlinux focused test case but diff --git a/tests/tcg/multiarch/gdbstub/sha1.py b/tests/tcg/multiarch/gdbstub/sha1.py index 1ce711a402..3403b82fd4 100644 --- a/tests/tcg/multiarch/gdbstub/sha1.py +++ b/tests/tcg/multiarch/gdbstub/sha1.py @@ -1,4 +1,3 @@ -from __future__ import print_function # # A very simple smoke test for debugging the SHA1 userspace test on # each target. diff --git a/tests/tcg/multiarch/gdbstub/test-proc-mappings.py b/tests/tcg/multiarch/gdbstub/test-proc-mappings.py index 6eb6ebf7b1..796dca75f0 100644 --- a/tests/tcg/multiarch/gdbstub/test-proc-mappings.py +++ b/tests/tcg/multiarch/gdbstub/test-proc-mappings.py @@ -1,7 +1,6 @@ """Test that gdbstub has access to proc mappings. This runs as a sourced script (via -x, via run-test.py).""" -from __future__ import print_function import gdb from test_gdbstub import gdb_exit, main, report diff --git a/tests/tcg/multiarch/gdbstub/test-qxfer-auxv-read.py b/tests/tcg/multiarch/gdbstub/test-qxfer-auxv-read.py index 00c26ab4a9..fa36c943d6 100644 --- a/tests/tcg/multiarch/gdbstub/test-qxfer-auxv-read.py +++ b/tests/tcg/multiarch/gdbstub/test-qxfer-auxv-read.py @@ -1,4 +1,3 @@ -from __future__ import print_function # # Test auxiliary vector is loaded via gdbstub # diff --git a/tests/tcg/multiarch/gdbstub/test-qxfer-siginfo-read.py b/tests/tcg/multiarch/gdbstub/test-qxfer-siginfo-read.py index 862596b07a..b18fa1234f 100644 --- a/tests/tcg/multiarch/gdbstub/test-qxfer-siginfo-read.py +++ b/tests/tcg/multiarch/gdbstub/test-qxfer-siginfo-read.py @@ -1,4 +1,3 @@ -from __future__ import print_function # # Test gdbstub Xfer:siginfo:read stub. # diff --git a/tests/tcg/multiarch/gdbstub/test-thread-breakpoint.py b/tests/tcg/multiarch/gdbstub/test-thread-breakpoint.py index 4d6b6b9fbe..49cbc3548f 100644 --- a/tests/tcg/multiarch/gdbstub/test-thread-breakpoint.py +++ b/tests/tcg/multiarch/gdbstub/test-thread-breakpoint.py @@ -1,4 +1,3 @@ -from __future__ import print_function # # Test auxiliary vector is loaded via gdbstub # diff --git a/tests/tcg/s390x/gdbstub/test-signals-s390x.py b/tests/tcg/s390x/gdbstub/test-signals-s390x.py index b6b7b39fc4..398ad534eb 100644 --- a/tests/tcg/s390x/gdbstub/test-signals-s390x.py +++ b/tests/tcg/s390x/gdbstub/test-signals-s390x.py @@ -1,4 +1,3 @@ -from __future__ import print_function # # Test that signals and debugging mix well together on s390x. diff --git a/tests/tcg/s390x/gdbstub/test-svc.py b/tests/tcg/s390x/gdbstub/test-svc.py index 17210b4e02..29a0aa0ede 100644 --- a/tests/tcg/s390x/gdbstub/test-svc.py +++ b/tests/tcg/s390x/gdbstub/test-svc.py @@ -1,7 +1,6 @@ """Test single-stepping SVC. 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 71eba045758289e12133c4977f81c9132325c648 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 30 Aug 2025 15:40:05 +1000 Subject: [PATCH 0403/2396] linux-user/aarch64: Split out signal_for_exception MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-id: 20250830054128.448363-2-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- linux-user/aarch64/cpu_loop.c | 84 ++++++++++++++++++++--------------- 1 file changed, 47 insertions(+), 37 deletions(-) diff --git a/linux-user/aarch64/cpu_loop.c b/linux-user/aarch64/cpu_loop.c index 4c4921152e..3b2782581b 100644 --- a/linux-user/aarch64/cpu_loop.c +++ b/linux-user/aarch64/cpu_loop.c @@ -27,11 +27,56 @@ #include "target/arm/syndrome.h" #include "target/arm/cpu-features.h" +/* Use the exception syndrome to map a cpu exception to a signal. */ +static void signal_for_exception(CPUARMState *env, vaddr addr) +{ + uint32_t syn = env->exception.syndrome; + int si_code, si_signo; + + switch (syn_get_ec(syn)) { + case EC_DATAABORT: + case EC_INSNABORT: + /* Both EC have the same format for FSC, or close enough. */ + switch (extract32(syn, 0, 6)) { + case 0x04 ... 0x07: /* Translation fault, level {0-3} */ + si_signo = TARGET_SIGSEGV; + si_code = TARGET_SEGV_MAPERR; + break; + case 0x09 ... 0x0b: /* Access flag fault, level {1-3} */ + case 0x0d ... 0x0f: /* Permission fault, level {1-3} */ + si_signo = TARGET_SIGSEGV; + si_code = TARGET_SEGV_ACCERR; + break; + case 0x11: /* Synchronous Tag Check Fault */ + si_signo = TARGET_SIGSEGV; + si_code = TARGET_SEGV_MTESERR; + break; + case 0x21: /* Alignment fault */ + si_signo = TARGET_SIGBUS; + si_code = TARGET_BUS_ADRALN; + break; + default: + g_assert_not_reached(); + } + break; + + case EC_PCALIGNMENT: + si_signo = TARGET_SIGBUS; + si_code = TARGET_BUS_ADRALN; + break; + + default: + g_assert_not_reached(); + } + + force_sig_fault(si_signo, si_code, addr); +} + /* AArch64 main loop */ void cpu_loop(CPUARMState *env) { CPUState *cs = env_cpu(env); - int trapnr, ec, fsc, si_code, si_signo; + int trapnr; abi_long ret; for (;;) { @@ -67,42 +112,7 @@ void cpu_loop(CPUARMState *env) break; case EXCP_PREFETCH_ABORT: case EXCP_DATA_ABORT: - ec = syn_get_ec(env->exception.syndrome); - switch (ec) { - case EC_DATAABORT: - case EC_INSNABORT: - /* Both EC have the same format for FSC, or close enough. */ - fsc = extract32(env->exception.syndrome, 0, 6); - switch (fsc) { - case 0x04 ... 0x07: /* Translation fault, level {0-3} */ - si_signo = TARGET_SIGSEGV; - si_code = TARGET_SEGV_MAPERR; - break; - case 0x09 ... 0x0b: /* Access flag fault, level {1-3} */ - case 0x0d ... 0x0f: /* Permission fault, level {1-3} */ - si_signo = TARGET_SIGSEGV; - si_code = TARGET_SEGV_ACCERR; - break; - case 0x11: /* Synchronous Tag Check Fault */ - si_signo = TARGET_SIGSEGV; - si_code = TARGET_SEGV_MTESERR; - break; - case 0x21: /* Alignment fault */ - si_signo = TARGET_SIGBUS; - si_code = TARGET_BUS_ADRALN; - break; - default: - g_assert_not_reached(); - } - break; - case EC_PCALIGNMENT: - si_signo = TARGET_SIGBUS; - si_code = TARGET_BUS_ADRALN; - break; - default: - g_assert_not_reached(); - } - force_sig_fault(si_signo, si_code, env->exception.vaddress); + signal_for_exception(env, env->exception.vaddress); break; case EXCP_DEBUG: case EXCP_BKPT: From 5fe3151c5e18eb15347c2ea572b551c7aed67a9f Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 30 Aug 2025 15:40:06 +1000 Subject: [PATCH 0404/2396] linux-user/aarch64: Check syndrome for EXCP_UDEF Note that we have been passing the incorrect code for most exception codes: uncategorized (do_el0_undef), systemregistertrap (do_el0_sys), smetrap (do_sme_acc), btitrap (do_el0_bti) and illegalstate (bad_el0_sync). Only pacfail uses ILL_ILLOPN (do_el0_fpac). Note that EC_MOP (do_el0_mops) ought not signal at all. For now, preserve existing behavior signalling ILL_ILLOPN. List all other exception codes and document why they do not apply to user-only. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-id: 20250830054128.448363-3-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- linux-user/aarch64/cpu_loop.c | 75 ++++++++++++++++++++++++++++++++++- 1 file changed, 74 insertions(+), 1 deletion(-) diff --git a/linux-user/aarch64/cpu_loop.c b/linux-user/aarch64/cpu_loop.c index 3b2782581b..7ad26316de 100644 --- a/linux-user/aarch64/cpu_loop.c +++ b/linux-user/aarch64/cpu_loop.c @@ -65,6 +65,79 @@ static void signal_for_exception(CPUARMState *env, vaddr addr) si_code = TARGET_BUS_ADRALN; break; + case EC_UNCATEGORIZED: /* E.g. undefined instruction */ + case EC_SYSTEMREGISTERTRAP: /* E.g. inaccessible register */ + case EC_SMETRAP: /* E.g. invalid insn in streaming state */ + case EC_BTITRAP: /* E.g. invalid guarded branch target */ + case EC_ILLEGALSTATE: + /* + * Illegal state happens via an ERET from a privileged mode, + * so is not normally possible from user-only. However, gdbstub + * is not prevented from writing CPSR_IL, aka PSTATE.IL, which + * would generate a trap from the next translated block. + * In the kernel, default case -> el0_inv -> bad_el0_sync. + */ + si_signo = TARGET_SIGILL; + si_code = TARGET_ILL_ILLOPC; + break; + + case EC_PACFAIL: + si_signo = TARGET_SIGILL; + si_code = TARGET_ILL_ILLOPN; + break; + + case EC_MOP: + /* + * FIXME: The kernel fixes up wrong-option exceptions. + * For QEMU linux-user mode, you can only get these if + * the process is doing something silly (not executing + * the MOPS instructions in the required P/M/E sequence), + * so it is not a problem in practice that we do not. + * + * We ought ideally to implement the same "rewind to the + * start of the sequence" logic that the kernel does in + * arm64_mops_reset_regs(). In the meantime, deliver + * the guest a SIGILL, with the same ILLOPN si_code + * we've always used for this. + */ + si_signo = TARGET_SIGILL; + si_code = TARGET_ILL_ILLOPN; + break; + + case EC_WFX_TRAP: /* user-only WFI implemented as NOP */ + case EC_CP15RTTRAP: /* AArch32 */ + case EC_CP15RRTTRAP: /* AArch32 */ + case EC_CP14RTTRAP: /* AArch32 */ + case EC_CP14DTTRAP: /* AArch32 */ + case EC_ADVSIMDFPACCESSTRAP: /* user-only does not disable fpu */ + case EC_FPIDTRAP: /* AArch32 */ + case EC_PACTRAP: /* user-only does not disable pac regs */ + case EC_BXJTRAP: /* AArch32 */ + case EC_CP14RRTTRAP: /* AArch32 */ + case EC_AA32_SVC: /* AArch32 */ + case EC_AA32_HVC: /* AArch32 */ + case EC_AA32_SMC: /* AArch32 */ + case EC_AA64_SVC: /* generates EXCP_SWI */ + case EC_AA64_HVC: /* user-only generates EC_UNCATEGORIZED */ + case EC_AA64_SMC: /* user-only generates EC_UNCATEGORIZED */ + case EC_SVEACCESSTRAP: /* user-only does not disable sve */ + case EC_ERETTRAP: /* user-only generates EC_UNCATEGORIZED */ + case EC_GPC: /* user-only has no EL3 gpc tables */ + case EC_INSNABORT_SAME_EL: /* el0 cannot trap to el0 */ + case EC_DATAABORT_SAME_EL: /* el0 cannot trap to el0 */ + case EC_SPALIGNMENT: /* sp alignment checks not implemented */ + case EC_AA32_FPTRAP: /* fp exceptions not implemented */ + case EC_AA64_FPTRAP: /* fp exceptions not implemented */ + case EC_SERROR: /* user-only does not have hw faults */ + case EC_BREAKPOINT: /* user-only does not have hw debug */ + case EC_BREAKPOINT_SAME_EL: /* user-only does not have hw debug */ + case EC_SOFTWARESTEP: /* user-only does not have hw debug */ + case EC_SOFTWARESTEP_SAME_EL: /* user-only does not have hw debug */ + case EC_WATCHPOINT: /* user-only does not have hw debug */ + case EC_WATCHPOINT_SAME_EL: /* user-only does not have hw debug */ + case EC_AA32_BKPT: /* AArch32 */ + case EC_VECTORCATCH: /* AArch32 */ + case EC_AA64_BKPT: /* generates EXCP_BKPT */ default: g_assert_not_reached(); } @@ -108,7 +181,7 @@ void cpu_loop(CPUARMState *env) /* just indicate that signals should be handled asap */ break; case EXCP_UDEF: - force_sig_fault(TARGET_SIGILL, TARGET_ILL_ILLOPN, env->pc); + signal_for_exception(env, env->pc); break; case EXCP_PREFETCH_ABORT: case EXCP_DATA_ABORT: From e1b31ba94d143c5026b199c5df7523fa4d9fa4cf Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 30 Aug 2025 15:40:07 +1000 Subject: [PATCH 0405/2396] linux-user/aarch64: Generate ESR signal records Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-id: 20250830054128.448363-4-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- linux-user/aarch64/cpu_loop.c | 3 +++ linux-user/aarch64/signal.c | 34 +++++++++++++++++++++++++++++++++- 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/linux-user/aarch64/cpu_loop.c b/linux-user/aarch64/cpu_loop.c index 7ad26316de..6060572eed 100644 --- a/linux-user/aarch64/cpu_loop.c +++ b/linux-user/aarch64/cpu_loop.c @@ -33,6 +33,9 @@ static void signal_for_exception(CPUARMState *env, vaddr addr) uint32_t syn = env->exception.syndrome; int si_code, si_signo; + /* Let signal delivery see that ESR is live. */ + env->cp15.esr_el[1] = syn; + switch (syn_get_ec(syn)) { case EC_DATAABORT: case EC_INSNABORT: diff --git a/linux-user/aarch64/signal.c b/linux-user/aarch64/signal.c index 668353bbda..ef97be3ac7 100644 --- a/linux-user/aarch64/signal.c +++ b/linux-user/aarch64/signal.c @@ -65,6 +65,13 @@ struct target_fpsimd_context { uint64_t vregs[32 * 2]; /* really uint128_t vregs[32] */ }; +#define TARGET_ESR_MAGIC 0x45535201 + +struct target_esr_context { + struct target_aarch64_ctx head; + uint64_t esr; +}; + #define TARGET_EXTRA_MAGIC 0x45585401 struct target_extra_context { @@ -201,6 +208,14 @@ static void target_setup_fpsimd_record(struct target_fpsimd_context *fpsimd, } } +static void target_setup_esr_record(struct target_esr_context *ctx, + CPUARMState *env) +{ + __put_user(TARGET_ESR_MAGIC, &ctx->head.magic); + __put_user(sizeof(*ctx), &ctx->head.size); + __put_user(env->cp15.esr_el[1], &ctx->esr); +} + static void target_setup_extra_record(struct target_extra_context *extra, uint64_t datap, uint32_t extra_size) { @@ -531,6 +546,9 @@ static int target_restore_sigframe(CPUARMState *env, fpsimd = (struct target_fpsimd_context *)ctx; break; + case TARGET_ESR_MAGIC: + break; /* ignore */ + case TARGET_SVE_MAGIC: if (sve || size < sizeof(struct target_sve_context)) { goto err; @@ -683,7 +701,7 @@ static void target_setup_frame(int usig, struct target_sigaction *ka, uc.tuc_mcontext.__reserved), }; int fpsimd_ofs, fr_ofs, sve_ofs = 0, za_ofs = 0, tpidr2_ofs = 0; - int zt_ofs = 0; + int zt_ofs = 0, esr_ofs = 0; int sve_size = 0, za_size = 0, tpidr2_size = 0, zt_size = 0; struct target_rt_sigframe *frame; struct target_rt_frame_record *fr; @@ -693,6 +711,15 @@ static void target_setup_frame(int usig, struct target_sigaction *ka, fpsimd_ofs = alloc_sigframe_space(sizeof(struct target_fpsimd_context), &layout); + /* + * In user mode, ESR_EL1 is only set by cpu_loop while queueing the + * signal, and it's only valid for the one sync insn. + */ + if (env->cp15.esr_el[1]) { + esr_ofs = alloc_sigframe_space(sizeof(struct target_esr_context), + &layout); + } + /* SVE state needs saving only if it exists. */ if (cpu_isar_feature(aa64_sve, env_archcpu(env)) || cpu_isar_feature(aa64_sme, env_archcpu(env))) { @@ -754,6 +781,11 @@ static void target_setup_frame(int usig, struct target_sigaction *ka, target_setup_general_frame(frame, env, set); target_setup_fpsimd_record((void *)frame + fpsimd_ofs, env); + if (esr_ofs) { + target_setup_esr_record((void *)frame + esr_ofs, env); + /* Leave ESR_EL1 clear while it's not relevant. */ + env->cp15.esr_el[1] = 0; + } target_setup_end_record((void *)frame + layout.std_end_ofs); if (layout.extra_ofs) { target_setup_extra_record((void *)frame + layout.extra_ofs, From 76fea609082d9673449a1f6aca9a28af6f20f8cf Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 30 Aug 2025 15:40:08 +1000 Subject: [PATCH 0406/2396] target/arm: Add prot_check parameter to pmsav8_mpu_lookup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Separate the access_type from the protection check. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-id: 20250830054128.448363-5-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/internals.h | 5 +++-- target/arm/ptw.c | 11 ++++++----- target/arm/tcg/m_helper.c | 4 ++-- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/target/arm/internals.h b/target/arm/internals.h index f5a1e75db3..899242e572 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -1624,8 +1624,9 @@ bool get_phys_addr_with_space_nogpc(CPUARMState *env, vaddr address, __attribute__((nonnull)); bool pmsav8_mpu_lookup(CPUARMState *env, uint32_t address, - MMUAccessType access_type, ARMMMUIdx mmu_idx, - bool is_secure, GetPhysAddrResult *result, + MMUAccessType access_type, unsigned prot_check, + ARMMMUIdx mmu_idx, bool is_secure, + GetPhysAddrResult *result, ARMMMUFaultInfo *fi, uint32_t *mregion); void arm_log_exception(CPUState *cs); diff --git a/target/arm/ptw.c b/target/arm/ptw.c index ed5c728eab..9652f40ff8 100644 --- a/target/arm/ptw.c +++ b/target/arm/ptw.c @@ -2561,8 +2561,9 @@ static uint32_t *regime_rlar(CPUARMState *env, ARMMMUIdx mmu_idx, } bool pmsav8_mpu_lookup(CPUARMState *env, uint32_t address, - MMUAccessType access_type, ARMMMUIdx mmu_idx, - bool secure, GetPhysAddrResult *result, + MMUAccessType access_type, unsigned prot_check, + ARMMMUIdx mmu_idx, bool secure, + GetPhysAddrResult *result, ARMMMUFaultInfo *fi, uint32_t *mregion) { /* @@ -2750,7 +2751,7 @@ bool pmsav8_mpu_lookup(CPUARMState *env, uint32_t address, if (arm_feature(env, ARM_FEATURE_M)) { fi->level = 1; } - return !(result->f.prot & (1 << access_type)); + return (prot_check & ~result->f.prot) != 0; } static bool v8m_is_sau_exempt(CPUARMState *env, @@ -2952,8 +2953,8 @@ static bool get_phys_addr_pmsav8(CPUARMState *env, } } - ret = pmsav8_mpu_lookup(env, address, access_type, mmu_idx, secure, - result, fi, NULL); + ret = pmsav8_mpu_lookup(env, address, access_type, 1 << access_type, + mmu_idx, secure, result, fi, NULL); if (sattrs.subpage) { result->f.lg_page_size = 0; } diff --git a/target/arm/tcg/m_helper.c b/target/arm/tcg/m_helper.c index 28307b5615..d856e3bc8e 100644 --- a/target/arm/tcg/m_helper.c +++ b/target/arm/tcg/m_helper.c @@ -2829,8 +2829,8 @@ uint32_t HELPER(v7m_tt)(CPUARMState *env, uint32_t addr, uint32_t op) ARMMMUFaultInfo fi = {}; /* We can ignore the return value as prot is always set */ - pmsav8_mpu_lookup(env, addr, MMU_DATA_LOAD, mmu_idx, targetsec, - &res, &fi, &mregion); + pmsav8_mpu_lookup(env, addr, MMU_DATA_LOAD, PAGE_READ, mmu_idx, + targetsec, &res, &fi, &mregion); if (mregion == -1) { mrvalid = false; mregion = 0; From 015cefc0ed65ef054616171128f5962f1cccb2d1 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 30 Aug 2025 15:40:09 +1000 Subject: [PATCH 0407/2396] target/arm: Add in_prot_check to S1Translate Separate the access_type from the protection check. Save the trouble of modifying all helper functions by passing the new data in the control structure. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-id: 20250830054128.448363-6-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/ptw.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/target/arm/ptw.c b/target/arm/ptw.c index 9652f40ff8..d37c0ce0f1 100644 --- a/target/arm/ptw.c +++ b/target/arm/ptw.c @@ -64,6 +64,12 @@ typedef struct S1Translate { * Stage 2 is indicated by in_mmu_idx set to ARMMMUIdx_Stage2{,_S}. */ bool in_s1_is_el0; + /* + * The set of PAGE_* bits to be use in the permission check. + * This is normally directly related to the access_type, but + * may be suppressed for debug or AT insns. + */ + uint8_t in_prot_check; bool out_rw; bool out_be; ARMSecuritySpace out_space; @@ -581,6 +587,7 @@ static bool S1_ptw_translate(CPUARMState *env, S1Translate *ptw, .in_ptw_idx = ptw_idx_for_stage_2(env, s2_mmu_idx), .in_space = s2_space, .in_debug = true, + .in_prot_check = PAGE_READ, }; GetPhysAddrResult s2 = { }; @@ -1089,7 +1096,7 @@ static bool get_phys_addr_v5(CPUARMState *env, S1Translate *ptw, } result->f.prot = ap_to_rw_prot(env, ptw->in_mmu_idx, ap, domain_prot); result->f.prot |= result->f.prot ? PAGE_EXEC : 0; - if (!(result->f.prot & (1 << access_type))) { + if (ptw->in_prot_check & ~result->f.prot) { /* Access permission fault. */ fi->type = ARMFault_Permission; goto do_fault; @@ -1243,7 +1250,7 @@ static bool get_phys_addr_v6(CPUARMState *env, S1Translate *ptw, result->f.prot = get_S1prot(env, mmu_idx, false, user_rw, prot_rw, xn, pxn, result->f.attrs.space, out_space); - if (!(result->f.prot & (1 << access_type))) { + if (ptw->in_prot_check & ~result->f.prot) { /* Access permission fault. */ fi->type = ARMFault_Permission; goto do_fault; @@ -2123,7 +2130,7 @@ static bool get_phys_addr_lpae(CPUARMState *env, S1Translate *ptw, result->f.tlb_fill_flags = 0; } - if (!(result->f.prot & (1 << access_type))) { + if (ptw->in_prot_check & ~result->f.prot) { fi->type = ARMFault_Permission; goto do_fault; } @@ -2537,7 +2544,7 @@ static bool get_phys_addr_pmsav7(CPUARMState *env, fi->type = ARMFault_Permission; fi->level = 1; - return !(result->f.prot & (1 << access_type)); + return (ptw->in_prot_check & ~result->f.prot) != 0; } static uint32_t *regime_rbar(CPUARMState *env, ARMMMUIdx mmu_idx, @@ -2953,7 +2960,7 @@ static bool get_phys_addr_pmsav8(CPUARMState *env, } } - ret = pmsav8_mpu_lookup(env, address, access_type, 1 << access_type, + ret = pmsav8_mpu_lookup(env, address, access_type, ptw->in_prot_check, mmu_idx, secure, result, fi, NULL); if (sattrs.subpage) { result->f.lg_page_size = 0; @@ -3625,6 +3632,7 @@ bool get_phys_addr(CPUARMState *env, vaddr address, S1Translate ptw = { .in_mmu_idx = mmu_idx, .in_space = arm_mmu_idx_to_security_space(env, mmu_idx), + .in_prot_check = 1 << access_type, }; return get_phys_addr_gpc(env, &ptw, address, access_type, @@ -3638,6 +3646,7 @@ static hwaddr arm_cpu_get_phys_page(CPUARMState *env, vaddr addr, .in_mmu_idx = mmu_idx, .in_space = arm_mmu_idx_to_security_space(env, mmu_idx), .in_debug = true, + .in_prot_check = PAGE_READ, }; GetPhysAddrResult res = {}; ARMMMUFaultInfo fi = {}; From 7e130764415e0a15faf09c5c26043b016ba798fc Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 30 Aug 2025 15:40:10 +1000 Subject: [PATCH 0408/2396] target/arm: Skip permission check from arm_cpu_get_phys_page_attrs_debug Do not require read permission when translating addresses for debugging purposes. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-id: 20250830054128.448363-7-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/ptw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/arm/ptw.c b/target/arm/ptw.c index d37c0ce0f1..5d85610de2 100644 --- a/target/arm/ptw.c +++ b/target/arm/ptw.c @@ -3646,7 +3646,7 @@ static hwaddr arm_cpu_get_phys_page(CPUARMState *env, vaddr addr, .in_mmu_idx = mmu_idx, .in_space = arm_mmu_idx_to_security_space(env, mmu_idx), .in_debug = true, - .in_prot_check = PAGE_READ, + .in_prot_check = 0, }; GetPhysAddrResult res = {}; ARMMMUFaultInfo fi = {}; From 8818b2d91363dc6b478edc4e6325e958e7348648 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 30 Aug 2025 15:40:11 +1000 Subject: [PATCH 0409/2396] target/arm: Introduce get_phys_addr_for_at Rename get_phys_addr_with_space_nogpc for its only caller, do_ats_write. Drop the MemOp memop argument as it doesn't make sense in the new context. Replace the access_type parameter with prot_check. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-id: 20250830054128.448363-8-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/internals.h | 18 +++++++----------- target/arm/ptw.c | 21 ++++++++++++++------- target/arm/tcg/cpregs-at.c | 11 ++--------- 3 files changed, 23 insertions(+), 27 deletions(-) diff --git a/target/arm/internals.h b/target/arm/internals.h index 899242e572..8782594b77 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -1602,25 +1602,21 @@ bool get_phys_addr(CPUARMState *env, vaddr address, __attribute__((nonnull)); /** - * get_phys_addr_with_space_nogpc: get the physical address for a virtual - * address + * get_phys_addr_for_at: * @env: CPUARMState * @address: virtual address to get physical address for - * @access_type: 0 for read, 1 for write, 2 for execute - * @memop: memory operation feeding this access, or 0 for none + * @prot_check: PAGE_{READ,WRITE,EXEC}, or 0 * @mmu_idx: MMU index indicating required translation regime * @space: security space for the access * @result: set on translation success. * @fi: set to fault info if the translation fails * - * Similar to get_phys_addr, but use the given security space and don't perform - * a Granule Protection Check on the resulting address. + * Similar to get_phys_addr, but for use by AccessType_AT, i.e. + * system instructions for address translation. */ -bool get_phys_addr_with_space_nogpc(CPUARMState *env, vaddr address, - MMUAccessType access_type, MemOp memop, - ARMMMUIdx mmu_idx, ARMSecuritySpace space, - GetPhysAddrResult *result, - ARMMMUFaultInfo *fi) +bool get_phys_addr_for_at(CPUARMState *env, vaddr address, unsigned prot_check, + ARMMMUIdx mmu_idx, ARMSecuritySpace space, + GetPhysAddrResult *result, ARMMMUFaultInfo *fi) __attribute__((nonnull)); bool pmsav8_mpu_lookup(CPUARMState *env, uint32_t address, diff --git a/target/arm/ptw.c b/target/arm/ptw.c index 5d85610de2..8925c9a610 100644 --- a/target/arm/ptw.c +++ b/target/arm/ptw.c @@ -3545,18 +3545,25 @@ static bool get_phys_addr_gpc(CPUARMState *env, S1Translate *ptw, return false; } -bool get_phys_addr_with_space_nogpc(CPUARMState *env, vaddr address, - MMUAccessType access_type, MemOp memop, - ARMMMUIdx mmu_idx, ARMSecuritySpace space, - GetPhysAddrResult *result, - ARMMMUFaultInfo *fi) +bool get_phys_addr_for_at(CPUARMState *env, vaddr address, + unsigned prot_check, ARMMMUIdx mmu_idx, + ARMSecuritySpace space, GetPhysAddrResult *result, + ARMMMUFaultInfo *fi) { S1Translate ptw = { .in_mmu_idx = mmu_idx, .in_space = space, + .in_prot_check = prot_check, }; - return get_phys_addr_nogpc(env, &ptw, address, access_type, - memop, result, fi); + /* + * I_MXTJT: Granule protection checks are not performed on the final + * address of a successful translation. This is a translation not a + * memory reference, so MMU_DATA_LOAD is arbitrary (the exact protection + * check is handled or bypassed by .in_prot_check) and "memop = MO_8" + * bypasses any alignment check. + */ + return get_phys_addr_nogpc(env, &ptw, address, + MMU_DATA_LOAD, MO_8, result, fi); } static ARMSecuritySpace diff --git a/target/arm/tcg/cpregs-at.c b/target/arm/tcg/cpregs-at.c index 398a61d398..2ff0b3e76f 100644 --- a/target/arm/tcg/cpregs-at.c +++ b/target/arm/tcg/cpregs-at.c @@ -27,19 +27,12 @@ static uint64_t do_ats_write(CPUARMState *env, uint64_t value, MMUAccessType access_type, ARMMMUIdx mmu_idx, ARMSecuritySpace ss) { - bool ret; uint64_t par64; bool format64 = false; ARMMMUFaultInfo fi = {}; GetPhysAddrResult res = {}; - - /* - * I_MXTJT: Granule protection checks are not performed on the final - * address of a successful translation. This is a translation not a - * memory reference, so "memop = none = 0". - */ - ret = get_phys_addr_with_space_nogpc(env, value, access_type, 0, - mmu_idx, ss, &res, &fi); + bool ret = get_phys_addr_for_at(env, value, 1 << access_type, + mmu_idx, ss, &res, &fi); /* * ATS operations only do S1 or S1+S2 translations, so we never From efebeec13d076100191a4a5a98f047c46c0d592c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 30 Aug 2025 15:40:12 +1000 Subject: [PATCH 0410/2396] target/arm: Skip AF and DB updates for AccessType_AT We are required to skip DB update for AT instructions, and we are allowed to skip AF updates. Choose to skip both. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-id: 20250830054128.448363-9-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/ptw.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/target/arm/ptw.c b/target/arm/ptw.c index 8925c9a610..089eeff845 100644 --- a/target/arm/ptw.c +++ b/target/arm/ptw.c @@ -58,6 +58,12 @@ typedef struct S1Translate { * and will not change the state of the softmmu TLBs. */ bool in_debug; + /* + * in_at: is this AccessType_AT? + * This is also set for debug, because at heart that is also + * an address translation, and simplifies a test. + */ + bool in_at; /* * If this is stage 2 of a stage 1+2 page table walk, then this must * be true if stage 1 is an EL0 access; otherwise this is ignored. @@ -1929,7 +1935,12 @@ static bool get_phys_addr_lpae(CPUARMState *env, S1Translate *ptw, descaddr &= ~(hwaddr)(page_size - 1); descaddr |= (address & (page_size - 1)); - if (likely(!ptw->in_debug)) { + /* + * For AccessType_AT, DB is not updated (AArch64.SetDirtyFlag), + * and it is IMPLEMENTATION DEFINED whether AF is updated + * (AArch64.SetAccessFlag; qemu chooses to not update). + */ + if (likely(!ptw->in_at)) { /* * Access flag. * If HA is enabled, prepare to update the descriptor below. @@ -3553,6 +3564,7 @@ bool get_phys_addr_for_at(CPUARMState *env, vaddr address, S1Translate ptw = { .in_mmu_idx = mmu_idx, .in_space = space, + .in_at = true, .in_prot_check = prot_check, }; /* @@ -3653,6 +3665,7 @@ static hwaddr arm_cpu_get_phys_page(CPUARMState *env, vaddr addr, .in_mmu_idx = mmu_idx, .in_space = arm_mmu_idx_to_security_space(env, mmu_idx), .in_debug = true, + .in_at = true, .in_prot_check = 0, }; GetPhysAddrResult res = {}; From 95901c43a8845a86a6895a8bd5d141f3ce36b3d1 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 30 Aug 2025 15:40:13 +1000 Subject: [PATCH 0411/2396] target/arm: Add prot_check parameter to do_ats_write Separate protection check from access type, in preparation for skipping the protection check altogether. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-id: 20250830054128.448363-10-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/cpregs-at.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/target/arm/tcg/cpregs-at.c b/target/arm/tcg/cpregs-at.c index 2ff0b3e76f..bebf168997 100644 --- a/target/arm/tcg/cpregs-at.c +++ b/target/arm/tcg/cpregs-at.c @@ -24,14 +24,14 @@ static int par_el1_shareability(GetPhysAddrResult *res) } static uint64_t do_ats_write(CPUARMState *env, uint64_t value, - MMUAccessType access_type, ARMMMUIdx mmu_idx, + unsigned prot_check, ARMMMUIdx mmu_idx, ARMSecuritySpace ss) { uint64_t par64; bool format64 = false; ARMMMUFaultInfo fi = {}; GetPhysAddrResult res = {}; - bool ret = get_phys_addr_for_at(env, value, 1 << access_type, + bool ret = get_phys_addr_for_at(env, value, prot_check, mmu_idx, ss, &res, &fi); /* @@ -191,7 +191,7 @@ static uint64_t do_ats_write(CPUARMState *env, uint64_t value, static void ats_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { - MMUAccessType access_type = ri->opc2 & 1 ? MMU_DATA_STORE : MMU_DATA_LOAD; + unsigned access_perm = ri->opc2 & 1 ? PAGE_WRITE : PAGE_READ; uint64_t par64; ARMMMUIdx mmu_idx; int el = arm_current_el(env); @@ -253,7 +253,7 @@ static void ats_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) g_assert_not_reached(); } - par64 = do_ats_write(env, value, access_type, mmu_idx, ss); + par64 = do_ats_write(env, value, access_perm, mmu_idx, ss); A32_BANKED_CURRENT_REG_SET(env, par, par64); } @@ -261,11 +261,11 @@ static void ats_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) static void ats1h_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { - MMUAccessType access_type = ri->opc2 & 1 ? MMU_DATA_STORE : MMU_DATA_LOAD; + unsigned access_perm = ri->opc2 & 1 ? PAGE_WRITE : PAGE_READ; uint64_t par64; /* There is no SecureEL2 for AArch32. */ - par64 = do_ats_write(env, value, access_type, ARMMMUIdx_E2, + par64 = do_ats_write(env, value, access_perm, ARMMMUIdx_E2, ARMSS_NonSecure); A32_BANKED_CURRENT_REG_SET(env, par, par64); @@ -309,7 +309,7 @@ static CPAccessResult at_s1e01_access(CPUARMState *env, const ARMCPRegInfo *ri, static void ats_write64(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { - MMUAccessType access_type = ri->opc2 & 1 ? MMU_DATA_STORE : MMU_DATA_LOAD; + unsigned access_perm = ri->opc2 & 1 ? PAGE_WRITE : PAGE_READ; ARMMMUIdx mmu_idx; uint64_t hcr_el2 = arm_hcr_el2_eff(env); bool regime_e20 = (hcr_el2 & (HCR_E2H | HCR_TGE)) == (HCR_E2H | HCR_TGE); @@ -352,7 +352,7 @@ static void ats_write64(CPUARMState *env, const ARMCPRegInfo *ri, } ss = for_el3 ? arm_security_space(env) : arm_security_space_below_el3(env); - env->cp15.par_el[1] = do_ats_write(env, value, access_type, mmu_idx, ss); + env->cp15.par_el[1] = do_ats_write(env, value, access_perm, mmu_idx, ss); } static CPAccessResult ats_access(CPUARMState *env, const ARMCPRegInfo *ri, From b41cfb6d17ee29f3c5474d8a8f79260535a11335 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 30 Aug 2025 15:40:14 +1000 Subject: [PATCH 0412/2396] target/arm: Fill in HFG[RWI]TR_EL2 bits for Arm v9.5 Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-id: 20250830054128.448363-11-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/cpregs.h | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/target/arm/cpregs.h b/target/arm/cpregs.h index c9506aa6d5..1d103b577f 100644 --- a/target/arm/cpregs.h +++ b/target/arm/cpregs.h @@ -408,10 +408,19 @@ FIELD(HFGRTR_EL2, ERXPFGCTL_EL1, 47, 1) FIELD(HFGRTR_EL2, ERXPFGCDN_EL1, 48, 1) FIELD(HFGRTR_EL2, ERXADDR_EL1, 49, 1) FIELD(HFGRTR_EL2, NACCDATA_EL1, 50, 1) -/* 51-53: RES0 */ +/* 51: RES0 */ +FIELD(HFGRTR_EL2, NGCS_EL0, 52, 1) +FIELD(HFGRTR_EL2, NGCS_EL1, 53, 1) FIELD(HFGRTR_EL2, NSMPRI_EL1, 54, 1) FIELD(HFGRTR_EL2, NTPIDR2_EL0, 55, 1) -/* 56-63: RES0 */ +FIELD(HFGRTR_EL2, NRCWMASK_EL1, 56, 1) +FIELD(HFGRTR_EL2, NPIRE0_EL1, 57, 1) +FIELD(HFGRTR_EL2, NPIR_EL1, 58, 1) +FIELD(HFGRTR_EL2, NPOR_EL0, 59, 1) +FIELD(HFGRTR_EL2, NPOR_EL1, 60, 1) +FIELD(HFGRTR_EL2, NS2POR_EL1, 61, 1) +FIELD(HFGRTR_EL2, NMAIR2_EL1, 62, 1) +FIELD(HFGRTR_EL2, NAMAIR2_EL1, 63, 1) /* These match HFGRTR but bits for RO registers are RES0 */ FIELD(HFGWTR_EL2, AFSR0_EL1, 0, 1) @@ -452,8 +461,18 @@ FIELD(HFGWTR_EL2, ERXPFGCTL_EL1, 47, 1) FIELD(HFGWTR_EL2, ERXPFGCDN_EL1, 48, 1) FIELD(HFGWTR_EL2, ERXADDR_EL1, 49, 1) FIELD(HFGWTR_EL2, NACCDATA_EL1, 50, 1) +FIELD(HFGWTR_EL2, NGCS_EL0, 52, 1) +FIELD(HFGWTR_EL2, NGCS_EL1, 53, 1) FIELD(HFGWTR_EL2, NSMPRI_EL1, 54, 1) FIELD(HFGWTR_EL2, NTPIDR2_EL0, 55, 1) +FIELD(HFGWTR_EL2, NRCWMASK_EL1, 56, 1) +FIELD(HFGWTR_EL2, NPIRE0_EL1, 57, 1) +FIELD(HFGWTR_EL2, NPIR_EL1, 58, 1) +FIELD(HFGWTR_EL2, NPOR_EL0, 59, 1) +FIELD(HFGWTR_EL2, NPOR_EL1, 60, 1) +FIELD(HFGWTR_EL2, NS2POR_EL1, 61, 1) +FIELD(HFGWTR_EL2, NMAIR2_EL1, 62, 1) +FIELD(HFGWTR_EL2, NAMAIR2_EL1, 63, 1) FIELD(HFGITR_EL2, ICIALLUIS, 0, 1) FIELD(HFGITR_EL2, ICIALLU, 1, 1) @@ -512,6 +531,11 @@ FIELD(HFGITR_EL2, SVC_EL1, 53, 1) FIELD(HFGITR_EL2, DCCVAC, 54, 1) FIELD(HFGITR_EL2, NBRBINJ, 55, 1) FIELD(HFGITR_EL2, NBRBIALL, 56, 1) +FIELD(HFGITR_EL2, NGCSPUSHM_EL1, 57, 1) +FIELD(HFGITR_EL2, NGCSSTR_EL1, 58, 1) +FIELD(HFGITR_EL2, NGCSEPP, 59, 1) +FIELD(HFGITR_EL2, COSPRCTX, 60, 1) +FIELD(HFGITR_EL2, ATS1E1A, 62, 1) FIELD(HDFGRTR_EL2, DBGBCRN_EL1, 0, 1) FIELD(HDFGRTR_EL2, DBGBVRN_EL1, 1, 1) From 171a302a041ed5532d997d40bb50f39b2b9435a3 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 30 Aug 2025 15:40:15 +1000 Subject: [PATCH 0413/2396] target/arm: Remove outdated comment for ZCR_EL12 The comment about not being included in the summary table has been out of date for quite a while. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-id: 20250830054128.448363-12-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index 19637e7301..b641229ba0 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -4563,11 +4563,6 @@ static void define_arm_vh_e2h_redirects_aliases(ARMCPU *cpu) { K(3, 0, 14, 1, 0), K(3, 4, 14, 1, 0), K(3, 5, 14, 1, 0), "CNTKCTL", "CNTHCTL_EL2", "CNTKCTL_EL12" }, - /* - * Note that redirection of ZCR is mentioned in the description - * of ZCR_EL2, and aliasing in the description of ZCR_EL1, but - * not in the summary table. - */ { K(3, 0, 1, 2, 0), K(3, 4, 1, 2, 0), K(3, 5, 1, 2, 0), "ZCR_EL1", "ZCR_EL2", "ZCR_EL12", isar_feature_aa64_sve }, { K(3, 0, 1, 2, 6), K(3, 4, 1, 2, 6), K(3, 5, 1, 2, 6), From 2b5daf79c32656264a23104c0693aa89c528cff8 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 30 Aug 2025 15:40:16 +1000 Subject: [PATCH 0414/2396] target/arm: Implement FEAT_ATS1A Implement FEAT_ATS1A and enable for -cpu max. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-id: 20250830054128.448363-13-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- docs/system/arm/emulation.rst | 1 + target/arm/cpregs.h | 1 + target/arm/cpu-features.h | 5 ++++ target/arm/tcg/cpregs-at.c | 44 +++++++++++++++++++++++++++++++++++ target/arm/tcg/cpu64.c | 1 + 5 files changed, 52 insertions(+) diff --git a/docs/system/arm/emulation.rst b/docs/system/arm/emulation.rst index 4e8aca8b5d..6b04c96c8c 100644 --- a/docs/system/arm/emulation.rst +++ b/docs/system/arm/emulation.rst @@ -23,6 +23,7 @@ the following architecture extensions: - FEAT_AFP (Alternate floating-point behavior) - FEAT_Armv9_Crypto (Armv9 Cryptographic Extension) - FEAT_ASID16 (16 bit ASID) +- FEAT_ATS1A (Address Translation operations that ignore stage 1 permissions) - FEAT_BBM at level 2 (Translation table break-before-make levels) - FEAT_BF16 (AArch64 BFloat16 instructions) - FEAT_BTI (Branch Target Identification) diff --git a/target/arm/cpregs.h b/target/arm/cpregs.h index 1d103b577f..2a4826f5c4 100644 --- a/target/arm/cpregs.h +++ b/target/arm/cpregs.h @@ -854,6 +854,7 @@ typedef enum FGTBit { DO_BIT(HFGITR, DVPRCTX), DO_BIT(HFGITR, CPPRCTX), DO_BIT(HFGITR, DCCVAC), + DO_BIT(HFGITR, ATS1E1A), } FGTBit; #undef DO_BIT diff --git a/target/arm/cpu-features.h b/target/arm/cpu-features.h index e49e0ae3af..512eeaf551 100644 --- a/target/arm/cpu-features.h +++ b/target/arm/cpu-features.h @@ -619,6 +619,11 @@ static inline bool isar_feature_aa64_lut(const ARMISARegisters *id) return FIELD_EX64_IDREG(id, ID_AA64ISAR2, LUT); } +static inline bool isar_feature_aa64_ats1a(const ARMISARegisters *id) +{ + return FIELD_EX64_IDREG(id, ID_AA64ISAR2, ATS1A); +} + static inline bool isar_feature_aa64_fp_simd(const ARMISARegisters *id) { /* We always set the AdvSIMD and FP fields identically. */ diff --git a/target/arm/tcg/cpregs-at.c b/target/arm/tcg/cpregs-at.c index bebf168997..0e8f229aa7 100644 --- a/target/arm/tcg/cpregs-at.c +++ b/target/arm/tcg/cpregs-at.c @@ -488,6 +488,47 @@ static const ARMCPRegInfo ats1cp_reginfo[] = { .writefn = ats_write }, }; +static void ats_s1e1a(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) +{ + uint64_t hcr_el2 = arm_hcr_el2_eff(env); + bool regime_e20 = (hcr_el2 & (HCR_E2H | HCR_TGE)) == (HCR_E2H | HCR_TGE); + ARMMMUIdx mmu_idx = regime_e20 ? ARMMMUIdx_E20_2 : ARMMMUIdx_Stage1_E1; + ARMSecuritySpace ss = arm_security_space_below_el3(env); + + env->cp15.par_el[1] = do_ats_write(env, value, 0, mmu_idx, ss); +} + +static void ats_s1e2a(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) +{ + uint64_t hcr_el2 = arm_hcr_el2_eff(env); + ARMMMUIdx mmu_idx = hcr_el2 & HCR_E2H ? ARMMMUIdx_E20_2 : ARMMMUIdx_E2; + ARMSecuritySpace ss = arm_security_space_below_el3(env); + + env->cp15.par_el[1] = do_ats_write(env, value, 0, mmu_idx, ss); +} + +static void ats_s1e3a(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) +{ + env->cp15.par_el[1] = do_ats_write(env, value, 0, ARMMMUIdx_E3, + arm_security_space(env)); +} + +static const ARMCPRegInfo ats1a_reginfo[] = { + { .name = "AT_S1E1A", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 9, .opc2 = 2, + .access = PL1_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC, + .fgt = FGT_ATS1E1A, + .accessfn = at_s1e01_access, .writefn = ats_s1e1a }, + { .name = "AT_S1E2A", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 4, .crn = 7, .crm = 9, .opc2 = 2, + .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC, + .accessfn = at_s1e2_access, .writefn = ats_s1e2a }, + { .name = "AT_S1E3A", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 6, .crn = 7, .crm = 9, .opc2 = 2, + .access = PL3_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC, + .writefn = ats_s1e3a }, +}; + void define_at_insn_regs(ARMCPU *cpu) { CPUARMState *env = &cpu->env; @@ -509,4 +550,7 @@ void define_at_insn_regs(ARMCPU *cpu) if (cpu_isar_feature(aa32_ats1e1, cpu)) { define_arm_cp_regs(cpu, ats1cp_reginfo); } + if (cpu_isar_feature(aa64_ats1a, cpu)) { + define_arm_cp_regs(cpu, ats1a_reginfo); + } } diff --git a/target/arm/tcg/cpu64.c b/target/arm/tcg/cpu64.c index b8b1981e70..abef6a246e 100644 --- a/target/arm/tcg/cpu64.c +++ b/target/arm/tcg/cpu64.c @@ -1179,6 +1179,7 @@ void aarch64_max_tcg_initfn(Object *obj) t = FIELD_DP64(t, ID_AA64ISAR2, BC, 1); /* FEAT_HBC */ t = FIELD_DP64(t, ID_AA64ISAR2, WFXT, 2); /* FEAT_WFxT */ t = FIELD_DP64(t, ID_AA64ISAR2, CSSC, 1); /* FEAT_CSSC */ + t = FIELD_DP64(t, ID_AA64ISAR2, ATS1A, 1); /* FEAT_ATS1A */ SET_IDREG(isar, ID_AA64ISAR2, t); t = GET_IDREG(isar, ID_AA64PFR0); From cc2c5027dc755b0b7e2a6531b089e5a239d3e0ce Mon Sep 17 00:00:00 2001 From: Osama Abdelkader Date: Tue, 2 Sep 2025 22:08:18 +0200 Subject: [PATCH 0415/2396] hw/arm/raspi4b: remove redundant check in raspi_add_memory_node MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The if (acells == 0 || scells == 0) check is redundant in raspi_add_memory_node, since it is already checked in the call chain, arm_load_dtb. Also the return value of the function is not checked/used so it's removed. Signed-off-by: Osama Abdelkader Reviewed-by: Alex Bennée Tested-by: Alex Bennée Message-id: 20250902200818.43305-1-osama.abdelkader@gmail.com Signed-off-by: Peter Maydell --- hw/arm/raspi4b.c | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/hw/arm/raspi4b.c b/hw/arm/raspi4b.c index 20082d5266..4df951a0d8 100644 --- a/hw/arm/raspi4b.c +++ b/hw/arm/raspi4b.c @@ -36,9 +36,8 @@ struct Raspi4bMachineState { * (see https://datasheets.raspberrypi.com/bcm2711/bcm2711-peripherals.pdf * 1.2 Address Map) */ -static int raspi_add_memory_node(void *fdt, hwaddr mem_base, hwaddr mem_len) +static void raspi_add_memory_node(void *fdt, hwaddr mem_base, hwaddr mem_len) { - int ret; uint32_t acells, scells; char *nodename = g_strdup_printf("/memory@%" PRIx64, mem_base); @@ -46,19 +45,16 @@ static int raspi_add_memory_node(void *fdt, hwaddr mem_base, hwaddr mem_len) NULL, &error_fatal); scells = qemu_fdt_getprop_cell(fdt, "/", "#size-cells", NULL, &error_fatal); - if (acells == 0 || scells == 0) { - fprintf(stderr, "dtb file invalid (#address-cells or #size-cells 0)\n"); - ret = -1; - } else { - qemu_fdt_add_subnode(fdt, nodename); - qemu_fdt_setprop_string(fdt, nodename, "device_type", "memory"); - ret = qemu_fdt_setprop_sized_cells(fdt, nodename, "reg", - acells, mem_base, - scells, mem_len); - } + /* validated by arm_load_dtb */ + g_assert(acells && scells); + + qemu_fdt_add_subnode(fdt, nodename); + qemu_fdt_setprop_string(fdt, nodename, "device_type", "memory"); + qemu_fdt_setprop_sized_cells(fdt, nodename, "reg", + acells, mem_base, + scells, mem_len); g_free(nodename); - return ret; } static void raspi4_modify_dtb(const struct arm_boot_info *info, void *fdt) From 5b3764d9e30627853c5b9171925c51232e56a293 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 28 Aug 2025 15:04:18 +0100 Subject: [PATCH 0416/2396] target/arm: Remove deprecated pxa CPU family MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In 10.0 we deprecated the pxa CPU family (pxa250, pxa255, pxa260 pxa261, pxa262, pxa270-a0, pxa270-a1, pxa270, pxa270-b0, pxa270-b1, pxa270-c0, pxa270-c5). Now we have released 10.1 we can remove them. This commit removes only the top level CPU definitions and updates the documentation. Removing the CPUs means that there is now a lot of dead iwMMXt code, which we will delete in subsequent commits. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-id: 20250828140422.3271703-2-peter.maydell@linaro.org --- docs/about/deprecated.rst | 21 ---- docs/about/removed-features.rst | 14 +++ target/arm/tcg/cpu32.c | 163 -------------------------------- 3 files changed, 14 insertions(+), 184 deletions(-) diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst index b2420732e1..f031414769 100644 --- a/docs/about/deprecated.rst +++ b/docs/about/deprecated.rst @@ -236,27 +236,6 @@ Keeping 32-bit host support alive is a substantial burden for the QEMU project. Thus QEMU will in future drop the support for all 32-bit host systems. -linux-user mode CPUs --------------------- - -iwMMXt emulation and the ``pxa`` CPUs (since 10.0) -'''''''''''''''''''''''''''''''''''''''''''''''''' - -The ``pxa`` CPU family (``pxa250``, ``pxa255``, ``pxa260``, -``pxa261``, ``pxa262``, ``pxa270-a0``, ``pxa270-a1``, ``pxa270``, -``pxa270-b0``, ``pxa270-b1``, ``pxa270-c0``, ``pxa270-c5``) are no -longer used in system emulation, because all the machine types which -used these CPUs were removed in the QEMU 9.2 release. These CPUs can -now only be used in linux-user mode, and to do that you would have to -explicitly select one of these CPUs with the ``-cpu`` command line -option or the ``QEMU_CPU`` environment variable. - -We don't believe that anybody is using the iwMMXt emulation, and we do -not have any tests to validate it or any real hardware or similar -known-good implementation to test against. GCC is in the process of -dropping their support for iwMMXt codegen. These CPU types are -therefore deprecated in QEMU, and will be removed in a future release. - System emulator CPUs -------------------- diff --git a/docs/about/removed-features.rst b/docs/about/removed-features.rst index fff781d6b7..65fd564d22 100644 --- a/docs/about/removed-features.rst +++ b/docs/about/removed-features.rst @@ -1138,6 +1138,20 @@ reason the maintainers strongly suspected no one actually used it. QEMU Nios II architecture was orphan; Intel has EOL'ed the Nios II processor IP (see `Intel discontinuance notification`_). +iwMMXt emulation and the ``pxa`` CPUs (removed in 10.2) +''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +The ``pxa`` CPU family (``pxa250``, ``pxa255``, ``pxa260``, +``pxa261``, ``pxa262``, ``pxa270-a0``, ``pxa270-a1``, ``pxa270``, +``pxa270-b0``, ``pxa270-b1``, ``pxa270-c0``, ``pxa270-c5``) were +not available in system emulation, because all the machine types which +used these CPUs were removed in the QEMU 9.2 release. We don't +believe that anybody was using the iwMMXt emulation (which you +would have to explicitly enable on the command line), and we did +not have any tests to validate it or any real hardware or similar +known-good implementation to test against. These CPUs have +therefore been removed in linux-user mode as well. + TCG introspection features -------------------------- diff --git a/target/arm/tcg/cpu32.c b/target/arm/tcg/cpu32.c index a2a23eae0d..f0761410ad 100644 --- a/target/arm/tcg/cpu32.c +++ b/target/arm/tcg/cpu32.c @@ -807,144 +807,6 @@ static void sa1110_initfn(Object *obj) cpu->reset_sctlr = 0x00000070; } -static void pxa250_initfn(Object *obj) -{ - ARMCPU *cpu = ARM_CPU(obj); - - cpu->dtb_compatible = "marvell,xscale"; - set_feature(&cpu->env, ARM_FEATURE_V5); - set_feature(&cpu->env, ARM_FEATURE_XSCALE); - cpu->midr = 0x69052100; - cpu->ctr = 0xd172172; - cpu->reset_sctlr = 0x00000078; -} - -static void pxa255_initfn(Object *obj) -{ - ARMCPU *cpu = ARM_CPU(obj); - - cpu->dtb_compatible = "marvell,xscale"; - set_feature(&cpu->env, ARM_FEATURE_V5); - set_feature(&cpu->env, ARM_FEATURE_XSCALE); - cpu->midr = 0x69052d00; - cpu->ctr = 0xd172172; - cpu->reset_sctlr = 0x00000078; -} - -static void pxa260_initfn(Object *obj) -{ - ARMCPU *cpu = ARM_CPU(obj); - - cpu->dtb_compatible = "marvell,xscale"; - set_feature(&cpu->env, ARM_FEATURE_V5); - set_feature(&cpu->env, ARM_FEATURE_XSCALE); - cpu->midr = 0x69052903; - cpu->ctr = 0xd172172; - cpu->reset_sctlr = 0x00000078; -} - -static void pxa261_initfn(Object *obj) -{ - ARMCPU *cpu = ARM_CPU(obj); - - cpu->dtb_compatible = "marvell,xscale"; - set_feature(&cpu->env, ARM_FEATURE_V5); - set_feature(&cpu->env, ARM_FEATURE_XSCALE); - cpu->midr = 0x69052d05; - cpu->ctr = 0xd172172; - cpu->reset_sctlr = 0x00000078; -} - -static void pxa262_initfn(Object *obj) -{ - ARMCPU *cpu = ARM_CPU(obj); - - cpu->dtb_compatible = "marvell,xscale"; - set_feature(&cpu->env, ARM_FEATURE_V5); - set_feature(&cpu->env, ARM_FEATURE_XSCALE); - cpu->midr = 0x69052d06; - cpu->ctr = 0xd172172; - cpu->reset_sctlr = 0x00000078; -} - -static void pxa270a0_initfn(Object *obj) -{ - ARMCPU *cpu = ARM_CPU(obj); - - cpu->dtb_compatible = "marvell,xscale"; - set_feature(&cpu->env, ARM_FEATURE_V5); - set_feature(&cpu->env, ARM_FEATURE_XSCALE); - set_feature(&cpu->env, ARM_FEATURE_IWMMXT); - cpu->midr = 0x69054110; - cpu->ctr = 0xd172172; - cpu->reset_sctlr = 0x00000078; -} - -static void pxa270a1_initfn(Object *obj) -{ - ARMCPU *cpu = ARM_CPU(obj); - - cpu->dtb_compatible = "marvell,xscale"; - set_feature(&cpu->env, ARM_FEATURE_V5); - set_feature(&cpu->env, ARM_FEATURE_XSCALE); - set_feature(&cpu->env, ARM_FEATURE_IWMMXT); - cpu->midr = 0x69054111; - cpu->ctr = 0xd172172; - cpu->reset_sctlr = 0x00000078; -} - -static void pxa270b0_initfn(Object *obj) -{ - ARMCPU *cpu = ARM_CPU(obj); - - cpu->dtb_compatible = "marvell,xscale"; - set_feature(&cpu->env, ARM_FEATURE_V5); - set_feature(&cpu->env, ARM_FEATURE_XSCALE); - set_feature(&cpu->env, ARM_FEATURE_IWMMXT); - cpu->midr = 0x69054112; - cpu->ctr = 0xd172172; - cpu->reset_sctlr = 0x00000078; -} - -static void pxa270b1_initfn(Object *obj) -{ - ARMCPU *cpu = ARM_CPU(obj); - - cpu->dtb_compatible = "marvell,xscale"; - set_feature(&cpu->env, ARM_FEATURE_V5); - set_feature(&cpu->env, ARM_FEATURE_XSCALE); - set_feature(&cpu->env, ARM_FEATURE_IWMMXT); - cpu->midr = 0x69054113; - cpu->ctr = 0xd172172; - cpu->reset_sctlr = 0x00000078; -} - -static void pxa270c0_initfn(Object *obj) -{ - ARMCPU *cpu = ARM_CPU(obj); - - cpu->dtb_compatible = "marvell,xscale"; - set_feature(&cpu->env, ARM_FEATURE_V5); - set_feature(&cpu->env, ARM_FEATURE_XSCALE); - set_feature(&cpu->env, ARM_FEATURE_IWMMXT); - cpu->midr = 0x69054114; - cpu->ctr = 0xd172172; - cpu->reset_sctlr = 0x00000078; -} - -static void pxa270c5_initfn(Object *obj) -{ - ARMCPU *cpu = ARM_CPU(obj); - - cpu->dtb_compatible = "marvell,xscale"; - set_feature(&cpu->env, ARM_FEATURE_V5); - set_feature(&cpu->env, ARM_FEATURE_XSCALE); - set_feature(&cpu->env, ARM_FEATURE_IWMMXT); - cpu->midr = 0x69054117; - cpu->ctr = 0xd172172; - cpu->reset_sctlr = 0x00000078; -} - #ifndef TARGET_AARCH64 /* * -cpu max: a CPU with as many features enabled as our emulation supports. @@ -1032,31 +894,6 @@ static const ARMCPUInfo arm_tcg_cpus[] = { { .name = "ti925t", .initfn = ti925t_initfn }, { .name = "sa1100", .initfn = sa1100_initfn }, { .name = "sa1110", .initfn = sa1110_initfn }, - { .name = "pxa250", .initfn = pxa250_initfn, - .deprecation_note = "iwMMXt CPUs are no longer supported", }, - { .name = "pxa255", .initfn = pxa255_initfn, - .deprecation_note = "iwMMXt CPUs are no longer supported", }, - { .name = "pxa260", .initfn = pxa260_initfn, - .deprecation_note = "iwMMXt CPUs are no longer supported", }, - { .name = "pxa261", .initfn = pxa261_initfn, - .deprecation_note = "iwMMXt CPUs are no longer supported", }, - { .name = "pxa262", .initfn = pxa262_initfn, - .deprecation_note = "iwMMXt CPUs are no longer supported", }, - /* "pxa270" is an alias for "pxa270-a0" */ - { .name = "pxa270", .initfn = pxa270a0_initfn, - .deprecation_note = "iwMMXt CPUs are no longer supported", }, - { .name = "pxa270-a0", .initfn = pxa270a0_initfn, - .deprecation_note = "iwMMXt CPUs are no longer supported", }, - { .name = "pxa270-a1", .initfn = pxa270a1_initfn, - .deprecation_note = "iwMMXt CPUs are no longer supported", }, - { .name = "pxa270-b0", .initfn = pxa270b0_initfn, - .deprecation_note = "iwMMXt CPUs are no longer supported", }, - { .name = "pxa270-b1", .initfn = pxa270b1_initfn, - .deprecation_note = "iwMMXt CPUs are no longer supported", }, - { .name = "pxa270-c0", .initfn = pxa270c0_initfn, - .deprecation_note = "iwMMXt CPUs are no longer supported", }, - { .name = "pxa270-c5", .initfn = pxa270c5_initfn, - .deprecation_note = "iwMMXt CPUs are no longer supported", }, #ifndef TARGET_AARCH64 { .name = "max", .initfn = arm_max_initfn }, #endif From cdafe5bd90eef82bec798029ad5669bf2ee15023 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 28 Aug 2025 15:04:19 +0100 Subject: [PATCH 0417/2396] target/arm: Remove XScale and iWMMXt translate.c code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove all the translator code that is accessible only via ARM_FEATURE_XSCALE or ARM_FEATURE_IWMMXT. This includes the xscale-only cp15_cpar TB flags and cpu_{V0,V1,M0} TCG temps. The no-longer-used helper functions will be removed in a separate commit. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-id: 20250828140422.3271703-3-peter.maydell@linaro.org --- target/arm/cpu.h | 7 - target/arm/tcg/hflags.c | 13 +- target/arm/tcg/translate.c | 1324 +----------------------------------- target/arm/tcg/translate.h | 2 - 4 files changed, 7 insertions(+), 1339 deletions(-) diff --git a/target/arm/cpu.h b/target/arm/cpu.h index c15d79a106..f56fa6df8d 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -3025,13 +3025,6 @@ FIELD(TBFLAG_AM32, THUMB, 23, 1) /* Not cached. */ */ FIELD(TBFLAG_A32, VECLEN, 0, 3) /* Not cached. */ FIELD(TBFLAG_A32, VECSTRIDE, 3, 2) /* Not cached. */ -/* - * We store the bottom two bits of the CPAR as TB flags and handle - * checks on the other bits at runtime. This shares the same bits as - * VECSTRIDE, which is OK as no XScale CPU has VFP. - * Not cached, because VECLEN+VECSTRIDE are not cached. - */ -FIELD(TBFLAG_A32, XSCALE_CPAR, 5, 2) FIELD(TBFLAG_A32, VFPEN, 7, 1) /* Partially cached, minus FPEXC. */ FIELD(TBFLAG_A32, SCTLR__B, 8, 1) /* Cannot overlap with SCTLR_B */ FIELD(TBFLAG_A32, HSTR_ACTIVE, 9, 1) diff --git a/target/arm/tcg/hflags.c b/target/arm/tcg/hflags.c index 59ab526375..01894226cc 100644 --- a/target/arm/tcg/hflags.c +++ b/target/arm/tcg/hflags.c @@ -624,16 +624,9 @@ TCGTBCPUState arm_get_tb_cpu_state(CPUState *cs) DP_TBFLAG_M32(flags, MVE_NO_PRED, 1); } } else { - /* - * Note that XSCALE_CPAR shares bits with VECSTRIDE. - * Note that VECLEN+VECSTRIDE are RES0 for M-profile. - */ - if (arm_feature(env, ARM_FEATURE_XSCALE)) { - DP_TBFLAG_A32(flags, XSCALE_CPAR, env->cp15.c15_cpar); - } else { - DP_TBFLAG_A32(flags, VECLEN, env->vfp.vec_len); - DP_TBFLAG_A32(flags, VECSTRIDE, env->vfp.vec_stride); - } + /* Note that VECLEN+VECSTRIDE are RES0 for M-profile. */ + DP_TBFLAG_A32(flags, VECLEN, env->vfp.vec_len); + DP_TBFLAG_A32(flags, VECSTRIDE, env->vfp.vec_stride); if (env->vfp.xregs[ARM_VFP_FPEXC] & (1 << 30)) { DP_TBFLAG_A32(flags, VFPEN, 1); } diff --git a/target/arm/tcg/translate.c b/target/arm/tcg/translate.c index f7d6d8ce19..e62dcc5d85 100644 --- a/target/arm/tcg/translate.c +++ b/target/arm/tcg/translate.c @@ -44,8 +44,6 @@ #define ENABLE_ARCH_7 arm_dc_feature(s, ARM_FEATURE_V7) #define ENABLE_ARCH_8 arm_dc_feature(s, ARM_FEATURE_V8) -/* These are TCG temporaries used only by the legacy iwMMXt decoder */ -static TCGv_i64 cpu_V0, cpu_V1, cpu_M0; /* These are TCG globals which alias CPUARMState fields */ static TCGv_i32 cpu_R[16]; TCGv_i32 cpu_CF, cpu_NF, cpu_VF, cpu_ZF; @@ -1252,1263 +1250,6 @@ void write_neon_element64(TCGv_i64 src, int reg, int ele, MemOp memop) } } -#define ARM_CP_RW_BIT (1 << 20) - -static inline void iwmmxt_load_reg(TCGv_i64 var, int reg) -{ - tcg_gen_ld_i64(var, tcg_env, offsetof(CPUARMState, iwmmxt.regs[reg])); -} - -static inline void iwmmxt_store_reg(TCGv_i64 var, int reg) -{ - tcg_gen_st_i64(var, tcg_env, offsetof(CPUARMState, iwmmxt.regs[reg])); -} - -static inline TCGv_i32 iwmmxt_load_creg(int reg) -{ - TCGv_i32 var = tcg_temp_new_i32(); - tcg_gen_ld_i32(var, tcg_env, offsetof(CPUARMState, iwmmxt.cregs[reg])); - return var; -} - -static inline void iwmmxt_store_creg(int reg, TCGv_i32 var) -{ - tcg_gen_st_i32(var, tcg_env, offsetof(CPUARMState, iwmmxt.cregs[reg])); -} - -static inline void gen_op_iwmmxt_movq_wRn_M0(int rn) -{ - iwmmxt_store_reg(cpu_M0, rn); -} - -static inline void gen_op_iwmmxt_movq_M0_wRn(int rn) -{ - iwmmxt_load_reg(cpu_M0, rn); -} - -static inline void gen_op_iwmmxt_orq_M0_wRn(int rn) -{ - iwmmxt_load_reg(cpu_V1, rn); - tcg_gen_or_i64(cpu_M0, cpu_M0, cpu_V1); -} - -static inline void gen_op_iwmmxt_andq_M0_wRn(int rn) -{ - iwmmxt_load_reg(cpu_V1, rn); - tcg_gen_and_i64(cpu_M0, cpu_M0, cpu_V1); -} - -static inline void gen_op_iwmmxt_xorq_M0_wRn(int rn) -{ - iwmmxt_load_reg(cpu_V1, rn); - tcg_gen_xor_i64(cpu_M0, cpu_M0, cpu_V1); -} - -#define IWMMXT_OP(name) \ -static inline void gen_op_iwmmxt_##name##_M0_wRn(int rn) \ -{ \ - iwmmxt_load_reg(cpu_V1, rn); \ - gen_helper_iwmmxt_##name(cpu_M0, cpu_M0, cpu_V1); \ -} - -#define IWMMXT_OP_ENV(name) \ -static inline void gen_op_iwmmxt_##name##_M0_wRn(int rn) \ -{ \ - iwmmxt_load_reg(cpu_V1, rn); \ - gen_helper_iwmmxt_##name(cpu_M0, tcg_env, cpu_M0, cpu_V1); \ -} - -#define IWMMXT_OP_ENV_SIZE(name) \ -IWMMXT_OP_ENV(name##b) \ -IWMMXT_OP_ENV(name##w) \ -IWMMXT_OP_ENV(name##l) - -#define IWMMXT_OP_ENV1(name) \ -static inline void gen_op_iwmmxt_##name##_M0(void) \ -{ \ - gen_helper_iwmmxt_##name(cpu_M0, tcg_env, cpu_M0); \ -} - -IWMMXT_OP(maddsq) -IWMMXT_OP(madduq) -IWMMXT_OP(sadb) -IWMMXT_OP(sadw) -IWMMXT_OP(mulslw) -IWMMXT_OP(mulshw) -IWMMXT_OP(mululw) -IWMMXT_OP(muluhw) -IWMMXT_OP(macsw) -IWMMXT_OP(macuw) - -IWMMXT_OP_ENV_SIZE(unpackl) -IWMMXT_OP_ENV_SIZE(unpackh) - -IWMMXT_OP_ENV1(unpacklub) -IWMMXT_OP_ENV1(unpackluw) -IWMMXT_OP_ENV1(unpacklul) -IWMMXT_OP_ENV1(unpackhub) -IWMMXT_OP_ENV1(unpackhuw) -IWMMXT_OP_ENV1(unpackhul) -IWMMXT_OP_ENV1(unpacklsb) -IWMMXT_OP_ENV1(unpacklsw) -IWMMXT_OP_ENV1(unpacklsl) -IWMMXT_OP_ENV1(unpackhsb) -IWMMXT_OP_ENV1(unpackhsw) -IWMMXT_OP_ENV1(unpackhsl) - -IWMMXT_OP_ENV_SIZE(cmpeq) -IWMMXT_OP_ENV_SIZE(cmpgtu) -IWMMXT_OP_ENV_SIZE(cmpgts) - -IWMMXT_OP_ENV_SIZE(mins) -IWMMXT_OP_ENV_SIZE(minu) -IWMMXT_OP_ENV_SIZE(maxs) -IWMMXT_OP_ENV_SIZE(maxu) - -IWMMXT_OP_ENV_SIZE(subn) -IWMMXT_OP_ENV_SIZE(addn) -IWMMXT_OP_ENV_SIZE(subu) -IWMMXT_OP_ENV_SIZE(addu) -IWMMXT_OP_ENV_SIZE(subs) -IWMMXT_OP_ENV_SIZE(adds) - -IWMMXT_OP_ENV(avgb0) -IWMMXT_OP_ENV(avgb1) -IWMMXT_OP_ENV(avgw0) -IWMMXT_OP_ENV(avgw1) - -IWMMXT_OP_ENV(packuw) -IWMMXT_OP_ENV(packul) -IWMMXT_OP_ENV(packuq) -IWMMXT_OP_ENV(packsw) -IWMMXT_OP_ENV(packsl) -IWMMXT_OP_ENV(packsq) - -static void gen_op_iwmmxt_set_mup(void) -{ - TCGv_i32 tmp; - tmp = load_cpu_field(iwmmxt.cregs[ARM_IWMMXT_wCon]); - tcg_gen_ori_i32(tmp, tmp, 2); - store_cpu_field(tmp, iwmmxt.cregs[ARM_IWMMXT_wCon]); -} - -static void gen_op_iwmmxt_set_cup(void) -{ - TCGv_i32 tmp; - tmp = load_cpu_field(iwmmxt.cregs[ARM_IWMMXT_wCon]); - tcg_gen_ori_i32(tmp, tmp, 1); - store_cpu_field(tmp, iwmmxt.cregs[ARM_IWMMXT_wCon]); -} - -static void gen_op_iwmmxt_setpsr_nz(void) -{ - TCGv_i32 tmp = tcg_temp_new_i32(); - gen_helper_iwmmxt_setpsr_nz(tmp, cpu_M0); - store_cpu_field(tmp, iwmmxt.cregs[ARM_IWMMXT_wCASF]); -} - -static inline void gen_op_iwmmxt_addl_M0_wRn(int rn) -{ - iwmmxt_load_reg(cpu_V1, rn); - tcg_gen_ext32u_i64(cpu_V1, cpu_V1); - tcg_gen_add_i64(cpu_M0, cpu_M0, cpu_V1); -} - -static inline int gen_iwmmxt_address(DisasContext *s, uint32_t insn, - TCGv_i32 dest) -{ - int rd; - uint32_t offset; - TCGv_i32 tmp; - - rd = (insn >> 16) & 0xf; - tmp = load_reg(s, rd); - - offset = (insn & 0xff) << ((insn >> 7) & 2); - if (insn & (1 << 24)) { - /* Pre indexed */ - if (insn & (1 << 23)) - tcg_gen_addi_i32(tmp, tmp, offset); - else - tcg_gen_addi_i32(tmp, tmp, -offset); - tcg_gen_mov_i32(dest, tmp); - if (insn & (1 << 21)) { - store_reg(s, rd, tmp); - } - } else if (insn & (1 << 21)) { - /* Post indexed */ - tcg_gen_mov_i32(dest, tmp); - if (insn & (1 << 23)) - tcg_gen_addi_i32(tmp, tmp, offset); - else - tcg_gen_addi_i32(tmp, tmp, -offset); - store_reg(s, rd, tmp); - } else if (!(insn & (1 << 23))) - return 1; - return 0; -} - -static inline int gen_iwmmxt_shift(uint32_t insn, uint32_t mask, TCGv_i32 dest) -{ - int rd = (insn >> 0) & 0xf; - TCGv_i32 tmp; - - if (insn & (1 << 8)) { - if (rd < ARM_IWMMXT_wCGR0 || rd > ARM_IWMMXT_wCGR3) { - return 1; - } else { - tmp = iwmmxt_load_creg(rd); - } - } else { - tmp = tcg_temp_new_i32(); - iwmmxt_load_reg(cpu_V0, rd); - tcg_gen_extrl_i64_i32(tmp, cpu_V0); - } - tcg_gen_andi_i32(tmp, tmp, mask); - tcg_gen_mov_i32(dest, tmp); - return 0; -} - -/* Disassemble an iwMMXt instruction. Returns nonzero if an error occurred - (ie. an undefined instruction). */ -static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) -{ - int rd, wrd; - int rdhi, rdlo, rd0, rd1, i; - TCGv_i32 addr; - TCGv_i32 tmp, tmp2, tmp3; - - if ((insn & 0x0e000e00) == 0x0c000000) { - if ((insn & 0x0fe00ff0) == 0x0c400000) { - wrd = insn & 0xf; - rdlo = (insn >> 12) & 0xf; - rdhi = (insn >> 16) & 0xf; - if (insn & ARM_CP_RW_BIT) { /* TMRRC */ - iwmmxt_load_reg(cpu_V0, wrd); - tcg_gen_extrl_i64_i32(cpu_R[rdlo], cpu_V0); - tcg_gen_extrh_i64_i32(cpu_R[rdhi], cpu_V0); - } else { /* TMCRR */ - tcg_gen_concat_i32_i64(cpu_V0, cpu_R[rdlo], cpu_R[rdhi]); - iwmmxt_store_reg(cpu_V0, wrd); - gen_op_iwmmxt_set_mup(); - } - return 0; - } - - wrd = (insn >> 12) & 0xf; - addr = tcg_temp_new_i32(); - if (gen_iwmmxt_address(s, insn, addr)) { - return 1; - } - if (insn & ARM_CP_RW_BIT) { - if ((insn >> 28) == 0xf) { /* WLDRW wCx */ - tmp = tcg_temp_new_i32(); - gen_aa32_ld32u(s, tmp, addr, get_mem_index(s)); - iwmmxt_store_creg(wrd, tmp); - } else { - i = 1; - if (insn & (1 << 8)) { - if (insn & (1 << 22)) { /* WLDRD */ - gen_aa32_ld64(s, cpu_M0, addr, get_mem_index(s)); - i = 0; - } else { /* WLDRW wRd */ - tmp = tcg_temp_new_i32(); - gen_aa32_ld32u(s, tmp, addr, get_mem_index(s)); - } - } else { - tmp = tcg_temp_new_i32(); - if (insn & (1 << 22)) { /* WLDRH */ - gen_aa32_ld16u(s, tmp, addr, get_mem_index(s)); - } else { /* WLDRB */ - gen_aa32_ld8u(s, tmp, addr, get_mem_index(s)); - } - } - if (i) { - tcg_gen_extu_i32_i64(cpu_M0, tmp); - } - gen_op_iwmmxt_movq_wRn_M0(wrd); - } - } else { - if ((insn >> 28) == 0xf) { /* WSTRW wCx */ - tmp = iwmmxt_load_creg(wrd); - gen_aa32_st32(s, tmp, addr, get_mem_index(s)); - } else { - gen_op_iwmmxt_movq_M0_wRn(wrd); - tmp = tcg_temp_new_i32(); - if (insn & (1 << 8)) { - if (insn & (1 << 22)) { /* WSTRD */ - gen_aa32_st64(s, cpu_M0, addr, get_mem_index(s)); - } else { /* WSTRW wRd */ - tcg_gen_extrl_i64_i32(tmp, cpu_M0); - gen_aa32_st32(s, tmp, addr, get_mem_index(s)); - } - } else { - if (insn & (1 << 22)) { /* WSTRH */ - tcg_gen_extrl_i64_i32(tmp, cpu_M0); - gen_aa32_st16(s, tmp, addr, get_mem_index(s)); - } else { /* WSTRB */ - tcg_gen_extrl_i64_i32(tmp, cpu_M0); - gen_aa32_st8(s, tmp, addr, get_mem_index(s)); - } - } - } - } - return 0; - } - - if ((insn & 0x0f000000) != 0x0e000000) - return 1; - - switch (((insn >> 12) & 0xf00) | ((insn >> 4) & 0xff)) { - case 0x000: /* WOR */ - wrd = (insn >> 12) & 0xf; - rd0 = (insn >> 0) & 0xf; - rd1 = (insn >> 16) & 0xf; - gen_op_iwmmxt_movq_M0_wRn(rd0); - gen_op_iwmmxt_orq_M0_wRn(rd1); - gen_op_iwmmxt_setpsr_nz(); - gen_op_iwmmxt_movq_wRn_M0(wrd); - gen_op_iwmmxt_set_mup(); - gen_op_iwmmxt_set_cup(); - break; - case 0x011: /* TMCR */ - if (insn & 0xf) - return 1; - rd = (insn >> 12) & 0xf; - wrd = (insn >> 16) & 0xf; - switch (wrd) { - case ARM_IWMMXT_wCID: - case ARM_IWMMXT_wCASF: - break; - case ARM_IWMMXT_wCon: - gen_op_iwmmxt_set_cup(); - /* Fall through. */ - case ARM_IWMMXT_wCSSF: - tmp = iwmmxt_load_creg(wrd); - tmp2 = load_reg(s, rd); - tcg_gen_andc_i32(tmp, tmp, tmp2); - iwmmxt_store_creg(wrd, tmp); - break; - case ARM_IWMMXT_wCGR0: - case ARM_IWMMXT_wCGR1: - case ARM_IWMMXT_wCGR2: - case ARM_IWMMXT_wCGR3: - gen_op_iwmmxt_set_cup(); - tmp = load_reg(s, rd); - iwmmxt_store_creg(wrd, tmp); - break; - default: - return 1; - } - break; - case 0x100: /* WXOR */ - wrd = (insn >> 12) & 0xf; - rd0 = (insn >> 0) & 0xf; - rd1 = (insn >> 16) & 0xf; - gen_op_iwmmxt_movq_M0_wRn(rd0); - gen_op_iwmmxt_xorq_M0_wRn(rd1); - gen_op_iwmmxt_setpsr_nz(); - gen_op_iwmmxt_movq_wRn_M0(wrd); - gen_op_iwmmxt_set_mup(); - gen_op_iwmmxt_set_cup(); - break; - case 0x111: /* TMRC */ - if (insn & 0xf) - return 1; - rd = (insn >> 12) & 0xf; - wrd = (insn >> 16) & 0xf; - tmp = iwmmxt_load_creg(wrd); - store_reg(s, rd, tmp); - break; - case 0x300: /* WANDN */ - wrd = (insn >> 12) & 0xf; - rd0 = (insn >> 0) & 0xf; - rd1 = (insn >> 16) & 0xf; - gen_op_iwmmxt_movq_M0_wRn(rd0); - tcg_gen_neg_i64(cpu_M0, cpu_M0); - gen_op_iwmmxt_andq_M0_wRn(rd1); - gen_op_iwmmxt_setpsr_nz(); - gen_op_iwmmxt_movq_wRn_M0(wrd); - gen_op_iwmmxt_set_mup(); - gen_op_iwmmxt_set_cup(); - break; - case 0x200: /* WAND */ - wrd = (insn >> 12) & 0xf; - rd0 = (insn >> 0) & 0xf; - rd1 = (insn >> 16) & 0xf; - gen_op_iwmmxt_movq_M0_wRn(rd0); - gen_op_iwmmxt_andq_M0_wRn(rd1); - gen_op_iwmmxt_setpsr_nz(); - gen_op_iwmmxt_movq_wRn_M0(wrd); - gen_op_iwmmxt_set_mup(); - gen_op_iwmmxt_set_cup(); - break; - case 0x810: case 0xa10: /* WMADD */ - wrd = (insn >> 12) & 0xf; - rd0 = (insn >> 0) & 0xf; - rd1 = (insn >> 16) & 0xf; - gen_op_iwmmxt_movq_M0_wRn(rd0); - if (insn & (1 << 21)) - gen_op_iwmmxt_maddsq_M0_wRn(rd1); - else - gen_op_iwmmxt_madduq_M0_wRn(rd1); - gen_op_iwmmxt_movq_wRn_M0(wrd); - gen_op_iwmmxt_set_mup(); - break; - case 0x10e: case 0x50e: case 0x90e: case 0xd0e: /* WUNPCKIL */ - wrd = (insn >> 12) & 0xf; - rd0 = (insn >> 16) & 0xf; - rd1 = (insn >> 0) & 0xf; - gen_op_iwmmxt_movq_M0_wRn(rd0); - switch ((insn >> 22) & 3) { - case 0: - gen_op_iwmmxt_unpacklb_M0_wRn(rd1); - break; - case 1: - gen_op_iwmmxt_unpacklw_M0_wRn(rd1); - break; - case 2: - gen_op_iwmmxt_unpackll_M0_wRn(rd1); - break; - case 3: - return 1; - } - gen_op_iwmmxt_movq_wRn_M0(wrd); - gen_op_iwmmxt_set_mup(); - gen_op_iwmmxt_set_cup(); - break; - case 0x10c: case 0x50c: case 0x90c: case 0xd0c: /* WUNPCKIH */ - wrd = (insn >> 12) & 0xf; - rd0 = (insn >> 16) & 0xf; - rd1 = (insn >> 0) & 0xf; - gen_op_iwmmxt_movq_M0_wRn(rd0); - switch ((insn >> 22) & 3) { - case 0: - gen_op_iwmmxt_unpackhb_M0_wRn(rd1); - break; - case 1: - gen_op_iwmmxt_unpackhw_M0_wRn(rd1); - break; - case 2: - gen_op_iwmmxt_unpackhl_M0_wRn(rd1); - break; - case 3: - return 1; - } - gen_op_iwmmxt_movq_wRn_M0(wrd); - gen_op_iwmmxt_set_mup(); - gen_op_iwmmxt_set_cup(); - break; - case 0x012: case 0x112: case 0x412: case 0x512: /* WSAD */ - wrd = (insn >> 12) & 0xf; - rd0 = (insn >> 16) & 0xf; - rd1 = (insn >> 0) & 0xf; - gen_op_iwmmxt_movq_M0_wRn(rd0); - if (insn & (1 << 22)) - gen_op_iwmmxt_sadw_M0_wRn(rd1); - else - gen_op_iwmmxt_sadb_M0_wRn(rd1); - if (!(insn & (1 << 20))) - gen_op_iwmmxt_addl_M0_wRn(wrd); - gen_op_iwmmxt_movq_wRn_M0(wrd); - gen_op_iwmmxt_set_mup(); - break; - case 0x010: case 0x110: case 0x210: case 0x310: /* WMUL */ - wrd = (insn >> 12) & 0xf; - rd0 = (insn >> 16) & 0xf; - rd1 = (insn >> 0) & 0xf; - gen_op_iwmmxt_movq_M0_wRn(rd0); - if (insn & (1 << 21)) { - if (insn & (1 << 20)) - gen_op_iwmmxt_mulshw_M0_wRn(rd1); - else - gen_op_iwmmxt_mulslw_M0_wRn(rd1); - } else { - if (insn & (1 << 20)) - gen_op_iwmmxt_muluhw_M0_wRn(rd1); - else - gen_op_iwmmxt_mululw_M0_wRn(rd1); - } - gen_op_iwmmxt_movq_wRn_M0(wrd); - gen_op_iwmmxt_set_mup(); - break; - case 0x410: case 0x510: case 0x610: case 0x710: /* WMAC */ - wrd = (insn >> 12) & 0xf; - rd0 = (insn >> 16) & 0xf; - rd1 = (insn >> 0) & 0xf; - gen_op_iwmmxt_movq_M0_wRn(rd0); - if (insn & (1 << 21)) - gen_op_iwmmxt_macsw_M0_wRn(rd1); - else - gen_op_iwmmxt_macuw_M0_wRn(rd1); - if (!(insn & (1 << 20))) { - iwmmxt_load_reg(cpu_V1, wrd); - tcg_gen_add_i64(cpu_M0, cpu_M0, cpu_V1); - } - gen_op_iwmmxt_movq_wRn_M0(wrd); - gen_op_iwmmxt_set_mup(); - break; - case 0x006: case 0x406: case 0x806: case 0xc06: /* WCMPEQ */ - wrd = (insn >> 12) & 0xf; - rd0 = (insn >> 16) & 0xf; - rd1 = (insn >> 0) & 0xf; - gen_op_iwmmxt_movq_M0_wRn(rd0); - switch ((insn >> 22) & 3) { - case 0: - gen_op_iwmmxt_cmpeqb_M0_wRn(rd1); - break; - case 1: - gen_op_iwmmxt_cmpeqw_M0_wRn(rd1); - break; - case 2: - gen_op_iwmmxt_cmpeql_M0_wRn(rd1); - break; - case 3: - return 1; - } - gen_op_iwmmxt_movq_wRn_M0(wrd); - gen_op_iwmmxt_set_mup(); - gen_op_iwmmxt_set_cup(); - break; - case 0x800: case 0x900: case 0xc00: case 0xd00: /* WAVG2 */ - wrd = (insn >> 12) & 0xf; - rd0 = (insn >> 16) & 0xf; - rd1 = (insn >> 0) & 0xf; - gen_op_iwmmxt_movq_M0_wRn(rd0); - if (insn & (1 << 22)) { - if (insn & (1 << 20)) - gen_op_iwmmxt_avgw1_M0_wRn(rd1); - else - gen_op_iwmmxt_avgw0_M0_wRn(rd1); - } else { - if (insn & (1 << 20)) - gen_op_iwmmxt_avgb1_M0_wRn(rd1); - else - gen_op_iwmmxt_avgb0_M0_wRn(rd1); - } - gen_op_iwmmxt_movq_wRn_M0(wrd); - gen_op_iwmmxt_set_mup(); - gen_op_iwmmxt_set_cup(); - break; - case 0x802: case 0x902: case 0xa02: case 0xb02: /* WALIGNR */ - wrd = (insn >> 12) & 0xf; - rd0 = (insn >> 16) & 0xf; - rd1 = (insn >> 0) & 0xf; - gen_op_iwmmxt_movq_M0_wRn(rd0); - tmp = iwmmxt_load_creg(ARM_IWMMXT_wCGR0 + ((insn >> 20) & 3)); - tcg_gen_andi_i32(tmp, tmp, 7); - iwmmxt_load_reg(cpu_V1, rd1); - gen_helper_iwmmxt_align(cpu_M0, cpu_M0, cpu_V1, tmp); - gen_op_iwmmxt_movq_wRn_M0(wrd); - gen_op_iwmmxt_set_mup(); - break; - case 0x601: case 0x605: case 0x609: case 0x60d: /* TINSR */ - if (((insn >> 6) & 3) == 3) - return 1; - rd = (insn >> 12) & 0xf; - wrd = (insn >> 16) & 0xf; - tmp = load_reg(s, rd); - gen_op_iwmmxt_movq_M0_wRn(wrd); - switch ((insn >> 6) & 3) { - case 0: - tmp2 = tcg_constant_i32(0xff); - tmp3 = tcg_constant_i32((insn & 7) << 3); - break; - case 1: - tmp2 = tcg_constant_i32(0xffff); - tmp3 = tcg_constant_i32((insn & 3) << 4); - break; - case 2: - tmp2 = tcg_constant_i32(0xffffffff); - tmp3 = tcg_constant_i32((insn & 1) << 5); - break; - default: - g_assert_not_reached(); - } - gen_helper_iwmmxt_insr(cpu_M0, cpu_M0, tmp, tmp2, tmp3); - gen_op_iwmmxt_movq_wRn_M0(wrd); - gen_op_iwmmxt_set_mup(); - break; - case 0x107: case 0x507: case 0x907: case 0xd07: /* TEXTRM */ - rd = (insn >> 12) & 0xf; - wrd = (insn >> 16) & 0xf; - if (rd == 15 || ((insn >> 22) & 3) == 3) - return 1; - gen_op_iwmmxt_movq_M0_wRn(wrd); - tmp = tcg_temp_new_i32(); - switch ((insn >> 22) & 3) { - case 0: - tcg_gen_shri_i64(cpu_M0, cpu_M0, (insn & 7) << 3); - tcg_gen_extrl_i64_i32(tmp, cpu_M0); - if (insn & 8) { - tcg_gen_ext8s_i32(tmp, tmp); - } else { - tcg_gen_andi_i32(tmp, tmp, 0xff); - } - break; - case 1: - tcg_gen_shri_i64(cpu_M0, cpu_M0, (insn & 3) << 4); - tcg_gen_extrl_i64_i32(tmp, cpu_M0); - if (insn & 8) { - tcg_gen_ext16s_i32(tmp, tmp); - } else { - tcg_gen_andi_i32(tmp, tmp, 0xffff); - } - break; - case 2: - tcg_gen_shri_i64(cpu_M0, cpu_M0, (insn & 1) << 5); - tcg_gen_extrl_i64_i32(tmp, cpu_M0); - break; - } - store_reg(s, rd, tmp); - break; - case 0x117: case 0x517: case 0x917: case 0xd17: /* TEXTRC */ - if ((insn & 0x000ff008) != 0x0003f000 || ((insn >> 22) & 3) == 3) - return 1; - tmp = iwmmxt_load_creg(ARM_IWMMXT_wCASF); - switch ((insn >> 22) & 3) { - case 0: - tcg_gen_shri_i32(tmp, tmp, ((insn & 7) << 2) + 0); - break; - case 1: - tcg_gen_shri_i32(tmp, tmp, ((insn & 3) << 3) + 4); - break; - case 2: - tcg_gen_shri_i32(tmp, tmp, ((insn & 1) << 4) + 12); - break; - } - tcg_gen_shli_i32(tmp, tmp, 28); - gen_set_nzcv(tmp); - break; - case 0x401: case 0x405: case 0x409: case 0x40d: /* TBCST */ - if (((insn >> 6) & 3) == 3) - return 1; - rd = (insn >> 12) & 0xf; - wrd = (insn >> 16) & 0xf; - tmp = load_reg(s, rd); - switch ((insn >> 6) & 3) { - case 0: - gen_helper_iwmmxt_bcstb(cpu_M0, tmp); - break; - case 1: - gen_helper_iwmmxt_bcstw(cpu_M0, tmp); - break; - case 2: - gen_helper_iwmmxt_bcstl(cpu_M0, tmp); - break; - } - gen_op_iwmmxt_movq_wRn_M0(wrd); - gen_op_iwmmxt_set_mup(); - break; - case 0x113: case 0x513: case 0x913: case 0xd13: /* TANDC */ - if ((insn & 0x000ff00f) != 0x0003f000 || ((insn >> 22) & 3) == 3) - return 1; - tmp = iwmmxt_load_creg(ARM_IWMMXT_wCASF); - tmp2 = tcg_temp_new_i32(); - tcg_gen_mov_i32(tmp2, tmp); - switch ((insn >> 22) & 3) { - case 0: - for (i = 0; i < 7; i ++) { - tcg_gen_shli_i32(tmp2, tmp2, 4); - tcg_gen_and_i32(tmp, tmp, tmp2); - } - break; - case 1: - for (i = 0; i < 3; i ++) { - tcg_gen_shli_i32(tmp2, tmp2, 8); - tcg_gen_and_i32(tmp, tmp, tmp2); - } - break; - case 2: - tcg_gen_shli_i32(tmp2, tmp2, 16); - tcg_gen_and_i32(tmp, tmp, tmp2); - break; - } - gen_set_nzcv(tmp); - break; - case 0x01c: case 0x41c: case 0x81c: case 0xc1c: /* WACC */ - wrd = (insn >> 12) & 0xf; - rd0 = (insn >> 16) & 0xf; - gen_op_iwmmxt_movq_M0_wRn(rd0); - switch ((insn >> 22) & 3) { - case 0: - gen_helper_iwmmxt_addcb(cpu_M0, cpu_M0); - break; - case 1: - gen_helper_iwmmxt_addcw(cpu_M0, cpu_M0); - break; - case 2: - gen_helper_iwmmxt_addcl(cpu_M0, cpu_M0); - break; - case 3: - return 1; - } - gen_op_iwmmxt_movq_wRn_M0(wrd); - gen_op_iwmmxt_set_mup(); - break; - case 0x115: case 0x515: case 0x915: case 0xd15: /* TORC */ - if ((insn & 0x000ff00f) != 0x0003f000 || ((insn >> 22) & 3) == 3) - return 1; - tmp = iwmmxt_load_creg(ARM_IWMMXT_wCASF); - tmp2 = tcg_temp_new_i32(); - tcg_gen_mov_i32(tmp2, tmp); - switch ((insn >> 22) & 3) { - case 0: - for (i = 0; i < 7; i ++) { - tcg_gen_shli_i32(tmp2, tmp2, 4); - tcg_gen_or_i32(tmp, tmp, tmp2); - } - break; - case 1: - for (i = 0; i < 3; i ++) { - tcg_gen_shli_i32(tmp2, tmp2, 8); - tcg_gen_or_i32(tmp, tmp, tmp2); - } - break; - case 2: - tcg_gen_shli_i32(tmp2, tmp2, 16); - tcg_gen_or_i32(tmp, tmp, tmp2); - break; - } - gen_set_nzcv(tmp); - break; - case 0x103: case 0x503: case 0x903: case 0xd03: /* TMOVMSK */ - rd = (insn >> 12) & 0xf; - rd0 = (insn >> 16) & 0xf; - if ((insn & 0xf) != 0 || ((insn >> 22) & 3) == 3) - return 1; - gen_op_iwmmxt_movq_M0_wRn(rd0); - tmp = tcg_temp_new_i32(); - switch ((insn >> 22) & 3) { - case 0: - gen_helper_iwmmxt_msbb(tmp, cpu_M0); - break; - case 1: - gen_helper_iwmmxt_msbw(tmp, cpu_M0); - break; - case 2: - gen_helper_iwmmxt_msbl(tmp, cpu_M0); - break; - } - store_reg(s, rd, tmp); - break; - case 0x106: case 0x306: case 0x506: case 0x706: /* WCMPGT */ - case 0x906: case 0xb06: case 0xd06: case 0xf06: - wrd = (insn >> 12) & 0xf; - rd0 = (insn >> 16) & 0xf; - rd1 = (insn >> 0) & 0xf; - gen_op_iwmmxt_movq_M0_wRn(rd0); - switch ((insn >> 22) & 3) { - case 0: - if (insn & (1 << 21)) - gen_op_iwmmxt_cmpgtsb_M0_wRn(rd1); - else - gen_op_iwmmxt_cmpgtub_M0_wRn(rd1); - break; - case 1: - if (insn & (1 << 21)) - gen_op_iwmmxt_cmpgtsw_M0_wRn(rd1); - else - gen_op_iwmmxt_cmpgtuw_M0_wRn(rd1); - break; - case 2: - if (insn & (1 << 21)) - gen_op_iwmmxt_cmpgtsl_M0_wRn(rd1); - else - gen_op_iwmmxt_cmpgtul_M0_wRn(rd1); - break; - case 3: - return 1; - } - gen_op_iwmmxt_movq_wRn_M0(wrd); - gen_op_iwmmxt_set_mup(); - gen_op_iwmmxt_set_cup(); - break; - case 0x00e: case 0x20e: case 0x40e: case 0x60e: /* WUNPCKEL */ - case 0x80e: case 0xa0e: case 0xc0e: case 0xe0e: - wrd = (insn >> 12) & 0xf; - rd0 = (insn >> 16) & 0xf; - gen_op_iwmmxt_movq_M0_wRn(rd0); - switch ((insn >> 22) & 3) { - case 0: - if (insn & (1 << 21)) - gen_op_iwmmxt_unpacklsb_M0(); - else - gen_op_iwmmxt_unpacklub_M0(); - break; - case 1: - if (insn & (1 << 21)) - gen_op_iwmmxt_unpacklsw_M0(); - else - gen_op_iwmmxt_unpackluw_M0(); - break; - case 2: - if (insn & (1 << 21)) - gen_op_iwmmxt_unpacklsl_M0(); - else - gen_op_iwmmxt_unpacklul_M0(); - break; - case 3: - return 1; - } - gen_op_iwmmxt_movq_wRn_M0(wrd); - gen_op_iwmmxt_set_mup(); - gen_op_iwmmxt_set_cup(); - break; - case 0x00c: case 0x20c: case 0x40c: case 0x60c: /* WUNPCKEH */ - case 0x80c: case 0xa0c: case 0xc0c: case 0xe0c: - wrd = (insn >> 12) & 0xf; - rd0 = (insn >> 16) & 0xf; - gen_op_iwmmxt_movq_M0_wRn(rd0); - switch ((insn >> 22) & 3) { - case 0: - if (insn & (1 << 21)) - gen_op_iwmmxt_unpackhsb_M0(); - else - gen_op_iwmmxt_unpackhub_M0(); - break; - case 1: - if (insn & (1 << 21)) - gen_op_iwmmxt_unpackhsw_M0(); - else - gen_op_iwmmxt_unpackhuw_M0(); - break; - case 2: - if (insn & (1 << 21)) - gen_op_iwmmxt_unpackhsl_M0(); - else - gen_op_iwmmxt_unpackhul_M0(); - break; - case 3: - return 1; - } - gen_op_iwmmxt_movq_wRn_M0(wrd); - gen_op_iwmmxt_set_mup(); - gen_op_iwmmxt_set_cup(); - break; - case 0x204: case 0x604: case 0xa04: case 0xe04: /* WSRL */ - case 0x214: case 0x614: case 0xa14: case 0xe14: - if (((insn >> 22) & 3) == 0) - return 1; - wrd = (insn >> 12) & 0xf; - rd0 = (insn >> 16) & 0xf; - gen_op_iwmmxt_movq_M0_wRn(rd0); - tmp = tcg_temp_new_i32(); - if (gen_iwmmxt_shift(insn, 0xff, tmp)) { - return 1; - } - switch ((insn >> 22) & 3) { - case 1: - gen_helper_iwmmxt_srlw(cpu_M0, tcg_env, cpu_M0, tmp); - break; - case 2: - gen_helper_iwmmxt_srll(cpu_M0, tcg_env, cpu_M0, tmp); - break; - case 3: - gen_helper_iwmmxt_srlq(cpu_M0, tcg_env, cpu_M0, tmp); - break; - } - gen_op_iwmmxt_movq_wRn_M0(wrd); - gen_op_iwmmxt_set_mup(); - gen_op_iwmmxt_set_cup(); - break; - case 0x004: case 0x404: case 0x804: case 0xc04: /* WSRA */ - case 0x014: case 0x414: case 0x814: case 0xc14: - if (((insn >> 22) & 3) == 0) - return 1; - wrd = (insn >> 12) & 0xf; - rd0 = (insn >> 16) & 0xf; - gen_op_iwmmxt_movq_M0_wRn(rd0); - tmp = tcg_temp_new_i32(); - if (gen_iwmmxt_shift(insn, 0xff, tmp)) { - return 1; - } - switch ((insn >> 22) & 3) { - case 1: - gen_helper_iwmmxt_sraw(cpu_M0, tcg_env, cpu_M0, tmp); - break; - case 2: - gen_helper_iwmmxt_sral(cpu_M0, tcg_env, cpu_M0, tmp); - break; - case 3: - gen_helper_iwmmxt_sraq(cpu_M0, tcg_env, cpu_M0, tmp); - break; - } - gen_op_iwmmxt_movq_wRn_M0(wrd); - gen_op_iwmmxt_set_mup(); - gen_op_iwmmxt_set_cup(); - break; - case 0x104: case 0x504: case 0x904: case 0xd04: /* WSLL */ - case 0x114: case 0x514: case 0x914: case 0xd14: - if (((insn >> 22) & 3) == 0) - return 1; - wrd = (insn >> 12) & 0xf; - rd0 = (insn >> 16) & 0xf; - gen_op_iwmmxt_movq_M0_wRn(rd0); - tmp = tcg_temp_new_i32(); - if (gen_iwmmxt_shift(insn, 0xff, tmp)) { - return 1; - } - switch ((insn >> 22) & 3) { - case 1: - gen_helper_iwmmxt_sllw(cpu_M0, tcg_env, cpu_M0, tmp); - break; - case 2: - gen_helper_iwmmxt_slll(cpu_M0, tcg_env, cpu_M0, tmp); - break; - case 3: - gen_helper_iwmmxt_sllq(cpu_M0, tcg_env, cpu_M0, tmp); - break; - } - gen_op_iwmmxt_movq_wRn_M0(wrd); - gen_op_iwmmxt_set_mup(); - gen_op_iwmmxt_set_cup(); - break; - case 0x304: case 0x704: case 0xb04: case 0xf04: /* WROR */ - case 0x314: case 0x714: case 0xb14: case 0xf14: - if (((insn >> 22) & 3) == 0) - return 1; - wrd = (insn >> 12) & 0xf; - rd0 = (insn >> 16) & 0xf; - gen_op_iwmmxt_movq_M0_wRn(rd0); - tmp = tcg_temp_new_i32(); - switch ((insn >> 22) & 3) { - case 1: - if (gen_iwmmxt_shift(insn, 0xf, tmp)) { - return 1; - } - gen_helper_iwmmxt_rorw(cpu_M0, tcg_env, cpu_M0, tmp); - break; - case 2: - if (gen_iwmmxt_shift(insn, 0x1f, tmp)) { - return 1; - } - gen_helper_iwmmxt_rorl(cpu_M0, tcg_env, cpu_M0, tmp); - break; - case 3: - if (gen_iwmmxt_shift(insn, 0x3f, tmp)) { - return 1; - } - gen_helper_iwmmxt_rorq(cpu_M0, tcg_env, cpu_M0, tmp); - break; - } - gen_op_iwmmxt_movq_wRn_M0(wrd); - gen_op_iwmmxt_set_mup(); - gen_op_iwmmxt_set_cup(); - break; - case 0x116: case 0x316: case 0x516: case 0x716: /* WMIN */ - case 0x916: case 0xb16: case 0xd16: case 0xf16: - wrd = (insn >> 12) & 0xf; - rd0 = (insn >> 16) & 0xf; - rd1 = (insn >> 0) & 0xf; - gen_op_iwmmxt_movq_M0_wRn(rd0); - switch ((insn >> 22) & 3) { - case 0: - if (insn & (1 << 21)) - gen_op_iwmmxt_minsb_M0_wRn(rd1); - else - gen_op_iwmmxt_minub_M0_wRn(rd1); - break; - case 1: - if (insn & (1 << 21)) - gen_op_iwmmxt_minsw_M0_wRn(rd1); - else - gen_op_iwmmxt_minuw_M0_wRn(rd1); - break; - case 2: - if (insn & (1 << 21)) - gen_op_iwmmxt_minsl_M0_wRn(rd1); - else - gen_op_iwmmxt_minul_M0_wRn(rd1); - break; - case 3: - return 1; - } - gen_op_iwmmxt_movq_wRn_M0(wrd); - gen_op_iwmmxt_set_mup(); - break; - case 0x016: case 0x216: case 0x416: case 0x616: /* WMAX */ - case 0x816: case 0xa16: case 0xc16: case 0xe16: - wrd = (insn >> 12) & 0xf; - rd0 = (insn >> 16) & 0xf; - rd1 = (insn >> 0) & 0xf; - gen_op_iwmmxt_movq_M0_wRn(rd0); - switch ((insn >> 22) & 3) { - case 0: - if (insn & (1 << 21)) - gen_op_iwmmxt_maxsb_M0_wRn(rd1); - else - gen_op_iwmmxt_maxub_M0_wRn(rd1); - break; - case 1: - if (insn & (1 << 21)) - gen_op_iwmmxt_maxsw_M0_wRn(rd1); - else - gen_op_iwmmxt_maxuw_M0_wRn(rd1); - break; - case 2: - if (insn & (1 << 21)) - gen_op_iwmmxt_maxsl_M0_wRn(rd1); - else - gen_op_iwmmxt_maxul_M0_wRn(rd1); - break; - case 3: - return 1; - } - gen_op_iwmmxt_movq_wRn_M0(wrd); - gen_op_iwmmxt_set_mup(); - break; - case 0x002: case 0x102: case 0x202: case 0x302: /* WALIGNI */ - case 0x402: case 0x502: case 0x602: case 0x702: - wrd = (insn >> 12) & 0xf; - rd0 = (insn >> 16) & 0xf; - rd1 = (insn >> 0) & 0xf; - gen_op_iwmmxt_movq_M0_wRn(rd0); - iwmmxt_load_reg(cpu_V1, rd1); - gen_helper_iwmmxt_align(cpu_M0, cpu_M0, cpu_V1, - tcg_constant_i32((insn >> 20) & 3)); - gen_op_iwmmxt_movq_wRn_M0(wrd); - gen_op_iwmmxt_set_mup(); - break; - case 0x01a: case 0x11a: case 0x21a: case 0x31a: /* WSUB */ - case 0x41a: case 0x51a: case 0x61a: case 0x71a: - case 0x81a: case 0x91a: case 0xa1a: case 0xb1a: - case 0xc1a: case 0xd1a: case 0xe1a: case 0xf1a: - wrd = (insn >> 12) & 0xf; - rd0 = (insn >> 16) & 0xf; - rd1 = (insn >> 0) & 0xf; - gen_op_iwmmxt_movq_M0_wRn(rd0); - switch ((insn >> 20) & 0xf) { - case 0x0: - gen_op_iwmmxt_subnb_M0_wRn(rd1); - break; - case 0x1: - gen_op_iwmmxt_subub_M0_wRn(rd1); - break; - case 0x3: - gen_op_iwmmxt_subsb_M0_wRn(rd1); - break; - case 0x4: - gen_op_iwmmxt_subnw_M0_wRn(rd1); - break; - case 0x5: - gen_op_iwmmxt_subuw_M0_wRn(rd1); - break; - case 0x7: - gen_op_iwmmxt_subsw_M0_wRn(rd1); - break; - case 0x8: - gen_op_iwmmxt_subnl_M0_wRn(rd1); - break; - case 0x9: - gen_op_iwmmxt_subul_M0_wRn(rd1); - break; - case 0xb: - gen_op_iwmmxt_subsl_M0_wRn(rd1); - break; - default: - return 1; - } - gen_op_iwmmxt_movq_wRn_M0(wrd); - gen_op_iwmmxt_set_mup(); - gen_op_iwmmxt_set_cup(); - break; - case 0x01e: case 0x11e: case 0x21e: case 0x31e: /* WSHUFH */ - case 0x41e: case 0x51e: case 0x61e: case 0x71e: - case 0x81e: case 0x91e: case 0xa1e: case 0xb1e: - case 0xc1e: case 0xd1e: case 0xe1e: case 0xf1e: - wrd = (insn >> 12) & 0xf; - rd0 = (insn >> 16) & 0xf; - gen_op_iwmmxt_movq_M0_wRn(rd0); - tmp = tcg_constant_i32(((insn >> 16) & 0xf0) | (insn & 0x0f)); - gen_helper_iwmmxt_shufh(cpu_M0, tcg_env, cpu_M0, tmp); - gen_op_iwmmxt_movq_wRn_M0(wrd); - gen_op_iwmmxt_set_mup(); - gen_op_iwmmxt_set_cup(); - break; - case 0x018: case 0x118: case 0x218: case 0x318: /* WADD */ - case 0x418: case 0x518: case 0x618: case 0x718: - case 0x818: case 0x918: case 0xa18: case 0xb18: - case 0xc18: case 0xd18: case 0xe18: case 0xf18: - wrd = (insn >> 12) & 0xf; - rd0 = (insn >> 16) & 0xf; - rd1 = (insn >> 0) & 0xf; - gen_op_iwmmxt_movq_M0_wRn(rd0); - switch ((insn >> 20) & 0xf) { - case 0x0: - gen_op_iwmmxt_addnb_M0_wRn(rd1); - break; - case 0x1: - gen_op_iwmmxt_addub_M0_wRn(rd1); - break; - case 0x3: - gen_op_iwmmxt_addsb_M0_wRn(rd1); - break; - case 0x4: - gen_op_iwmmxt_addnw_M0_wRn(rd1); - break; - case 0x5: - gen_op_iwmmxt_adduw_M0_wRn(rd1); - break; - case 0x7: - gen_op_iwmmxt_addsw_M0_wRn(rd1); - break; - case 0x8: - gen_op_iwmmxt_addnl_M0_wRn(rd1); - break; - case 0x9: - gen_op_iwmmxt_addul_M0_wRn(rd1); - break; - case 0xb: - gen_op_iwmmxt_addsl_M0_wRn(rd1); - break; - default: - return 1; - } - gen_op_iwmmxt_movq_wRn_M0(wrd); - gen_op_iwmmxt_set_mup(); - gen_op_iwmmxt_set_cup(); - break; - case 0x008: case 0x108: case 0x208: case 0x308: /* WPACK */ - case 0x408: case 0x508: case 0x608: case 0x708: - case 0x808: case 0x908: case 0xa08: case 0xb08: - case 0xc08: case 0xd08: case 0xe08: case 0xf08: - if (!(insn & (1 << 20)) || ((insn >> 22) & 3) == 0) - return 1; - wrd = (insn >> 12) & 0xf; - rd0 = (insn >> 16) & 0xf; - rd1 = (insn >> 0) & 0xf; - gen_op_iwmmxt_movq_M0_wRn(rd0); - switch ((insn >> 22) & 3) { - case 1: - if (insn & (1 << 21)) - gen_op_iwmmxt_packsw_M0_wRn(rd1); - else - gen_op_iwmmxt_packuw_M0_wRn(rd1); - break; - case 2: - if (insn & (1 << 21)) - gen_op_iwmmxt_packsl_M0_wRn(rd1); - else - gen_op_iwmmxt_packul_M0_wRn(rd1); - break; - case 3: - if (insn & (1 << 21)) - gen_op_iwmmxt_packsq_M0_wRn(rd1); - else - gen_op_iwmmxt_packuq_M0_wRn(rd1); - break; - } - gen_op_iwmmxt_movq_wRn_M0(wrd); - gen_op_iwmmxt_set_mup(); - gen_op_iwmmxt_set_cup(); - break; - case 0x201: case 0x203: case 0x205: case 0x207: - case 0x209: case 0x20b: case 0x20d: case 0x20f: - case 0x211: case 0x213: case 0x215: case 0x217: - case 0x219: case 0x21b: case 0x21d: case 0x21f: - wrd = (insn >> 5) & 0xf; - rd0 = (insn >> 12) & 0xf; - rd1 = (insn >> 0) & 0xf; - if (rd0 == 0xf || rd1 == 0xf) - return 1; - gen_op_iwmmxt_movq_M0_wRn(wrd); - tmp = load_reg(s, rd0); - tmp2 = load_reg(s, rd1); - switch ((insn >> 16) & 0xf) { - case 0x0: /* TMIA */ - gen_helper_iwmmxt_muladdsl(cpu_M0, cpu_M0, tmp, tmp2); - break; - case 0x8: /* TMIAPH */ - gen_helper_iwmmxt_muladdsw(cpu_M0, cpu_M0, tmp, tmp2); - break; - case 0xc: case 0xd: case 0xe: case 0xf: /* TMIAxy */ - if (insn & (1 << 16)) - tcg_gen_shri_i32(tmp, tmp, 16); - if (insn & (1 << 17)) - tcg_gen_shri_i32(tmp2, tmp2, 16); - gen_helper_iwmmxt_muladdswl(cpu_M0, cpu_M0, tmp, tmp2); - break; - default: - return 1; - } - gen_op_iwmmxt_movq_wRn_M0(wrd); - gen_op_iwmmxt_set_mup(); - break; - default: - return 1; - } - - return 0; -} - -/* Disassemble an XScale DSP instruction. Returns nonzero if an error occurred - (ie. an undefined instruction). */ -static int disas_dsp_insn(DisasContext *s, uint32_t insn) -{ - int acc, rd0, rd1, rdhi, rdlo; - TCGv_i32 tmp, tmp2; - - if ((insn & 0x0ff00f10) == 0x0e200010) { - /* Multiply with Internal Accumulate Format */ - rd0 = (insn >> 12) & 0xf; - rd1 = insn & 0xf; - acc = (insn >> 5) & 7; - - if (acc != 0) - return 1; - - tmp = load_reg(s, rd0); - tmp2 = load_reg(s, rd1); - switch ((insn >> 16) & 0xf) { - case 0x0: /* MIA */ - gen_helper_iwmmxt_muladdsl(cpu_M0, cpu_M0, tmp, tmp2); - break; - case 0x8: /* MIAPH */ - gen_helper_iwmmxt_muladdsw(cpu_M0, cpu_M0, tmp, tmp2); - break; - case 0xc: /* MIABB */ - case 0xd: /* MIABT */ - case 0xe: /* MIATB */ - case 0xf: /* MIATT */ - if (insn & (1 << 16)) - tcg_gen_shri_i32(tmp, tmp, 16); - if (insn & (1 << 17)) - tcg_gen_shri_i32(tmp2, tmp2, 16); - gen_helper_iwmmxt_muladdswl(cpu_M0, cpu_M0, tmp, tmp2); - break; - default: - return 1; - } - - gen_op_iwmmxt_movq_wRn_M0(acc); - return 0; - } - - if ((insn & 0x0fe00ff8) == 0x0c400000) { - /* Internal Accumulator Access Format */ - rdhi = (insn >> 16) & 0xf; - rdlo = (insn >> 12) & 0xf; - acc = insn & 7; - - if (acc != 0) - return 1; - - if (insn & ARM_CP_RW_BIT) { /* MRA */ - iwmmxt_load_reg(cpu_V0, acc); - tcg_gen_extrl_i64_i32(cpu_R[rdlo], cpu_V0); - tcg_gen_extrh_i64_i32(cpu_R[rdhi], cpu_V0); - tcg_gen_andi_i32(cpu_R[rdhi], cpu_R[rdhi], (1 << (40 - 32)) - 1); - } else { /* MAR */ - tcg_gen_concat_i32_i64(cpu_V0, cpu_R[rdlo], cpu_R[rdhi]); - iwmmxt_store_reg(cpu_V0, acc); - } - return 0; - } - - return 1; -} - static void gen_goto_ptr(void) { tcg_gen_lookup_and_goto_ptr(); @@ -3048,13 +1789,10 @@ static void do_coproc_insn(DisasContext *s, int cpnum, int is64, } if ((s->hstr_active && s->current_el == 0) || ri->accessfn || - (ri->fgt && s->fgt_active) || - (arm_dc_feature(s, ARM_FEATURE_XSCALE) && cpnum < 14)) { + (ri->fgt && s->fgt_active)) { /* * Emit code to perform further access permissions checks at * runtime; this may result in an exception. - * Note that on XScale all cp0..c13 registers do an access check - * call in order to handle c15_cpar. */ gen_set_condexec(s); gen_update_pc(s, 0); @@ -3192,24 +1930,6 @@ static void do_coproc_insn(DisasContext *s, int cpnum, int is64, } } -/* Decode XScale DSP or iWMMXt insn (in the copro space, cp=0 or 1) */ -static void disas_xscale_insn(DisasContext *s, uint32_t insn) -{ - int cpnum = (insn >> 8) & 0xf; - - if (extract32(s->c15_cpar, cpnum, 1) == 0) { - unallocated_encoding(s); - } else if (arm_dc_feature(s, ARM_FEATURE_IWMMXT)) { - if (disas_iwmmxt_insn(s, insn)) { - unallocated_encoding(s); - } - } else if (arm_dc_feature(s, ARM_FEATURE_XSCALE)) { - if (disas_dsp_insn(s, insn)) { - unallocated_encoding(s); - } - } -} - /* Store a 64-bit value to a register pair. Clobbers val. */ static void gen_storeq_reg(DisasContext *s, int rlow, int rhigh, TCGv_i64 val) { @@ -3569,14 +2289,7 @@ static bool valid_cp(DisasContext *s, int cp) * only cp14 and cp15 are valid, and other values aren't considered * to be in the coprocessor-instruction space at all. v8M still * permits coprocessors 0..7. - * For XScale, we must not decode the XScale cp0, cp1 space as - * a standard coprocessor insn, because we want to fall through to - * the legacy disas_xscale_insn() decoder after decodetree is done. */ - if (arm_dc_feature(s, ARM_FEATURE_XSCALE) && (cp == 0 || cp == 1)) { - return false; - } - if (arm_dc_feature(s, ARM_FEATURE_V8) && !arm_dc_feature(s, ARM_FEATURE_M)) { return cp >= 14; @@ -7343,18 +6056,6 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn) disas_neon_shared(s, insn)) { return; } - /* fall back to legacy decoder */ - - if ((insn & 0x0e000f00) == 0x0c000100) { - if (arm_dc_feature(s, ARM_FEATURE_IWMMXT)) { - /* iWMMXt register transfer. */ - if (extract32(s->c15_cpar, 1, 1)) { - if (!disas_iwmmxt_insn(s, insn)) { - return; - } - } - } - } goto illegal_op; } if (cond != 0xe) { @@ -7368,16 +6069,7 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn) disas_vfp(s, insn)) { return; } - /* fall back to legacy decoder */ - /* TODO: convert xscale/iwmmxt decoder to decodetree ?? */ - if (arm_dc_feature(s, ARM_FEATURE_XSCALE)) { - if (((insn & 0x0c000e00) == 0x0c000000) - && ((insn & 0x03000000) != 0x03000000)) { - /* Coprocessor insn, coprocessor 0 or 1 */ - disas_xscale_insn(s, insn); - return; - } - } + /* We didn't match anything in the decoder: UNDEF */ illegal_op: unallocated_encoding(s); @@ -7606,12 +6298,8 @@ static void arm_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) dc->hstr_active = EX_TBFLAG_A32(tb_flags, HSTR_ACTIVE); dc->ns = EX_TBFLAG_A32(tb_flags, NS); dc->vfp_enabled = EX_TBFLAG_A32(tb_flags, VFPEN); - if (arm_feature(env, ARM_FEATURE_XSCALE)) { - dc->c15_cpar = EX_TBFLAG_A32(tb_flags, XSCALE_CPAR); - } else { - dc->vec_len = EX_TBFLAG_A32(tb_flags, VECLEN); - dc->vec_stride = EX_TBFLAG_A32(tb_flags, VECSTRIDE); - } + dc->vec_len = EX_TBFLAG_A32(tb_flags, VECLEN); + dc->vec_stride = EX_TBFLAG_A32(tb_flags, VECSTRIDE); dc->sme_trap_nonstreaming = EX_TBFLAG_A32(tb_flags, SME_TRAP_NONSTREAMING); } @@ -7651,10 +6339,6 @@ static void arm_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) int bound = -(dc->base.pc_first | TARGET_PAGE_MASK) / 4; dc->base.max_insns = MIN(dc->base.max_insns, bound); } - - cpu_V0 = tcg_temp_new_i64(); - cpu_V1 = tcg_temp_new_i64(); - cpu_M0 = tcg_temp_new_i64(); } static void arm_tr_tb_start(DisasContextBase *dcbase, CPUState *cpu) diff --git a/target/arm/tcg/translate.h b/target/arm/tcg/translate.h index f974996f3f..ec4755ae3f 100644 --- a/target/arm/tcg/translate.h +++ b/target/arm/tcg/translate.h @@ -175,8 +175,6 @@ typedef struct DisasContext { uint8_t gm_blocksize; /* True if the current insn_start has been updated. */ bool insn_start_updated; - /* Bottom two bits of XScale c15_cpar coprocessor access control reg */ - int c15_cpar; /* Offset from VNCR_EL2 when FEAT_NV2 redirects this reg to memory */ uint32_t nv2_redirect_offset; } DisasContext; From 144aac11d6e0600f6dc644ad0d7e47110d8484f1 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 28 Aug 2025 15:04:20 +0100 Subject: [PATCH 0418/2396] target/arm: Remove iwmmxt helper functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove the iwmmxt helper functions which are no longer called now that we have removed the associated translate.c handling. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-id: 20250828140422.3271703-4-peter.maydell@linaro.org --- target/arm/tcg/helper.h | 95 ----- target/arm/tcg/iwmmxt_helper.c | 672 --------------------------------- target/arm/tcg/meson.build | 2 - 3 files changed, 769 deletions(-) delete mode 100644 target/arm/tcg/iwmmxt_helper.c diff --git a/target/arm/tcg/helper.h b/target/arm/tcg/helper.h index 4da32db902..4636d1bc03 100644 --- a/target/arm/tcg/helper.h +++ b/target/arm/tcg/helper.h @@ -444,101 +444,6 @@ DEF_HELPER_3(neon_acgt_f32, i32, i32, i32, fpst) DEF_HELPER_3(neon_acge_f64, i64, i64, i64, fpst) DEF_HELPER_3(neon_acgt_f64, i64, i64, i64, fpst) -/* iwmmxt_helper.c */ -DEF_HELPER_2(iwmmxt_maddsq, i64, i64, i64) -DEF_HELPER_2(iwmmxt_madduq, i64, i64, i64) -DEF_HELPER_2(iwmmxt_sadb, i64, i64, i64) -DEF_HELPER_2(iwmmxt_sadw, i64, i64, i64) -DEF_HELPER_2(iwmmxt_mulslw, i64, i64, i64) -DEF_HELPER_2(iwmmxt_mulshw, i64, i64, i64) -DEF_HELPER_2(iwmmxt_mululw, i64, i64, i64) -DEF_HELPER_2(iwmmxt_muluhw, i64, i64, i64) -DEF_HELPER_2(iwmmxt_macsw, i64, i64, i64) -DEF_HELPER_2(iwmmxt_macuw, i64, i64, i64) -DEF_HELPER_1(iwmmxt_setpsr_nz, i32, i64) - -#define DEF_IWMMXT_HELPER_SIZE_ENV(name) \ -DEF_HELPER_3(iwmmxt_##name##b, i64, env, i64, i64) \ -DEF_HELPER_3(iwmmxt_##name##w, i64, env, i64, i64) \ -DEF_HELPER_3(iwmmxt_##name##l, i64, env, i64, i64) \ - -DEF_IWMMXT_HELPER_SIZE_ENV(unpackl) -DEF_IWMMXT_HELPER_SIZE_ENV(unpackh) - -DEF_HELPER_2(iwmmxt_unpacklub, i64, env, i64) -DEF_HELPER_2(iwmmxt_unpackluw, i64, env, i64) -DEF_HELPER_2(iwmmxt_unpacklul, i64, env, i64) -DEF_HELPER_2(iwmmxt_unpackhub, i64, env, i64) -DEF_HELPER_2(iwmmxt_unpackhuw, i64, env, i64) -DEF_HELPER_2(iwmmxt_unpackhul, i64, env, i64) -DEF_HELPER_2(iwmmxt_unpacklsb, i64, env, i64) -DEF_HELPER_2(iwmmxt_unpacklsw, i64, env, i64) -DEF_HELPER_2(iwmmxt_unpacklsl, i64, env, i64) -DEF_HELPER_2(iwmmxt_unpackhsb, i64, env, i64) -DEF_HELPER_2(iwmmxt_unpackhsw, i64, env, i64) -DEF_HELPER_2(iwmmxt_unpackhsl, i64, env, i64) - -DEF_IWMMXT_HELPER_SIZE_ENV(cmpeq) -DEF_IWMMXT_HELPER_SIZE_ENV(cmpgtu) -DEF_IWMMXT_HELPER_SIZE_ENV(cmpgts) - -DEF_IWMMXT_HELPER_SIZE_ENV(mins) -DEF_IWMMXT_HELPER_SIZE_ENV(minu) -DEF_IWMMXT_HELPER_SIZE_ENV(maxs) -DEF_IWMMXT_HELPER_SIZE_ENV(maxu) - -DEF_IWMMXT_HELPER_SIZE_ENV(subn) -DEF_IWMMXT_HELPER_SIZE_ENV(addn) -DEF_IWMMXT_HELPER_SIZE_ENV(subu) -DEF_IWMMXT_HELPER_SIZE_ENV(addu) -DEF_IWMMXT_HELPER_SIZE_ENV(subs) -DEF_IWMMXT_HELPER_SIZE_ENV(adds) - -DEF_HELPER_3(iwmmxt_avgb0, i64, env, i64, i64) -DEF_HELPER_3(iwmmxt_avgb1, i64, env, i64, i64) -DEF_HELPER_3(iwmmxt_avgw0, i64, env, i64, i64) -DEF_HELPER_3(iwmmxt_avgw1, i64, env, i64, i64) - -DEF_HELPER_3(iwmmxt_align, i64, i64, i64, i32) -DEF_HELPER_4(iwmmxt_insr, i64, i64, i32, i32, i32) - -DEF_HELPER_1(iwmmxt_bcstb, i64, i32) -DEF_HELPER_1(iwmmxt_bcstw, i64, i32) -DEF_HELPER_1(iwmmxt_bcstl, i64, i32) - -DEF_HELPER_1(iwmmxt_addcb, i64, i64) -DEF_HELPER_1(iwmmxt_addcw, i64, i64) -DEF_HELPER_1(iwmmxt_addcl, i64, i64) - -DEF_HELPER_1(iwmmxt_msbb, i32, i64) -DEF_HELPER_1(iwmmxt_msbw, i32, i64) -DEF_HELPER_1(iwmmxt_msbl, i32, i64) - -DEF_HELPER_3(iwmmxt_srlw, i64, env, i64, i32) -DEF_HELPER_3(iwmmxt_srll, i64, env, i64, i32) -DEF_HELPER_3(iwmmxt_srlq, i64, env, i64, i32) -DEF_HELPER_3(iwmmxt_sllw, i64, env, i64, i32) -DEF_HELPER_3(iwmmxt_slll, i64, env, i64, i32) -DEF_HELPER_3(iwmmxt_sllq, i64, env, i64, i32) -DEF_HELPER_3(iwmmxt_sraw, i64, env, i64, i32) -DEF_HELPER_3(iwmmxt_sral, i64, env, i64, i32) -DEF_HELPER_3(iwmmxt_sraq, i64, env, i64, i32) -DEF_HELPER_3(iwmmxt_rorw, i64, env, i64, i32) -DEF_HELPER_3(iwmmxt_rorl, i64, env, i64, i32) -DEF_HELPER_3(iwmmxt_rorq, i64, env, i64, i32) -DEF_HELPER_3(iwmmxt_shufh, i64, env, i64, i32) - -DEF_HELPER_3(iwmmxt_packuw, i64, env, i64, i64) -DEF_HELPER_3(iwmmxt_packul, i64, env, i64, i64) -DEF_HELPER_3(iwmmxt_packuq, i64, env, i64, i64) -DEF_HELPER_3(iwmmxt_packsw, i64, env, i64, i64) -DEF_HELPER_3(iwmmxt_packsl, i64, env, i64, i64) -DEF_HELPER_3(iwmmxt_packsq, i64, env, i64, i64) - -DEF_HELPER_3(iwmmxt_muladdsl, i64, i64, i32, i32) -DEF_HELPER_3(iwmmxt_muladdsw, i64, i64, i32, i32) -DEF_HELPER_3(iwmmxt_muladdswl, i64, i64, i32, i32) - DEF_HELPER_FLAGS_2(neon_unzip8, TCG_CALL_NO_RWG, void, ptr, ptr) DEF_HELPER_FLAGS_2(neon_unzip16, TCG_CALL_NO_RWG, void, ptr, ptr) DEF_HELPER_FLAGS_2(neon_qunzip8, TCG_CALL_NO_RWG, void, ptr, ptr) diff --git a/target/arm/tcg/iwmmxt_helper.c b/target/arm/tcg/iwmmxt_helper.c deleted file mode 100644 index ba054b6b4d..0000000000 --- a/target/arm/tcg/iwmmxt_helper.c +++ /dev/null @@ -1,672 +0,0 @@ -/* - * iwMMXt micro operations for XScale. - * - * Copyright (c) 2007 OpenedHand, Ltd. - * Written by Andrzej Zaborowski - * Copyright (c) 2008 CodeSourcery - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * 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 . - */ - -#include "qemu/osdep.h" - -#include "cpu.h" - -#define HELPER_H "tcg/helper.h" -#include "exec/helper-proto.h.inc" - -/* iwMMXt macros extracted from GNU gdb. */ - -/* Set the SIMD wCASF flags for 8, 16, 32 or 64-bit operations. */ -#define SIMD8_SET(v, n, b) ((v != 0) << ((((b) + 1) * 4) + (n))) -#define SIMD16_SET(v, n, h) ((v != 0) << ((((h) + 1) * 8) + (n))) -#define SIMD32_SET(v, n, w) ((v != 0) << ((((w) + 1) * 16) + (n))) -#define SIMD64_SET(v, n) ((v != 0) << (32 + (n))) -/* Flags to pass as "n" above. */ -#define SIMD_NBIT -1 -#define SIMD_ZBIT -2 -#define SIMD_CBIT -3 -#define SIMD_VBIT -4 -/* Various status bit macros. */ -#define NBIT8(x) ((x) & 0x80) -#define NBIT16(x) ((x) & 0x8000) -#define NBIT32(x) ((x) & 0x80000000) -#define NBIT64(x) ((x) & 0x8000000000000000ULL) -#define ZBIT8(x) (((x) & 0xff) == 0) -#define ZBIT16(x) (((x) & 0xffff) == 0) -#define ZBIT32(x) (((x) & 0xffffffff) == 0) -#define ZBIT64(x) (x == 0) -/* Sign extension macros. */ -#define EXTEND8H(a) ((uint16_t) (int8_t) (a)) -#define EXTEND8(a) ((uint32_t) (int8_t) (a)) -#define EXTEND16(a) ((uint32_t) (int16_t) (a)) -#define EXTEND16S(a) ((int32_t) (int16_t) (a)) -#define EXTEND32(a) ((uint64_t) (int32_t) (a)) - -uint64_t HELPER(iwmmxt_maddsq)(uint64_t a, uint64_t b) -{ - a = (( - EXTEND16S((a >> 0) & 0xffff) * EXTEND16S((b >> 0) & 0xffff) + - EXTEND16S((a >> 16) & 0xffff) * EXTEND16S((b >> 16) & 0xffff) - ) & 0xffffffff) | ((uint64_t) ( - EXTEND16S((a >> 32) & 0xffff) * EXTEND16S((b >> 32) & 0xffff) + - EXTEND16S((a >> 48) & 0xffff) * EXTEND16S((b >> 48) & 0xffff) - ) << 32); - return a; -} - -uint64_t HELPER(iwmmxt_madduq)(uint64_t a, uint64_t b) -{ - a = (( - ((a >> 0) & 0xffff) * ((b >> 0) & 0xffff) + - ((a >> 16) & 0xffff) * ((b >> 16) & 0xffff) - ) & 0xffffffff) | (( - ((a >> 32) & 0xffff) * ((b >> 32) & 0xffff) + - ((a >> 48) & 0xffff) * ((b >> 48) & 0xffff) - ) << 32); - return a; -} - -uint64_t HELPER(iwmmxt_sadb)(uint64_t a, uint64_t b) -{ -#define abs(x) (((x) >= 0) ? x : -x) -#define SADB(SHR) abs((int) ((a >> SHR) & 0xff) - (int) ((b >> SHR) & 0xff)) - return - SADB(0) + SADB(8) + SADB(16) + SADB(24) + - SADB(32) + SADB(40) + SADB(48) + SADB(56); -#undef SADB -} - -uint64_t HELPER(iwmmxt_sadw)(uint64_t a, uint64_t b) -{ -#define SADW(SHR) \ - abs((int) ((a >> SHR) & 0xffff) - (int) ((b >> SHR) & 0xffff)) - return SADW(0) + SADW(16) + SADW(32) + SADW(48); -#undef SADW -} - -uint64_t HELPER(iwmmxt_mulslw)(uint64_t a, uint64_t b) -{ -#define MULS(SHR) ((uint64_t) ((( \ - EXTEND16S((a >> SHR) & 0xffff) * EXTEND16S((b >> SHR) & 0xffff) \ - ) >> 0) & 0xffff) << SHR) - return MULS(0) | MULS(16) | MULS(32) | MULS(48); -#undef MULS -} - -uint64_t HELPER(iwmmxt_mulshw)(uint64_t a, uint64_t b) -{ -#define MULS(SHR) ((uint64_t) ((( \ - EXTEND16S((a >> SHR) & 0xffff) * EXTEND16S((b >> SHR) & 0xffff) \ - ) >> 16) & 0xffff) << SHR) - return MULS(0) | MULS(16) | MULS(32) | MULS(48); -#undef MULS -} - -uint64_t HELPER(iwmmxt_mululw)(uint64_t a, uint64_t b) -{ -#define MULU(SHR) ((uint64_t) ((( \ - ((a >> SHR) & 0xffff) * ((b >> SHR) & 0xffff) \ - ) >> 0) & 0xffff) << SHR) - return MULU(0) | MULU(16) | MULU(32) | MULU(48); -#undef MULU -} - -uint64_t HELPER(iwmmxt_muluhw)(uint64_t a, uint64_t b) -{ -#define MULU(SHR) ((uint64_t) ((( \ - ((a >> SHR) & 0xffff) * ((b >> SHR) & 0xffff) \ - ) >> 16) & 0xffff) << SHR) - return MULU(0) | MULU(16) | MULU(32) | MULU(48); -#undef MULU -} - -uint64_t HELPER(iwmmxt_macsw)(uint64_t a, uint64_t b) -{ -#define MACS(SHR) ( \ - EXTEND16((a >> SHR) & 0xffff) * EXTEND16S((b >> SHR) & 0xffff)) - return (int64_t) (MACS(0) + MACS(16) + MACS(32) + MACS(48)); -#undef MACS -} - -uint64_t HELPER(iwmmxt_macuw)(uint64_t a, uint64_t b) -{ -#define MACU(SHR) ( \ - (uint32_t) ((a >> SHR) & 0xffff) * \ - (uint32_t) ((b >> SHR) & 0xffff)) - return MACU(0) + MACU(16) + MACU(32) + MACU(48); -#undef MACU -} - -#define NZBIT8(x, i) \ - SIMD8_SET(NBIT8((x) & 0xff), SIMD_NBIT, i) | \ - SIMD8_SET(ZBIT8((x) & 0xff), SIMD_ZBIT, i) -#define NZBIT16(x, i) \ - SIMD16_SET(NBIT16((x) & 0xffff), SIMD_NBIT, i) | \ - SIMD16_SET(ZBIT16((x) & 0xffff), SIMD_ZBIT, i) -#define NZBIT32(x, i) \ - SIMD32_SET(NBIT32((x) & 0xffffffff), SIMD_NBIT, i) | \ - SIMD32_SET(ZBIT32((x) & 0xffffffff), SIMD_ZBIT, i) -#define NZBIT64(x) \ - SIMD64_SET(NBIT64(x), SIMD_NBIT) | \ - SIMD64_SET(ZBIT64(x), SIMD_ZBIT) -#define IWMMXT_OP_UNPACK(S, SH0, SH1, SH2, SH3) \ -uint64_t HELPER(glue(iwmmxt_unpack, glue(S, b)))(CPUARMState *env, \ - uint64_t a, uint64_t b) \ -{ \ - a = \ - (((a >> SH0) & 0xff) << 0) | (((b >> SH0) & 0xff) << 8) | \ - (((a >> SH1) & 0xff) << 16) | (((b >> SH1) & 0xff) << 24) | \ - (((a >> SH2) & 0xff) << 32) | (((b >> SH2) & 0xff) << 40) | \ - (((a >> SH3) & 0xff) << 48) | (((b >> SH3) & 0xff) << 56); \ - env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = \ - NZBIT8(a >> 0, 0) | NZBIT8(a >> 8, 1) | \ - NZBIT8(a >> 16, 2) | NZBIT8(a >> 24, 3) | \ - NZBIT8(a >> 32, 4) | NZBIT8(a >> 40, 5) | \ - NZBIT8(a >> 48, 6) | NZBIT8(a >> 56, 7); \ - return a; \ -} \ -uint64_t HELPER(glue(iwmmxt_unpack, glue(S, w)))(CPUARMState *env, \ - uint64_t a, uint64_t b) \ -{ \ - a = \ - (((a >> SH0) & 0xffff) << 0) | \ - (((b >> SH0) & 0xffff) << 16) | \ - (((a >> SH2) & 0xffff) << 32) | \ - (((b >> SH2) & 0xffff) << 48); \ - env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = \ - NZBIT8(a >> 0, 0) | NZBIT8(a >> 16, 1) | \ - NZBIT8(a >> 32, 2) | NZBIT8(a >> 48, 3); \ - return a; \ -} \ -uint64_t HELPER(glue(iwmmxt_unpack, glue(S, l)))(CPUARMState *env, \ - uint64_t a, uint64_t b) \ -{ \ - a = \ - (((a >> SH0) & 0xffffffff) << 0) | \ - (((b >> SH0) & 0xffffffff) << 32); \ - env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = \ - NZBIT32(a >> 0, 0) | NZBIT32(a >> 32, 1); \ - return a; \ -} \ -uint64_t HELPER(glue(iwmmxt_unpack, glue(S, ub)))(CPUARMState *env, \ - uint64_t x) \ -{ \ - x = \ - (((x >> SH0) & 0xff) << 0) | \ - (((x >> SH1) & 0xff) << 16) | \ - (((x >> SH2) & 0xff) << 32) | \ - (((x >> SH3) & 0xff) << 48); \ - env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = \ - NZBIT16(x >> 0, 0) | NZBIT16(x >> 16, 1) | \ - NZBIT16(x >> 32, 2) | NZBIT16(x >> 48, 3); \ - return x; \ -} \ -uint64_t HELPER(glue(iwmmxt_unpack, glue(S, uw)))(CPUARMState *env, \ - uint64_t x) \ -{ \ - x = \ - (((x >> SH0) & 0xffff) << 0) | \ - (((x >> SH2) & 0xffff) << 32); \ - env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = \ - NZBIT32(x >> 0, 0) | NZBIT32(x >> 32, 1); \ - return x; \ -} \ -uint64_t HELPER(glue(iwmmxt_unpack, glue(S, ul)))(CPUARMState *env, \ - uint64_t x) \ -{ \ - x = (((x >> SH0) & 0xffffffff) << 0); \ - env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = NZBIT64(x >> 0); \ - return x; \ -} \ -uint64_t HELPER(glue(iwmmxt_unpack, glue(S, sb)))(CPUARMState *env, \ - uint64_t x) \ -{ \ - x = \ - ((uint64_t) EXTEND8H((x >> SH0) & 0xff) << 0) | \ - ((uint64_t) EXTEND8H((x >> SH1) & 0xff) << 16) | \ - ((uint64_t) EXTEND8H((x >> SH2) & 0xff) << 32) | \ - ((uint64_t) EXTEND8H((x >> SH3) & 0xff) << 48); \ - env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = \ - NZBIT16(x >> 0, 0) | NZBIT16(x >> 16, 1) | \ - NZBIT16(x >> 32, 2) | NZBIT16(x >> 48, 3); \ - return x; \ -} \ -uint64_t HELPER(glue(iwmmxt_unpack, glue(S, sw)))(CPUARMState *env, \ - uint64_t x) \ -{ \ - x = \ - ((uint64_t) EXTEND16((x >> SH0) & 0xffff) << 0) | \ - ((uint64_t) EXTEND16((x >> SH2) & 0xffff) << 32); \ - env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = \ - NZBIT32(x >> 0, 0) | NZBIT32(x >> 32, 1); \ - return x; \ -} \ -uint64_t HELPER(glue(iwmmxt_unpack, glue(S, sl)))(CPUARMState *env, \ - uint64_t x) \ -{ \ - x = EXTEND32((x >> SH0) & 0xffffffff); \ - env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = NZBIT64(x >> 0); \ - return x; \ -} -IWMMXT_OP_UNPACK(l, 0, 8, 16, 24) -IWMMXT_OP_UNPACK(h, 32, 40, 48, 56) - -#define IWMMXT_OP_CMP(SUFF, Tb, Tw, Tl, O) \ -uint64_t HELPER(glue(iwmmxt_, glue(SUFF, b)))(CPUARMState *env, \ - uint64_t a, uint64_t b) \ -{ \ - a = \ - CMP(0, Tb, O, 0xff) | CMP(8, Tb, O, 0xff) | \ - CMP(16, Tb, O, 0xff) | CMP(24, Tb, O, 0xff) | \ - CMP(32, Tb, O, 0xff) | CMP(40, Tb, O, 0xff) | \ - CMP(48, Tb, O, 0xff) | CMP(56, Tb, O, 0xff); \ - env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = \ - NZBIT8(a >> 0, 0) | NZBIT8(a >> 8, 1) | \ - NZBIT8(a >> 16, 2) | NZBIT8(a >> 24, 3) | \ - NZBIT8(a >> 32, 4) | NZBIT8(a >> 40, 5) | \ - NZBIT8(a >> 48, 6) | NZBIT8(a >> 56, 7); \ - return a; \ -} \ -uint64_t HELPER(glue(iwmmxt_, glue(SUFF, w)))(CPUARMState *env, \ - uint64_t a, uint64_t b) \ -{ \ - a = CMP(0, Tw, O, 0xffff) | CMP(16, Tw, O, 0xffff) | \ - CMP(32, Tw, O, 0xffff) | CMP(48, Tw, O, 0xffff); \ - env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = \ - NZBIT16(a >> 0, 0) | NZBIT16(a >> 16, 1) | \ - NZBIT16(a >> 32, 2) | NZBIT16(a >> 48, 3); \ - return a; \ -} \ -uint64_t HELPER(glue(iwmmxt_, glue(SUFF, l)))(CPUARMState *env, \ - uint64_t a, uint64_t b) \ -{ \ - a = CMP(0, Tl, O, 0xffffffff) | \ - CMP(32, Tl, O, 0xffffffff); \ - env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = \ - NZBIT32(a >> 0, 0) | NZBIT32(a >> 32, 1); \ - return a; \ -} -#define CMP(SHR, TYPE, OPER, MASK) ((((TYPE) ((a >> SHR) & MASK) OPER \ - (TYPE) ((b >> SHR) & MASK)) ? (uint64_t) MASK : 0) << SHR) -IWMMXT_OP_CMP(cmpeq, uint8_t, uint16_t, uint32_t, ==) -IWMMXT_OP_CMP(cmpgts, int8_t, int16_t, int32_t, >) -IWMMXT_OP_CMP(cmpgtu, uint8_t, uint16_t, uint32_t, >) -#undef CMP -#define CMP(SHR, TYPE, OPER, MASK) ((((TYPE) ((a >> SHR) & MASK) OPER \ - (TYPE) ((b >> SHR) & MASK)) ? a : b) & ((uint64_t) MASK << SHR)) -IWMMXT_OP_CMP(mins, int8_t, int16_t, int32_t, <) -IWMMXT_OP_CMP(minu, uint8_t, uint16_t, uint32_t, <) -IWMMXT_OP_CMP(maxs, int8_t, int16_t, int32_t, >) -IWMMXT_OP_CMP(maxu, uint8_t, uint16_t, uint32_t, >) -#undef CMP -#define CMP(SHR, TYPE, OPER, MASK) ((uint64_t) (((TYPE) ((a >> SHR) & MASK) \ - OPER (TYPE) ((b >> SHR) & MASK)) & MASK) << SHR) -IWMMXT_OP_CMP(subn, uint8_t, uint16_t, uint32_t, -) -IWMMXT_OP_CMP(addn, uint8_t, uint16_t, uint32_t, +) -#undef CMP -/* TODO Signed- and Unsigned-Saturation */ -#define CMP(SHR, TYPE, OPER, MASK) ((uint64_t) (((TYPE) ((a >> SHR) & MASK) \ - OPER (TYPE) ((b >> SHR) & MASK)) & MASK) << SHR) -IWMMXT_OP_CMP(subu, uint8_t, uint16_t, uint32_t, -) -IWMMXT_OP_CMP(addu, uint8_t, uint16_t, uint32_t, +) -IWMMXT_OP_CMP(subs, int8_t, int16_t, int32_t, -) -IWMMXT_OP_CMP(adds, int8_t, int16_t, int32_t, +) -#undef CMP -#undef IWMMXT_OP_CMP - -#define AVGB(SHR) ((( \ - ((a >> SHR) & 0xff) + ((b >> SHR) & 0xff) + round) >> 1) << SHR) -#define IWMMXT_OP_AVGB(r) \ -uint64_t HELPER(iwmmxt_avgb##r)(CPUARMState *env, uint64_t a, uint64_t b) \ -{ \ - const int round = r; \ - a = AVGB(0) | AVGB(8) | AVGB(16) | AVGB(24) | \ - AVGB(32) | AVGB(40) | AVGB(48) | AVGB(56); \ - env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = \ - SIMD8_SET(ZBIT8((a >> 0) & 0xff), SIMD_ZBIT, 0) | \ - SIMD8_SET(ZBIT8((a >> 8) & 0xff), SIMD_ZBIT, 1) | \ - SIMD8_SET(ZBIT8((a >> 16) & 0xff), SIMD_ZBIT, 2) | \ - SIMD8_SET(ZBIT8((a >> 24) & 0xff), SIMD_ZBIT, 3) | \ - SIMD8_SET(ZBIT8((a >> 32) & 0xff), SIMD_ZBIT, 4) | \ - SIMD8_SET(ZBIT8((a >> 40) & 0xff), SIMD_ZBIT, 5) | \ - SIMD8_SET(ZBIT8((a >> 48) & 0xff), SIMD_ZBIT, 6) | \ - SIMD8_SET(ZBIT8((a >> 56) & 0xff), SIMD_ZBIT, 7); \ - return a; \ -} -IWMMXT_OP_AVGB(0) -IWMMXT_OP_AVGB(1) -#undef IWMMXT_OP_AVGB -#undef AVGB - -#define AVGW(SHR) ((( \ - ((a >> SHR) & 0xffff) + ((b >> SHR) & 0xffff) + round) >> 1) << SHR) -#define IWMMXT_OP_AVGW(r) \ -uint64_t HELPER(iwmmxt_avgw##r)(CPUARMState *env, uint64_t a, uint64_t b) \ -{ \ - const int round = r; \ - a = AVGW(0) | AVGW(16) | AVGW(32) | AVGW(48); \ - env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = \ - SIMD16_SET(ZBIT16((a >> 0) & 0xffff), SIMD_ZBIT, 0) | \ - SIMD16_SET(ZBIT16((a >> 16) & 0xffff), SIMD_ZBIT, 1) | \ - SIMD16_SET(ZBIT16((a >> 32) & 0xffff), SIMD_ZBIT, 2) | \ - SIMD16_SET(ZBIT16((a >> 48) & 0xffff), SIMD_ZBIT, 3); \ - return a; \ -} -IWMMXT_OP_AVGW(0) -IWMMXT_OP_AVGW(1) -#undef IWMMXT_OP_AVGW -#undef AVGW - -uint64_t HELPER(iwmmxt_align)(uint64_t a, uint64_t b, uint32_t n) -{ - a >>= n << 3; - a |= b << (64 - (n << 3)); - return a; -} - -uint64_t HELPER(iwmmxt_insr)(uint64_t x, uint32_t a, uint32_t b, uint32_t n) -{ - x &= ~((uint64_t) b << n); - x |= (uint64_t) (a & b) << n; - return x; -} - -uint32_t HELPER(iwmmxt_setpsr_nz)(uint64_t x) -{ - return SIMD64_SET((x == 0), SIMD_ZBIT) | - SIMD64_SET((x & (1ULL << 63)), SIMD_NBIT); -} - -uint64_t HELPER(iwmmxt_bcstb)(uint32_t arg) -{ - arg &= 0xff; - return - ((uint64_t) arg << 0 ) | ((uint64_t) arg << 8 ) | - ((uint64_t) arg << 16) | ((uint64_t) arg << 24) | - ((uint64_t) arg << 32) | ((uint64_t) arg << 40) | - ((uint64_t) arg << 48) | ((uint64_t) arg << 56); -} - -uint64_t HELPER(iwmmxt_bcstw)(uint32_t arg) -{ - arg &= 0xffff; - return - ((uint64_t) arg << 0 ) | ((uint64_t) arg << 16) | - ((uint64_t) arg << 32) | ((uint64_t) arg << 48); -} - -uint64_t HELPER(iwmmxt_bcstl)(uint32_t arg) -{ - return arg | ((uint64_t) arg << 32); -} - -uint64_t HELPER(iwmmxt_addcb)(uint64_t x) -{ - return - ((x >> 0) & 0xff) + ((x >> 8) & 0xff) + - ((x >> 16) & 0xff) + ((x >> 24) & 0xff) + - ((x >> 32) & 0xff) + ((x >> 40) & 0xff) + - ((x >> 48) & 0xff) + ((x >> 56) & 0xff); -} - -uint64_t HELPER(iwmmxt_addcw)(uint64_t x) -{ - return - ((x >> 0) & 0xffff) + ((x >> 16) & 0xffff) + - ((x >> 32) & 0xffff) + ((x >> 48) & 0xffff); -} - -uint64_t HELPER(iwmmxt_addcl)(uint64_t x) -{ - return (x & 0xffffffff) + (x >> 32); -} - -uint32_t HELPER(iwmmxt_msbb)(uint64_t x) -{ - return - ((x >> 7) & 0x01) | ((x >> 14) & 0x02) | - ((x >> 21) & 0x04) | ((x >> 28) & 0x08) | - ((x >> 35) & 0x10) | ((x >> 42) & 0x20) | - ((x >> 49) & 0x40) | ((x >> 56) & 0x80); -} - -uint32_t HELPER(iwmmxt_msbw)(uint64_t x) -{ - return - ((x >> 15) & 0x01) | ((x >> 30) & 0x02) | - ((x >> 45) & 0x04) | ((x >> 52) & 0x08); -} - -uint32_t HELPER(iwmmxt_msbl)(uint64_t x) -{ - return ((x >> 31) & 0x01) | ((x >> 62) & 0x02); -} - -/* FIXME: Split wCASF setting into a separate op to avoid env use. */ -uint64_t HELPER(iwmmxt_srlw)(CPUARMState *env, uint64_t x, uint32_t n) -{ - x = (((x & (0xffffll << 0)) >> n) & (0xffffll << 0)) | - (((x & (0xffffll << 16)) >> n) & (0xffffll << 16)) | - (((x & (0xffffll << 32)) >> n) & (0xffffll << 32)) | - (((x & (0xffffll << 48)) >> n) & (0xffffll << 48)); - env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = - NZBIT16(x >> 0, 0) | NZBIT16(x >> 16, 1) | - NZBIT16(x >> 32, 2) | NZBIT16(x >> 48, 3); - return x; -} - -uint64_t HELPER(iwmmxt_srll)(CPUARMState *env, uint64_t x, uint32_t n) -{ - x = ((x & (0xffffffffll << 0)) >> n) | - ((x >> n) & (0xffffffffll << 32)); - env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = - NZBIT32(x >> 0, 0) | NZBIT32(x >> 32, 1); - return x; -} - -uint64_t HELPER(iwmmxt_srlq)(CPUARMState *env, uint64_t x, uint32_t n) -{ - x >>= n; - env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = NZBIT64(x); - return x; -} - -uint64_t HELPER(iwmmxt_sllw)(CPUARMState *env, uint64_t x, uint32_t n) -{ - x = (((x & (0xffffll << 0)) << n) & (0xffffll << 0)) | - (((x & (0xffffll << 16)) << n) & (0xffffll << 16)) | - (((x & (0xffffll << 32)) << n) & (0xffffll << 32)) | - (((x & (0xffffll << 48)) << n) & (0xffffll << 48)); - env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = - NZBIT16(x >> 0, 0) | NZBIT16(x >> 16, 1) | - NZBIT16(x >> 32, 2) | NZBIT16(x >> 48, 3); - return x; -} - -uint64_t HELPER(iwmmxt_slll)(CPUARMState *env, uint64_t x, uint32_t n) -{ - x = ((x << n) & (0xffffffffll << 0)) | - ((x & (0xffffffffll << 32)) << n); - env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = - NZBIT32(x >> 0, 0) | NZBIT32(x >> 32, 1); - return x; -} - -uint64_t HELPER(iwmmxt_sllq)(CPUARMState *env, uint64_t x, uint32_t n) -{ - x <<= n; - env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = NZBIT64(x); - return x; -} - -uint64_t HELPER(iwmmxt_sraw)(CPUARMState *env, uint64_t x, uint32_t n) -{ - x = ((uint64_t) ((EXTEND16(x >> 0) >> n) & 0xffff) << 0) | - ((uint64_t) ((EXTEND16(x >> 16) >> n) & 0xffff) << 16) | - ((uint64_t) ((EXTEND16(x >> 32) >> n) & 0xffff) << 32) | - ((uint64_t) ((EXTEND16(x >> 48) >> n) & 0xffff) << 48); - env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = - NZBIT16(x >> 0, 0) | NZBIT16(x >> 16, 1) | - NZBIT16(x >> 32, 2) | NZBIT16(x >> 48, 3); - return x; -} - -uint64_t HELPER(iwmmxt_sral)(CPUARMState *env, uint64_t x, uint32_t n) -{ - x = (((EXTEND32(x >> 0) >> n) & 0xffffffff) << 0) | - (((EXTEND32(x >> 32) >> n) & 0xffffffff) << 32); - env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = - NZBIT32(x >> 0, 0) | NZBIT32(x >> 32, 1); - return x; -} - -uint64_t HELPER(iwmmxt_sraq)(CPUARMState *env, uint64_t x, uint32_t n) -{ - x = (int64_t) x >> n; - env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = NZBIT64(x); - return x; -} - -uint64_t HELPER(iwmmxt_rorw)(CPUARMState *env, uint64_t x, uint32_t n) -{ - x = ((((x & (0xffffll << 0)) >> n) | - ((x & (0xffffll << 0)) << (16 - n))) & (0xffffll << 0)) | - ((((x & (0xffffll << 16)) >> n) | - ((x & (0xffffll << 16)) << (16 - n))) & (0xffffll << 16)) | - ((((x & (0xffffll << 32)) >> n) | - ((x & (0xffffll << 32)) << (16 - n))) & (0xffffll << 32)) | - ((((x & (0xffffll << 48)) >> n) | - ((x & (0xffffll << 48)) << (16 - n))) & (0xffffll << 48)); - env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = - NZBIT16(x >> 0, 0) | NZBIT16(x >> 16, 1) | - NZBIT16(x >> 32, 2) | NZBIT16(x >> 48, 3); - return x; -} - -uint64_t HELPER(iwmmxt_rorl)(CPUARMState *env, uint64_t x, uint32_t n) -{ - x = ((x & (0xffffffffll << 0)) >> n) | - ((x >> n) & (0xffffffffll << 32)) | - ((x << (32 - n)) & (0xffffffffll << 0)) | - ((x & (0xffffffffll << 32)) << (32 - n)); - env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = - NZBIT32(x >> 0, 0) | NZBIT32(x >> 32, 1); - return x; -} - -uint64_t HELPER(iwmmxt_rorq)(CPUARMState *env, uint64_t x, uint32_t n) -{ - x = ror64(x, n); - env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = NZBIT64(x); - return x; -} - -uint64_t HELPER(iwmmxt_shufh)(CPUARMState *env, uint64_t x, uint32_t n) -{ - x = (((x >> ((n << 4) & 0x30)) & 0xffff) << 0) | - (((x >> ((n << 2) & 0x30)) & 0xffff) << 16) | - (((x >> ((n << 0) & 0x30)) & 0xffff) << 32) | - (((x >> ((n >> 2) & 0x30)) & 0xffff) << 48); - env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = - NZBIT16(x >> 0, 0) | NZBIT16(x >> 16, 1) | - NZBIT16(x >> 32, 2) | NZBIT16(x >> 48, 3); - return x; -} - -/* TODO: Unsigned-Saturation */ -uint64_t HELPER(iwmmxt_packuw)(CPUARMState *env, uint64_t a, uint64_t b) -{ - a = (((a >> 0) & 0xff) << 0) | (((a >> 16) & 0xff) << 8) | - (((a >> 32) & 0xff) << 16) | (((a >> 48) & 0xff) << 24) | - (((b >> 0) & 0xff) << 32) | (((b >> 16) & 0xff) << 40) | - (((b >> 32) & 0xff) << 48) | (((b >> 48) & 0xff) << 56); - env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = - NZBIT8(a >> 0, 0) | NZBIT8(a >> 8, 1) | - NZBIT8(a >> 16, 2) | NZBIT8(a >> 24, 3) | - NZBIT8(a >> 32, 4) | NZBIT8(a >> 40, 5) | - NZBIT8(a >> 48, 6) | NZBIT8(a >> 56, 7); - return a; -} - -uint64_t HELPER(iwmmxt_packul)(CPUARMState *env, uint64_t a, uint64_t b) -{ - a = (((a >> 0) & 0xffff) << 0) | (((a >> 32) & 0xffff) << 16) | - (((b >> 0) & 0xffff) << 32) | (((b >> 32) & 0xffff) << 48); - env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = - NZBIT16(a >> 0, 0) | NZBIT16(a >> 16, 1) | - NZBIT16(a >> 32, 2) | NZBIT16(a >> 48, 3); - return a; -} - -uint64_t HELPER(iwmmxt_packuq)(CPUARMState *env, uint64_t a, uint64_t b) -{ - a = (a & 0xffffffff) | ((b & 0xffffffff) << 32); - env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = - NZBIT32(a >> 0, 0) | NZBIT32(a >> 32, 1); - return a; -} - -/* TODO: Signed-Saturation */ -uint64_t HELPER(iwmmxt_packsw)(CPUARMState *env, uint64_t a, uint64_t b) -{ - a = (((a >> 0) & 0xff) << 0) | (((a >> 16) & 0xff) << 8) | - (((a >> 32) & 0xff) << 16) | (((a >> 48) & 0xff) << 24) | - (((b >> 0) & 0xff) << 32) | (((b >> 16) & 0xff) << 40) | - (((b >> 32) & 0xff) << 48) | (((b >> 48) & 0xff) << 56); - env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = - NZBIT8(a >> 0, 0) | NZBIT8(a >> 8, 1) | - NZBIT8(a >> 16, 2) | NZBIT8(a >> 24, 3) | - NZBIT8(a >> 32, 4) | NZBIT8(a >> 40, 5) | - NZBIT8(a >> 48, 6) | NZBIT8(a >> 56, 7); - return a; -} - -uint64_t HELPER(iwmmxt_packsl)(CPUARMState *env, uint64_t a, uint64_t b) -{ - a = (((a >> 0) & 0xffff) << 0) | (((a >> 32) & 0xffff) << 16) | - (((b >> 0) & 0xffff) << 32) | (((b >> 32) & 0xffff) << 48); - env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = - NZBIT16(a >> 0, 0) | NZBIT16(a >> 16, 1) | - NZBIT16(a >> 32, 2) | NZBIT16(a >> 48, 3); - return a; -} - -uint64_t HELPER(iwmmxt_packsq)(CPUARMState *env, uint64_t a, uint64_t b) -{ - a = (a & 0xffffffff) | ((b & 0xffffffff) << 32); - env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = - NZBIT32(a >> 0, 0) | NZBIT32(a >> 32, 1); - return a; -} - -uint64_t HELPER(iwmmxt_muladdsl)(uint64_t c, uint32_t a, uint32_t b) -{ - return c + ((int32_t) EXTEND32(a) * (int32_t) EXTEND32(b)); -} - -uint64_t HELPER(iwmmxt_muladdsw)(uint64_t c, uint32_t a, uint32_t b) -{ - c += EXTEND32(EXTEND16S((a >> 0) & 0xffff) * - EXTEND16S((b >> 0) & 0xffff)); - c += EXTEND32(EXTEND16S((a >> 16) & 0xffff) * - EXTEND16S((b >> 16) & 0xffff)); - return c; -} - -uint64_t HELPER(iwmmxt_muladdswl)(uint64_t c, uint32_t a, uint32_t b) -{ - return c + (EXTEND32(EXTEND16S(a & 0xffff) * - EXTEND16S(b & 0xffff))); -} diff --git a/target/arm/tcg/meson.build b/target/arm/tcg/meson.build index 895facdc30..1b115656c4 100644 --- a/target/arm/tcg/meson.build +++ b/target/arm/tcg/meson.build @@ -66,7 +66,6 @@ arm_common_ss.add(files( arm_common_system_ss.add(files( 'cpregs-at.c', 'hflags.c', - 'iwmmxt_helper.c', 'neon_helper.c', 'tlb_helper.c', 'tlb-insns.c', @@ -74,7 +73,6 @@ arm_common_system_ss.add(files( )) arm_user_ss.add(files( 'hflags.c', - 'iwmmxt_helper.c', 'neon_helper.c', 'tlb_helper.c', 'vfp_helper.c', From 4f0fa4bfd98f3ccdcdeed6f43a2340084a6e0a92 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 28 Aug 2025 15:04:21 +0100 Subject: [PATCH 0419/2396] target/arm: Drop ARM_FEATURE_XSCALE handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We have now removed all the CPU types which had the Intel XScale extensions indicated via ARM_FEATURE_XSCALE, so this feature bit is never set. Remove all the code that can only be reached when using this flag. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-id: 20250828140422.3271703-5-peter.maydell@linaro.org --- target/arm/cpu.c | 13 --------- target/arm/cpu.h | 3 --- target/arm/helper.c | 54 -------------------------------------- target/arm/ptw.c | 7 +++-- target/arm/tcg/op_helper.c | 6 ----- 5 files changed, 3 insertions(+), 80 deletions(-) diff --git a/target/arm/cpu.c b/target/arm/cpu.c index d0f6fcdfce..9781055bdc 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -349,11 +349,6 @@ static void arm_cpu_reset_hold(Object *obj, ResetType type) env->uncached_cpsr = ARM_CPU_MODE_USR; /* For user mode we must enable access to coprocessors */ env->vfp.xregs[ARM_VFP_FPEXC] = 1 << 30; - if (arm_feature(env, ARM_FEATURE_IWMMXT)) { - env->cp15.c15_cpar = 3; - } else if (arm_feature(env, ARM_FEATURE_XSCALE)) { - env->cp15.c15_cpar = 1; - } #else /* @@ -2259,14 +2254,6 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) } - /* - * We rely on no XScale CPU having VFP so we can use the same bits in the - * TB flags field for VECSTRIDE and XSCALE_CPAR. - */ - assert(arm_feature(env, ARM_FEATURE_AARCH64) || - !cpu_isar_feature(aa32_vfp_simd, cpu) || - !arm_feature(env, ARM_FEATURE_XSCALE)); - #ifndef CONFIG_USER_ONLY { int pagebits; diff --git a/target/arm/cpu.h b/target/arm/cpu.h index f56fa6df8d..92fcb96671 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -341,7 +341,6 @@ typedef struct CPUArchState { uint64_t vsctlr; /* Virtualization System control register. */ uint64_t cpacr_el1; /* Architectural feature access control register */ uint64_t cptr_el[4]; /* ARMv8 feature trap registers */ - uint32_t c1_xscaleauxcr; /* XScale auxiliary control register. */ uint64_t sder; /* Secure debug enable register. */ uint32_t nsacr; /* Non-secure access control register. */ union { /* MMU translation table base 0. */ @@ -513,7 +512,6 @@ typedef struct CPUArchState { uint64_t cntvoff_el2; /* Counter Virtual Offset register */ uint64_t cntpoff_el2; /* Counter Physical Offset register */ ARMGenericTimer c14_timer[NUM_GTIMERS]; - uint32_t c15_cpar; /* XScale Coprocessor Access Register */ uint32_t c15_ticonfig; /* TI925T configuration byte. */ uint32_t c15_i_max; /* Maximum D-cache dirty line index. */ uint32_t c15_i_min; /* Minimum D-cache dirty line index. */ @@ -2444,7 +2442,6 @@ QEMU_BUILD_BUG_ON(ARRAY_SIZE(((ARMCPU *)0)->ccsidr) <= R_V7M_CSSELR_INDEX_MASK); */ enum arm_features { ARM_FEATURE_AUXCR, /* ARM1026 Auxiliary control register. */ - ARM_FEATURE_XSCALE, /* Intel XScale extensions. */ ARM_FEATURE_IWMMXT, /* Intel iwMMXt extension. */ ARM_FEATURE_V6, ARM_FEATURE_V6K, diff --git a/target/arm/helper.c b/target/arm/helper.c index b641229ba0..fa8dfac299 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -2923,39 +2923,6 @@ static const ARMCPRegInfo omap_cp_reginfo[] = { .type = ARM_CP_CONST | ARM_CP_OVERRIDE, .resetvalue = 0 }, }; -static void xscale_cpar_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - env->cp15.c15_cpar = value & 0x3fff; -} - -static const ARMCPRegInfo xscale_cp_reginfo[] = { - { .name = "XSCALE_CPAR", - .cp = 15, .crn = 15, .crm = 1, .opc1 = 0, .opc2 = 0, .access = PL1_RW, - .fieldoffset = offsetof(CPUARMState, cp15.c15_cpar), .resetvalue = 0, - .writefn = xscale_cpar_write, }, - { .name = "XSCALE_AUXCR", - .cp = 15, .crn = 1, .crm = 0, .opc1 = 0, .opc2 = 1, .access = PL1_RW, - .fieldoffset = offsetof(CPUARMState, cp15.c1_xscaleauxcr), - .resetvalue = 0, }, - /* - * XScale specific cache-lockdown: since we have no cache we NOP these - * and hope the guest does not really rely on cache behaviour. - */ - { .name = "XSCALE_LOCK_ICACHE_LINE", - .cp = 15, .opc1 = 0, .crn = 9, .crm = 1, .opc2 = 0, - .access = PL1_W, .type = ARM_CP_NOP }, - { .name = "XSCALE_UNLOCK_ICACHE", - .cp = 15, .opc1 = 0, .crn = 9, .crm = 1, .opc2 = 1, - .access = PL1_W, .type = ARM_CP_NOP }, - { .name = "XSCALE_DCACHE_LOCK", - .cp = 15, .opc1 = 0, .crn = 9, .crm = 2, .opc2 = 0, - .access = PL1_RW, .type = ARM_CP_NOP }, - { .name = "XSCALE_UNLOCK_DCACHE", - .cp = 15, .opc1 = 0, .crn = 9, .crm = 2, .opc2 = 1, - .access = PL1_W, .type = ARM_CP_NOP }, -}; - static const ARMCPRegInfo dummy_c15_cp_reginfo[] = { /* * RAZ/WI the whole crn=15 space, when we don't have a more specific @@ -3346,16 +3313,6 @@ static void sctlr_write(CPUARMState *env, const ARMCPRegInfo *ri, /* This may enable/disable the MMU, so do a TLB flush. */ tlb_flush(CPU(cpu)); - - if (tcg_enabled() && ri->type & ARM_CP_SUPPRESS_TB_END) { - /* - * Normally we would always end the TB on an SCTLR write; see the - * comment in ARMCPRegInfo sctlr initialization below for why Xscale - * is special. Setting ARM_CP_SUPPRESS_TB_END also stops the rebuild - * of hflags from the translator, so do it here. - */ - arm_rebuild_hflags(env); - } } static void mdcr_el3_write(CPUARMState *env, const ARMCPRegInfo *ri, @@ -6894,9 +6851,6 @@ void register_cp_regs_for_features(ARMCPU *cpu) if (arm_feature(env, ARM_FEATURE_STRONGARM)) { define_arm_cp_regs(cpu, strongarm_cp_reginfo); } - if (arm_feature(env, ARM_FEATURE_XSCALE)) { - define_arm_cp_regs(cpu, xscale_cp_reginfo); - } if (arm_feature(env, ARM_FEATURE_DUMMY_C15_REGS)) { define_arm_cp_regs(cpu, dummy_c15_cp_reginfo); } @@ -7245,14 +7199,6 @@ void register_cp_regs_for_features(ARMCPU *cpu) .writefn = sctlr_write, .resetvalue = cpu->reset_sctlr, .raw_writefn = raw_write, }; - if (arm_feature(env, ARM_FEATURE_XSCALE)) { - /* - * Normally we would always end the TB on an SCTLR write, but Linux - * arch/arm/mach-pxa/sleep.S expects two instructions following - * an MMU enable to execute from cache. Imitate this behaviour. - */ - sctlr.type |= ARM_CP_SUPPRESS_TB_END; - } define_one_arm_cp_reg(cpu, &sctlr); if (arm_feature(env, ARM_FEATURE_PMSA) && diff --git a/target/arm/ptw.c b/target/arm/ptw.c index 089eeff845..6344971fa6 100644 --- a/target/arm/ptw.c +++ b/target/arm/ptw.c @@ -1074,11 +1074,10 @@ static bool get_phys_addr_v5(CPUARMState *env, S1Translate *ptw, ap = (desc >> (4 + ((address >> 9) & 6))) & 3; result->f.lg_page_size = 12; break; - case 3: /* 1k page, or ARMv6/XScale "extended small (4k) page" */ + case 3: /* 1k page, or ARMv6 "extended small (4k) page" */ if (type == 1) { - /* ARMv6/XScale extended small page format */ - if (arm_feature(env, ARM_FEATURE_XSCALE) - || arm_feature(env, ARM_FEATURE_V6)) { + /* ARMv6 extended small page format */ + if (arm_feature(env, ARM_FEATURE_V6)) { phys_addr = (desc & 0xfffff000) | (address & 0xfff); result->f.lg_page_size = 12; } else { diff --git a/target/arm/tcg/op_helper.c b/target/arm/tcg/op_helper.c index 575e566280..5373e0e998 100644 --- a/target/arm/tcg/op_helper.c +++ b/target/arm/tcg/op_helper.c @@ -768,12 +768,6 @@ const void *HELPER(access_check_cp_reg)(CPUARMState *env, uint32_t key, assert(ri != NULL); - if (arm_feature(env, ARM_FEATURE_XSCALE) && ri->cp < 14 - && extract32(env->cp15.c15_cpar, ri->cp, 1) == 0) { - res = CP_ACCESS_UNDEFINED; - goto fail; - } - if (ri->accessfn) { res = ri->accessfn(env, ri, isread); } From effe47ff4840860811e2e1cccfd60cb1cea8e459 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 28 Aug 2025 15:04:22 +0100 Subject: [PATCH 0420/2396] target/arm: Drop ARM_FEATURE_IWMMXT handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We have now removed all the CPU types which had the Intel XScale extensions indicated via ARM_FEATURE_IWMMXT, so this feature bit is never set. Remove all the code that can only be reached when using this flag. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-id: 20250828140422.3271703-6-peter.maydell@linaro.org --- bsd-user/arm/target_arch_elf.h | 1 - linux-user/arm/elfload.c | 1 - linux-user/arm/signal.c | 67 ---------------------------------- target/arm/cpu.c | 8 ---- target/arm/cpu.h | 19 ---------- target/arm/machine.c | 21 ----------- 6 files changed, 117 deletions(-) diff --git a/bsd-user/arm/target_arch_elf.h b/bsd-user/arm/target_arch_elf.h index b1c0fd2b32..b54bf5fbc6 100644 --- a/bsd-user/arm/target_arch_elf.h +++ b/bsd-user/arm/target_arch_elf.h @@ -86,7 +86,6 @@ static uint32_t get_elf_hwcap(void) /* probe for the extra features */ /* EDSP is in v5TE and above */ GET_FEATURE(ARM_FEATURE_V5, ARM_HWCAP_ARM_EDSP); - GET_FEATURE(ARM_FEATURE_IWMMXT, ARM_HWCAP_ARM_IWMMXT); GET_FEATURE(ARM_FEATURE_THUMB2EE, ARM_HWCAP_ARM_THUMBEE); GET_FEATURE(ARM_FEATURE_NEON, ARM_HWCAP_ARM_NEON); GET_FEATURE(ARM_FEATURE_V6K, ARM_HWCAP_ARM_TLS); diff --git a/linux-user/arm/elfload.c b/linux-user/arm/elfload.c index 308ed23fcb..b1a4db4466 100644 --- a/linux-user/arm/elfload.c +++ b/linux-user/arm/elfload.c @@ -76,7 +76,6 @@ abi_ulong get_elf_hwcap(CPUState *cs) /* EDSP is in v5TE and above, but all our v5 CPUs are v5TE */ GET_FEATURE(ARM_FEATURE_V5, ARM_HWCAP_ARM_EDSP); - GET_FEATURE(ARM_FEATURE_IWMMXT, ARM_HWCAP_ARM_IWMMXT); GET_FEATURE(ARM_FEATURE_THUMB2EE, ARM_HWCAP_ARM_THUMBEE); GET_FEATURE(ARM_FEATURE_NEON, ARM_HWCAP_ARM_NEON); GET_FEATURE(ARM_FEATURE_V6K, ARM_HWCAP_ARM_TLS); diff --git a/linux-user/arm/signal.c b/linux-user/arm/signal.c index 8db1c4b233..3b387cd6d7 100644 --- a/linux-user/arm/signal.c +++ b/linux-user/arm/signal.c @@ -76,21 +76,7 @@ struct target_vfp_sigframe { struct target_user_vfp_exc ufp_exc; } __attribute__((__aligned__(8))); -struct target_iwmmxt_sigframe { - abi_ulong magic; - abi_ulong size; - uint64_t regs[16]; - /* Note that not all the coprocessor control registers are stored here */ - uint32_t wcssf; - uint32_t wcasf; - uint32_t wcgr0; - uint32_t wcgr1; - uint32_t wcgr2; - uint32_t wcgr3; -} __attribute__((__aligned__(8))); - #define TARGET_VFP_MAGIC 0x56465001 -#define TARGET_IWMMXT_MAGIC 0x12ef842a struct sigframe { @@ -267,25 +253,6 @@ static abi_ulong *setup_sigframe_vfp(abi_ulong *regspace, CPUARMState *env) return (abi_ulong*)(vfpframe+1); } -static abi_ulong *setup_sigframe_iwmmxt(abi_ulong *regspace, CPUARMState *env) -{ - int i; - struct target_iwmmxt_sigframe *iwmmxtframe; - iwmmxtframe = (struct target_iwmmxt_sigframe *)regspace; - __put_user(TARGET_IWMMXT_MAGIC, &iwmmxtframe->magic); - __put_user(sizeof(*iwmmxtframe), &iwmmxtframe->size); - for (i = 0; i < 16; i++) { - __put_user(env->iwmmxt.regs[i], &iwmmxtframe->regs[i]); - } - __put_user(env->vfp.xregs[ARM_IWMMXT_wCSSF], &iwmmxtframe->wcssf); - __put_user(env->vfp.xregs[ARM_IWMMXT_wCASF], &iwmmxtframe->wcssf); - __put_user(env->vfp.xregs[ARM_IWMMXT_wCGR0], &iwmmxtframe->wcgr0); - __put_user(env->vfp.xregs[ARM_IWMMXT_wCGR1], &iwmmxtframe->wcgr1); - __put_user(env->vfp.xregs[ARM_IWMMXT_wCGR2], &iwmmxtframe->wcgr2); - __put_user(env->vfp.xregs[ARM_IWMMXT_wCGR3], &iwmmxtframe->wcgr3); - return (abi_ulong*)(iwmmxtframe+1); -} - static void setup_sigframe(struct target_ucontext *uc, target_sigset_t *set, CPUARMState *env) { @@ -306,9 +273,6 @@ static void setup_sigframe(struct target_ucontext *uc, if (cpu_isar_feature(aa32_vfp_simd, env_archcpu(env))) { regspace = setup_sigframe_vfp(regspace, env); } - if (arm_feature(env, ARM_FEATURE_IWMMXT)) { - regspace = setup_sigframe_iwmmxt(regspace, env); - } /* Write terminating magic word */ __put_user(0, regspace); @@ -435,31 +399,6 @@ static abi_ulong *restore_sigframe_vfp(CPUARMState *env, abi_ulong *regspace) return (abi_ulong*)(vfpframe + 1); } -static abi_ulong *restore_sigframe_iwmmxt(CPUARMState *env, - abi_ulong *regspace) -{ - int i; - abi_ulong magic, sz; - struct target_iwmmxt_sigframe *iwmmxtframe; - iwmmxtframe = (struct target_iwmmxt_sigframe *)regspace; - - __get_user(magic, &iwmmxtframe->magic); - __get_user(sz, &iwmmxtframe->size); - if (magic != TARGET_IWMMXT_MAGIC || sz != sizeof(*iwmmxtframe)) { - return 0; - } - for (i = 0; i < 16; i++) { - __get_user(env->iwmmxt.regs[i], &iwmmxtframe->regs[i]); - } - __get_user(env->vfp.xregs[ARM_IWMMXT_wCSSF], &iwmmxtframe->wcssf); - __get_user(env->vfp.xregs[ARM_IWMMXT_wCASF], &iwmmxtframe->wcssf); - __get_user(env->vfp.xregs[ARM_IWMMXT_wCGR0], &iwmmxtframe->wcgr0); - __get_user(env->vfp.xregs[ARM_IWMMXT_wCGR1], &iwmmxtframe->wcgr1); - __get_user(env->vfp.xregs[ARM_IWMMXT_wCGR2], &iwmmxtframe->wcgr2); - __get_user(env->vfp.xregs[ARM_IWMMXT_wCGR3], &iwmmxtframe->wcgr3); - return (abi_ulong*)(iwmmxtframe + 1); -} - static int do_sigframe_return(CPUARMState *env, target_ulong context_addr, struct target_ucontext *uc) @@ -482,12 +421,6 @@ static int do_sigframe_return(CPUARMState *env, return 1; } } - if (arm_feature(env, ARM_FEATURE_IWMMXT)) { - regspace = restore_sigframe_iwmmxt(env, regspace); - if (!regspace) { - return 1; - } - } target_restore_altstack(&uc->tuc_stack, env); diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 9781055bdc..02e2a31a86 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -247,10 +247,6 @@ static void arm_cpu_reset_hold(Object *obj, ResetType type) cpu->power_state = cs->start_powered_off ? PSCI_OFF : PSCI_ON; - if (arm_feature(env, ARM_FEATURE_IWMMXT)) { - env->iwmmxt.cregs[ARM_IWMMXT_wCID] = 0x69051000 | 'Q'; - } - if (arm_feature(env, ARM_FEATURE_AARCH64)) { /* 64 bit CPUs always start in 64 bit mode */ env->aarch64 = true; @@ -2610,14 +2606,10 @@ static const Property arm_cpu_properties[] = { static const gchar *arm_gdb_arch_name(CPUState *cs) { ARMCPU *cpu = ARM_CPU(cs); - CPUARMState *env = &cpu->env; if (arm_gdbstub_is_aarch64(cpu)) { return "aarch64"; } - if (arm_feature(env, ARM_FEATURE_IWMMXT)) { - return "iwmmxt"; - } return "arm"; } diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 92fcb96671..6644043f4c 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -697,14 +697,6 @@ typedef struct CPUArchState { */ uint64_t exclusive_high; - /* iwMMXt coprocessor state. */ - struct { - uint64_t regs[16]; - uint64_t val; - - uint32_t cregs[16]; - } iwmmxt; - struct { ARMPACKey apia; ARMPACKey apib; @@ -1863,16 +1855,6 @@ enum arm_cpu_mode { /* QEMU-internal value meaning "FPSCR, but we care only about NZCV" */ #define QEMU_VFP_FPSCR_NZCV 0xffff -/* iwMMXt coprocessor control registers. */ -#define ARM_IWMMXT_wCID 0 -#define ARM_IWMMXT_wCon 1 -#define ARM_IWMMXT_wCSSF 2 -#define ARM_IWMMXT_wCASF 3 -#define ARM_IWMMXT_wCGR0 8 -#define ARM_IWMMXT_wCGR1 9 -#define ARM_IWMMXT_wCGR2 10 -#define ARM_IWMMXT_wCGR3 11 - /* V7M CCR bits */ FIELD(V7M_CCR, NONBASETHRDENA, 0, 1) FIELD(V7M_CCR, USERSETMPEND, 1, 1) @@ -2442,7 +2424,6 @@ QEMU_BUILD_BUG_ON(ARRAY_SIZE(((ARMCPU *)0)->ccsidr) <= R_V7M_CSSELR_INDEX_MASK); */ enum arm_features { ARM_FEATURE_AUXCR, /* ARM1026 Auxiliary control register. */ - ARM_FEATURE_IWMMXT, /* Intel iwMMXt extension. */ ARM_FEATURE_V6, ARM_FEATURE_V6K, ARM_FEATURE_V7, diff --git a/target/arm/machine.c b/target/arm/machine.c index 6986915bee..6666a0c50c 100644 --- a/target/arm/machine.c +++ b/target/arm/machine.c @@ -221,26 +221,6 @@ static const VMStateDescription vmstate_vfp = { } }; -static bool iwmmxt_needed(void *opaque) -{ - ARMCPU *cpu = opaque; - CPUARMState *env = &cpu->env; - - return arm_feature(env, ARM_FEATURE_IWMMXT); -} - -static const VMStateDescription vmstate_iwmmxt = { - .name = "cpu/iwmmxt", - .version_id = 1, - .minimum_version_id = 1, - .needed = iwmmxt_needed, - .fields = (const VMStateField[]) { - VMSTATE_UINT64_ARRAY(env.iwmmxt.regs, ARMCPU, 16), - VMSTATE_UINT32_ARRAY(env.iwmmxt.cregs, ARMCPU, 16), - VMSTATE_END_OF_LIST() - } -}; - /* The expression ARM_MAX_VQ - 2 is 0 for pure AArch32 build, * and ARMPredicateReg is actively empty. This triggers errors * in the expansion of the VMSTATE macros. @@ -1102,7 +1082,6 @@ const VMStateDescription vmstate_arm_cpu = { }, .subsections = (const VMStateDescription * const []) { &vmstate_vfp, - &vmstate_iwmmxt, &vmstate_m, &vmstate_thumb2ee, /* pmsav7_rnr must come before pmsav7 so that we have the From 116c2c21d521eb31fffb44eb74d918975c9a5658 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 28 Aug 2025 17:27:00 +0100 Subject: [PATCH 0421/2396] system: drop the -old-param option MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We deprecated the command line option -old-param for the 10.0 release, which allows us to drop it in 10.2. This option was used to boot Arm targets with a very old boot protocol using the 'param_struct' ABI. We only ever needed this on a handful of board types which have all now been removed from QEMU. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Thomas Huth Reviewed-by: Manos Pitsidianakis Reviewed-by: Alex Bennée Message-id: 20250828162700.3308812-1-peter.maydell@linaro.org --- docs/about/deprecated.rst | 13 ------ docs/about/removed-features.rst | 12 +++++ hw/arm/boot.c | 81 +-------------------------------- include/system/system.h | 1 - qemu-options.hx | 7 --- system/globals.c | 1 - system/vl.c | 4 -- 7 files changed, 13 insertions(+), 106 deletions(-) diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst index f031414769..03f7cabf73 100644 --- a/docs/about/deprecated.rst +++ b/docs/about/deprecated.rst @@ -68,19 +68,6 @@ configurations (e.g. -smp drawers=1,books=1,clusters=1 for x86 PC machine) is marked deprecated since 9.0, users have to ensure that all the topology members described with -smp are supported by the target machine. -``-old-param`` option for booting Arm kernels via param_struct (since 10.0) -''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' - -The ``-old-param`` command line option is specific to Arm targets: -it is used when directly booting a guest kernel to pass it the -command line and other information via the old ``param_struct`` ABI, -rather than the newer ATAGS or DTB mechanisms. This option was only -ever needed to support ancient kernels on some old board types -like the ``akita`` or ``terrier``; it has been deprecated in the -kernel since 2001. None of the board types QEMU supports need -``param_struct`` support, so this option has been deprecated and will -be removed in a future QEMU version. - QEMU Machine Protocol (QMP) commands ------------------------------------ diff --git a/docs/about/removed-features.rst b/docs/about/removed-features.rst index 65fd564d22..07ca4079d4 100644 --- a/docs/about/removed-features.rst +++ b/docs/about/removed-features.rst @@ -560,6 +560,18 @@ the options along with the machine models they were intended for. Use ``-run-with user=..`` instead. +``-old-param`` option for booting Arm kernels via param_struct (removed in 10.2) +'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +The ``-old-param`` command line option was specific to Arm targets: +it was used when directly booting a guest kernel to pass it the +command line and other information via the old ``param_struct`` ABI, +rather than the newer ATAGS or DTB mechanisms. This option was only +ever needed to support ancient kernels on some old board types +like the ``akita`` or ``terrier``; it has been deprecated in the +kernel since 2001. None of the board types QEMU supports need +``param_struct`` support, so this option has been removed. + User-mode emulator command line arguments ----------------------------------------- diff --git a/hw/arm/boot.c b/hw/arm/boot.c index d0840308f5..e77d8679d8 100644 --- a/hw/arm/boot.c +++ b/hw/arm/boot.c @@ -337,81 +337,6 @@ static void set_kernel_args(const struct arm_boot_info *info, AddressSpace *as) WRITE_WORD(p, 0); } -static void set_kernel_args_old(const struct arm_boot_info *info, - AddressSpace *as) -{ - hwaddr p; - const char *s; - int initrd_size = info->initrd_size; - hwaddr base = info->loader_start; - - /* see linux/include/asm-arm/setup.h */ - p = base + KERNEL_ARGS_ADDR; - /* page_size */ - WRITE_WORD(p, 4096); - /* nr_pages */ - WRITE_WORD(p, info->ram_size / 4096); - /* ramdisk_size */ - WRITE_WORD(p, 0); -#define FLAG_READONLY 1 -#define FLAG_RDLOAD 4 -#define FLAG_RDPROMPT 8 - /* flags */ - WRITE_WORD(p, FLAG_READONLY | FLAG_RDLOAD | FLAG_RDPROMPT); - /* rootdev */ - WRITE_WORD(p, (31 << 8) | 0); /* /dev/mtdblock0 */ - /* video_num_cols */ - WRITE_WORD(p, 0); - /* video_num_rows */ - WRITE_WORD(p, 0); - /* video_x */ - WRITE_WORD(p, 0); - /* video_y */ - WRITE_WORD(p, 0); - /* memc_control_reg */ - WRITE_WORD(p, 0); - /* unsigned char sounddefault */ - /* unsigned char adfsdrives */ - /* unsigned char bytes_per_char_h */ - /* unsigned char bytes_per_char_v */ - WRITE_WORD(p, 0); - /* pages_in_bank[4] */ - WRITE_WORD(p, 0); - WRITE_WORD(p, 0); - WRITE_WORD(p, 0); - WRITE_WORD(p, 0); - /* pages_in_vram */ - WRITE_WORD(p, 0); - /* initrd_start */ - if (initrd_size) { - WRITE_WORD(p, info->initrd_start); - } else { - WRITE_WORD(p, 0); - } - /* initrd_size */ - WRITE_WORD(p, initrd_size); - /* rd_start */ - WRITE_WORD(p, 0); - /* system_rev */ - WRITE_WORD(p, 0); - /* system_serial_low */ - WRITE_WORD(p, 0); - /* system_serial_high */ - WRITE_WORD(p, 0); - /* mem_fclk_21285 */ - WRITE_WORD(p, 0); - /* zero unused fields */ - while (p < base + KERNEL_ARGS_ADDR + 256 + 1024) { - WRITE_WORD(p, 0); - } - s = info->kernel_cmdline; - if (s) { - address_space_write(as, p, MEMTXATTRS_UNSPECIFIED, s, strlen(s) + 1); - } else { - WRITE_WORD(p, 0); - } -} - static int fdt_add_memory_node(void *fdt, uint32_t acells, hwaddr mem_base, uint32_t scells, hwaddr mem_len, int numa_node_id) @@ -802,11 +727,7 @@ static void do_cpu_reset(void *opaque) cpu_set_pc(cs, info->loader_start); if (!have_dtb(info)) { - if (old_param) { - set_kernel_args_old(info, as); - } else { - set_kernel_args(info, as); - } + set_kernel_args(info, as); } } else if (info->secondary_cpu_reset_hook) { info->secondary_cpu_reset_hook(cpu, info); diff --git a/include/system/system.h b/include/system/system.h index a7effe7dfd..03a2d0e900 100644 --- a/include/system/system.h +++ b/include/system/system.h @@ -42,7 +42,6 @@ extern int graphic_height; extern int graphic_depth; extern int display_opengl; extern const char *keyboard_layout; -extern int old_param; extern uint8_t *boot_splash_filedata; extern bool enable_cpu_pm; extern QEMUClockType rtc_clock; diff --git a/qemu-options.hx b/qemu-options.hx index ab23f14d21..aa44b0e34a 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -5347,13 +5347,6 @@ SRST specified, the former is passed to semihosting as it always takes precedence. ERST -DEF("old-param", 0, QEMU_OPTION_old_param, - "-old-param old param mode\n", QEMU_ARCH_ARM) -SRST -``-old-param`` - Old param mode (ARM only). -ERST - DEF("sandbox", HAS_ARG, QEMU_OPTION_sandbox, \ "-sandbox on[,obsolete=allow|deny][,elevateprivileges=allow|deny|children]\n" \ " [,spawn=allow|deny][,resourcecontrol=allow|deny]\n" \ diff --git a/system/globals.c b/system/globals.c index 9640c9511e..98f9876d5d 100644 --- a/system/globals.c +++ b/system/globals.c @@ -52,7 +52,6 @@ bool vga_interface_created; Chardev *parallel_hds[MAX_PARALLEL_PORTS]; QEMUOptionRom option_rom[MAX_OPTION_ROMS]; int nb_option_roms; -int old_param; const char *qemu_name; unsigned int nb_prom_envs; const char *prom_envs[MAX_PROM_ENVS]; diff --git a/system/vl.c b/system/vl.c index 3b7057e6c6..00f3694725 100644 --- a/system/vl.c +++ b/system/vl.c @@ -3524,10 +3524,6 @@ void qemu_init(int argc, char **argv) prom_envs[nb_prom_envs] = optarg; nb_prom_envs++; break; - case QEMU_OPTION_old_param: - warn_report("-old-param is deprecated"); - old_param = 1; - break; case QEMU_OPTION_rtc: opts = qemu_opts_parse_noisily(qemu_find_opts("rtc"), optarg, false); From 19f6dcfe6b8b2a3523362812fc696ab83050d316 Mon Sep 17 00:00:00 2001 From: Cornelia Huck Date: Thu, 11 Sep 2025 17:41:59 +0200 Subject: [PATCH 0422/2396] arm/kvm: report registers we failed to set If we fail migration because of a mismatch of some registers between source and destination, the error message is not very informative: qemu-system-aarch64: error while loading state for instance 0x0 ofdevice 'cpu' qemu-system-aarch64: Failed to put registers after init: Invalid argument At least try to give the user a hint which registers had a problem, even if they cannot really do anything about it right now. Sample output: Could not set register op0:3 op1:0 crn:0 crm:0 op2:0 to c00fac31 (is 413fd0c1) We could be even more helpful once we support writable ID registers, at which point the user might actually be able to configure something that is migratable. Suggested-by: Eric Auger Reviewed-by: Sebastian Ott Signed-off-by: Cornelia Huck Message-id: 20250911154159.158046-1-cohuck@redhat.com Signed-off-by: Peter Maydell --- target/arm/kvm.c | 86 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) diff --git a/target/arm/kvm.c b/target/arm/kvm.c index 6672344855..c1ec6654ca 100644 --- a/target/arm/kvm.c +++ b/target/arm/kvm.c @@ -900,6 +900,58 @@ bool write_kvmstate_to_list(ARMCPU *cpu) return ok; } +/* pretty-print a KVM register */ +#define CP_REG_ARM64_SYSREG_OP(_reg, _op) \ + ((uint8_t)((_reg & CP_REG_ARM64_SYSREG_ ## _op ## _MASK) >> \ + CP_REG_ARM64_SYSREG_ ## _op ## _SHIFT)) + +static gchar *kvm_print_sve_register_name(uint64_t regidx) +{ + uint16_t sve_reg = regidx & 0x000000000000ffff; + + if (regidx == KVM_REG_ARM64_SVE_VLS) { + return g_strdup_printf("SVE VLS"); + } + /* zreg, preg, ffr */ + switch (sve_reg & 0xfc00) { + case 0: + return g_strdup_printf("SVE zreg n:%d slice:%d", + (sve_reg & 0x03e0) >> 5, sve_reg & 0x001f); + case 0x04: + return g_strdup_printf("SVE preg n:%d slice:%d", + (sve_reg & 0x01e0) >> 5, sve_reg & 0x001f); + case 0x06: + return g_strdup_printf("SVE ffr slice:%d", sve_reg & 0x001f); + default: + return g_strdup_printf("SVE ???"); + } +} + +static gchar *kvm_print_register_name(uint64_t regidx) +{ + switch ((regidx & KVM_REG_ARM_COPROC_MASK)) { + case KVM_REG_ARM_CORE: + return g_strdup_printf("core reg %"PRIx64, regidx); + case KVM_REG_ARM_DEMUX: + return g_strdup_printf("demuxed reg %"PRIx64, regidx); + case KVM_REG_ARM64_SYSREG: + return g_strdup_printf("op0:%d op1:%d crn:%d crm:%d op2:%d", + CP_REG_ARM64_SYSREG_OP(regidx, OP0), + CP_REG_ARM64_SYSREG_OP(regidx, OP1), + CP_REG_ARM64_SYSREG_OP(regidx, CRN), + CP_REG_ARM64_SYSREG_OP(regidx, CRM), + CP_REG_ARM64_SYSREG_OP(regidx, OP2)); + case KVM_REG_ARM_FW: + return g_strdup_printf("fw reg %d", (int)(regidx & 0xffff)); + case KVM_REG_ARM64_SVE: + return kvm_print_sve_register_name(regidx); + case KVM_REG_ARM_FW_FEAT_BMAP: + return g_strdup_printf("fw feat reg %d", (int)(regidx & 0xffff)); + default: + return g_strdup_printf("%"PRIx64, regidx); + } +} + bool write_list_to_kvmstate(ARMCPU *cpu, int level) { CPUState *cs = CPU(cpu); @@ -927,11 +979,45 @@ bool write_list_to_kvmstate(ARMCPU *cpu, int level) g_assert_not_reached(); } if (ret) { + gchar *reg_str = kvm_print_register_name(regidx); + /* We might fail for "unknown register" and also for * "you tried to set a register which is constant with * a different value from what it actually contains". */ ok = false; + switch (ret) { + case -ENOENT: + error_report("Could not set register %s: unknown to KVM", + reg_str); + break; + case -EINVAL: + if ((regidx & KVM_REG_SIZE_MASK) == KVM_REG_SIZE_U32) { + if (!kvm_get_one_reg(cs, regidx, &v32)) { + error_report("Could not set register %s to %x (is %x)", + reg_str, (uint32_t)cpu->cpreg_values[i], + v32); + } else { + error_report("Could not set register %s to %x", + reg_str, (uint32_t)cpu->cpreg_values[i]); + } + } else /* U64 */ { + uint64_t v64; + + if (!kvm_get_one_reg(cs, regidx, &v64)) { + error_report("Could not set register %s to %"PRIx64" (is %"PRIx64")", + reg_str, cpu->cpreg_values[i], v64); + } else { + error_report("Could not set register %s to %"PRIx64, + reg_str, cpu->cpreg_values[i]); + } + } + break; + default: + error_report("Could not set register %s: %s", + reg_str, strerror(-ret)); + } + g_free(reg_str); } } return ok; From 71cda981208ebcfa403d8e3451d1d5bdef0b0931 Mon Sep 17 00:00:00 2001 From: Vacha Bhavsar Date: Tue, 9 Sep 2025 16:10:10 +0000 Subject: [PATCH 0423/2396] target/arm: Increase MAX_PACKET_LENGTH for SME ZA remote gdb debugging This patch increases the value of the MAX_PACKET_LEGNTH to 131104 from 4096 to allow the GDBState.line_buf to be large enough to accommodate the full contents of the SME ZA storage when the vector length is maximal. This is in preparation for a related patch that allows SME register visibility through remote GDB debugging. Signed-off-by: Vacha Bhavsar Reviewed-by: Peter Maydell Message-id: 20250909161012.2561593-2-vacha.bhavsar@oss.qualcomm.com [PMM: fixed up comment formatting] Signed-off-by: Peter Maydell --- gdbstub/internals.h | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/gdbstub/internals.h b/gdbstub/internals.h index bf5a5c6302..92466b28c1 100644 --- a/gdbstub/internals.h +++ b/gdbstub/internals.h @@ -11,7 +11,27 @@ #include "exec/cpu-common.h" -#define MAX_PACKET_LENGTH 4096 +/* + * Most "large" transfers (e.g. memory reads, feature XML + * transfer) have mechanisms in the gdb protocol for splitting + * them. However, register values in particular cannot currently + * be split. This packet size must therefore be at least big enough + * for the worst-case register size. Currently that is Arm SME + * ZA storage with a 256x256 byte value. We also must account + * for the conversion from raw data to hex in gdb_memtohex(), + * which writes 2 * size bytes, and for other protocol overhead + * including command, register number and checksum which add + * another 4 bytes of overhead. However, to be consistent with + * the changes made in gdbserver to address this same requirement, + * we add a total of 32 bytes to account for protocol overhead + * (unclear why specifically 32 bytes), bringing the value of + * MAX_PACKET_LENGTH to 2 * 256 * 256 + 32 = 131104. + * + * The commit making this change for gdbserver can be found here: + * https://sourceware.org/git/?p=binutils-gdb.git;a=commit;h= + * b816042e88583f280ad186ff124ab84d31fb592b + */ +#define MAX_PACKET_LENGTH 131104 /* * Shared structures and definitions From 030f0ba11767c7bd4148d9cd4f63e299d38139b3 Mon Sep 17 00:00:00 2001 From: Vacha Bhavsar Date: Tue, 9 Sep 2025 16:10:11 +0000 Subject: [PATCH 0424/2396] target/arm: Added support for SME register exposure to GDB The QEMU GDB stub does not expose the ZA storage SME register to GDB via the remote serial protocol, which can be a useful functionality to debug SME code. To provide this functionality for AArch64 targets, this patch registers the SME register set with the GDB stub. To do so, this patch implements the aarch64_gdb_get_sme_reg() and aarch64_gdb_set_sme_reg() functions to specify how to get and set the SME registers, and the arm_gen_dynamic_smereg_feature() function to generate the target description in XML format to indicate the target architecture supports SME. Finally, this patch includes a dyn_smereg_feature structure to hold this GDB XML description of the SME registers for each CPU. Note that according to the GDB documentation the ZA register is defined as a vector of bytes; however the target description xml retrieved when using gdb natively on a host with SME capabilities represents the ZA register as a vector of vectors of bytes, so this is a GDB documentation error. We follow GDB's own gdbstub implementation and represent the ZA register as a vector of vectors of bytes as is done by GDB here: https://github.com/bminor/binutils-gdb/blob/5cce2b7006daa7073b98e3d1a3b176199d1381d7/gdb/features/aarch64-sme.c#L50 Signed-off-by: Vacha Bhavsar Message-id: 20250909161012.2561593-3-vacha.bhavsar@oss.qualcomm.com Reviewed-by: Peter Maydell [PMM: fixed minor checkpatch nits] Signed-off-by: Peter Maydell --- target/arm/cpu.h | 1 + target/arm/gdbstub.c | 10 +++- target/arm/gdbstub64.c | 119 +++++++++++++++++++++++++++++++++++++++++ target/arm/internals.h | 3 ++ 4 files changed, 132 insertions(+), 1 deletion(-) diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 6644043f4c..1c0deb723d 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -925,6 +925,7 @@ struct ArchCPU { DynamicGDBFeatureInfo dyn_sysreg_feature; DynamicGDBFeatureInfo dyn_svereg_feature; + DynamicGDBFeatureInfo dyn_smereg_feature; DynamicGDBFeatureInfo dyn_m_systemreg_feature; DynamicGDBFeatureInfo dyn_m_secextreg_feature; diff --git a/target/arm/gdbstub.c b/target/arm/gdbstub.c index ce4497ad7c..2d331fff44 100644 --- a/target/arm/gdbstub.c +++ b/target/arm/gdbstub.c @@ -527,7 +527,8 @@ void arm_cpu_register_gdb_regs_for_features(ARMCPU *cpu) * registers so we don't need to include both. */ #ifdef TARGET_AARCH64 - if (isar_feature_aa64_sve(&cpu->isar)) { + if (isar_feature_aa64_sve(&cpu->isar) || + isar_feature_aa64_sme(&cpu->isar)) { GDBFeature *feature = arm_gen_dynamic_svereg_feature(cs, cs->gdb_num_regs); gdb_register_coprocessor(cs, aarch64_gdb_get_sve_reg, aarch64_gdb_set_sve_reg, feature, 0); @@ -537,6 +538,13 @@ void arm_cpu_register_gdb_regs_for_features(ARMCPU *cpu) gdb_find_static_feature("aarch64-fpu.xml"), 0); } + + if (isar_feature_aa64_sme(&cpu->isar)) { + GDBFeature *sme_feature = + arm_gen_dynamic_smereg_feature(cs, cs->gdb_num_regs); + gdb_register_coprocessor(cs, aarch64_gdb_get_sme_reg, + aarch64_gdb_set_sme_reg, sme_feature, 0); + } /* * Note that we report pauth information via the feature name * org.gnu.gdb.aarch64.pauth_v2, not org.gnu.gdb.aarch64.pauth. diff --git a/target/arm/gdbstub64.c b/target/arm/gdbstub64.c index 08e2858539..3bccde2bf2 100644 --- a/target/arm/gdbstub64.c +++ b/target/arm/gdbstub64.c @@ -249,6 +249,90 @@ int aarch64_gdb_set_sve_reg(CPUState *cs, uint8_t *buf, int reg) return 0; } +int aarch64_gdb_get_sme_reg(CPUState *cs, GByteArray *buf, int reg) +{ + ARMCPU *cpu = ARM_CPU(cs); + CPUARMState *env = &cpu->env; + + switch (reg) { + case 0: /* svg register */ + { + int vq = 0; + if (FIELD_EX64(env->svcr, SVCR, SM)) { + vq = sve_vqm1_for_el_sm(env, arm_current_el(env), + FIELD_EX64(env->svcr, SVCR, SM)) + 1; + } + /* svg = vector granules (2 * vector quardwords) in streaming mode */ + return gdb_get_reg64(buf, vq * 2); + } + case 1: /* svcr register */ + return gdb_get_reg64(buf, env->svcr); + case 2: /* za register */ + { + int len = 0; + int vq = cpu->sme_max_vq; + int svl = vq * 16; + for (int i = 0; i < svl; i++) { + for (int q = 0; q < vq; q++) { + len += gdb_get_reg128(buf, + env->za_state.za[i].d[q * 2 + 1], + env->za_state.za[i].d[q * 2]); + } + } + return len; + } + default: + /* gdbstub asked for something out of range */ + qemu_log_mask(LOG_UNIMP, "%s: out of range register %d", __func__, reg); + break; + } + + return 0; +} + +int aarch64_gdb_set_sme_reg(CPUState *cs, uint8_t *buf, int reg) +{ + ARMCPU *cpu = ARM_CPU(cs); + CPUARMState *env = &cpu->env; + + switch (reg) { + case 0: /* svg register */ + /* cannot set svg via gdbstub */ + return 8; + case 1: /* svcr register */ + aarch64_set_svcr(env, ldq_le_p(buf), + R_SVCR_SM_MASK | R_SVCR_ZA_MASK); + return 8; + case 2: /* za register */ + { + int len = 0; + int vq = cpu->sme_max_vq; + int svl = vq * 16; + for (int i = 0; i < svl; i++) { + for (int q = 0; q < vq; q++) { + if (target_big_endian()) { + env->za_state.za[i].d[q * 2 + 1] = ldq_p(buf); + buf += 8; + env->za_state.za[i].d[q * 2] = ldq_p(buf); + } else{ + env->za_state.za[i].d[q * 2] = ldq_p(buf); + buf += 8; + env->za_state.za[i].d[q * 2 + 1] = ldq_p(buf); + } + buf += 8; + len += 16; + } + } + return len; + } + default: + /* gdbstub asked for something out of range */ + break; + } + + return 0; +} + int aarch64_gdb_get_pauth_reg(CPUState *cs, GByteArray *buf, int reg) { ARMCPU *cpu = ARM_CPU(cs); @@ -413,6 +497,41 @@ GDBFeature *arm_gen_dynamic_svereg_feature(CPUState *cs, int base_reg) return &cpu->dyn_svereg_feature.desc; } +GDBFeature *arm_gen_dynamic_smereg_feature(CPUState *cs, int base_reg) +{ + ARMCPU *cpu = ARM_CPU(cs); + int vq = cpu->sme_max_vq; + int svl = vq * 16; + GDBFeatureBuilder builder; + int reg = 0; + + gdb_feature_builder_init(&builder, &cpu->dyn_smereg_feature.desc, + "org.gnu.gdb.aarch64.sme", "sme-registers.xml", + base_reg); + + + /* Create the sme_bv vector type. */ + gdb_feature_builder_append_tag( + &builder, "", + svl); + + /* Create the sme_bvv vector type. */ + gdb_feature_builder_append_tag( + &builder, "", + svl); + + /* Define the svg, svcr, and za registers. */ + + gdb_feature_builder_append_reg(&builder, "svg", 64, reg++, "int", NULL); + gdb_feature_builder_append_reg(&builder, "svcr", 64, reg++, "int", NULL); + gdb_feature_builder_append_reg(&builder, "za", svl * svl * 8, reg++, + "sme_bvv", NULL); + + gdb_feature_builder_end(&builder); + + return &cpu->dyn_smereg_feature.desc; +} + #ifdef CONFIG_USER_ONLY int aarch64_gdb_get_tag_ctl_reg(CPUState *cs, GByteArray *buf, int reg) { diff --git a/target/arm/internals.h b/target/arm/internals.h index 8782594b77..532fabcafc 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -1817,8 +1817,11 @@ static inline uint64_t pmu_counter_mask(CPUARMState *env) } GDBFeature *arm_gen_dynamic_svereg_feature(CPUState *cpu, int base_reg); +GDBFeature *arm_gen_dynamic_smereg_feature(CPUState *cpu, int base_reg); int aarch64_gdb_get_sve_reg(CPUState *cs, GByteArray *buf, int reg); int aarch64_gdb_set_sve_reg(CPUState *cs, uint8_t *buf, int reg); +int aarch64_gdb_get_sme_reg(CPUState *cs, GByteArray *buf, int reg); +int aarch64_gdb_set_sme_reg(CPUState *cs, uint8_t *buf, int reg); int aarch64_gdb_get_fpu_reg(CPUState *cs, GByteArray *buf, int reg); int aarch64_gdb_set_fpu_reg(CPUState *cs, uint8_t *buf, int reg); int aarch64_gdb_get_pauth_reg(CPUState *cs, GByteArray *buf, int reg); From 904b8aae52a7b23fa00285decba535bd1e8f8d50 Mon Sep 17 00:00:00 2001 From: Vacha Bhavsar Date: Tue, 9 Sep 2025 16:10:12 +0000 Subject: [PATCH 0425/2396] target/arm: Added test case for SME register exposure to GDB This patch adds a test case to test SME register exposure to a remote gdb debugging session. This test simply sets and reads SME registers. Signed-off-by: Vacha Bhavsar Message-id: 20250909161012.2561593-4-vacha.bhavsar@oss.qualcomm.com [PMM: fixed various python formatting nits] Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- configure | 6 ++ tests/tcg/aarch64/Makefile.target | 29 +++++++ tests/tcg/aarch64/gdbstub/test-sme.py | 117 ++++++++++++++++++++++++++ 3 files changed, 152 insertions(+) create mode 100644 tests/tcg/aarch64/gdbstub/test-sme.py diff --git a/configure b/configure index 274a778764..9aea02cf6a 100755 --- a/configure +++ b/configure @@ -1839,6 +1839,12 @@ for target in $target_list; do echo "GDB=$gdb_bin" >> $config_target_mak fi + if test "${gdb_arches#*$arch}" != "$gdb_arches" && version_ge $gdb_version 14.1; then + echo "GDB_HAS_SME_TILES=y" >> $config_target_mak + else + echo "GDB_HAS_SME_TILES=n" >> $config_target_mak + fi + if test "${gdb_arches#*aarch64}" != "$gdb_arches" && version_ge $gdb_version 15.1; then echo "GDB_HAS_MTE=y" >> $config_target_mak fi diff --git a/tests/tcg/aarch64/Makefile.target b/tests/tcg/aarch64/Makefile.target index 16ddcf4f88..1755874bee 100644 --- a/tests/tcg/aarch64/Makefile.target +++ b/tests/tcg/aarch64/Makefile.target @@ -134,6 +134,35 @@ run-gdbstub-sve-ioctls: sve-ioctls EXTRA_RUNS += run-gdbstub-sysregs run-gdbstub-sve-ioctls +ifneq ($(CROSS_AS_HAS_ARMV9_SME),) +# SME gdbstub tests + +run-gdbstub-sysregs-sme: sysregs + $(call run-test, $@, $(GDB_SCRIPT) \ + --gdb $(GDB) \ + --qemu $(QEMU) --qargs "$(QEMU_OPTS)" \ + --bin $< --test $(AARCH64_SRC)/gdbstub/test-sme.py \ + -- test_sme --gdb_basic_za_test, \ + basic gdbstub SME support) + +ifeq ($(GDB_HAS_SME_TILES),y) +run-gdbstub-sysregs-sme-tile-slice: sysregs + $(call run-test, $@, $(GDB_SCRIPT) \ + --gdb $(GDB) \ + --qemu $(QEMU) --qargs "$(QEMU_OPTS)" \ + --bin $< --test $(AARCH64_SRC)/gdbstub/test-sme.py \ + -- test_sme --gdb_tile_slice_test, \ + gdbstub SME ZA tile slice support) +else +run-gdbstub-sysregs-sme-tile-slice: sysregs + $(call skip-test,"gdbstub SME ZA tile slice support", \ + "selected gdb ($(GDB)) does not support SME ZA tile slices") +endif + +EXTRA_RUNS += run-gdbstub-sysregs-sme run-gdbstub-sysregs-sme-tile-slice + +endif + ifeq ($(GDB_HAS_MTE),y) run-gdbstub-mte: mte-8 $(call run-test, $@, $(GDB_SCRIPT) \ diff --git a/tests/tcg/aarch64/gdbstub/test-sme.py b/tests/tcg/aarch64/gdbstub/test-sme.py new file mode 100644 index 0000000000..ec03189642 --- /dev/null +++ b/tests/tcg/aarch64/gdbstub/test-sme.py @@ -0,0 +1,117 @@ +# +# Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +# +# SPDX-License-Identifier: GPL-2.0-or-later + +# +# Test the SME registers are visible and changeable via gdbstub +# +# This is launched via tests/guest-debug/run-test.py +# + +import argparse +import gdb +from test_gdbstub import main, report + +MAGIC = 0x01020304 +BASIC_ZA_TEST = 0 +TILE_SLICE_TEST = 0 + + +def run_test(): + """Run the requested test(s) for SME ZA gdbstub support""" + + if BASIC_ZA_TEST: + run_basic_sme_za_gdbstub_support_test() + if TILE_SLICE_TEST: + run_basic_sme_za_tile_slice_gdbstub_support_test() + + +def run_basic_sme_za_gdbstub_support_test(): + """Test reads and writes to the SME ZA register at the byte level""" + + frame = gdb.selected_frame() + rname = "za" + za = frame.read_register(rname) + report(True, "Reading %s" % rname) + + # Writing to the ZA register, byte by byte. + for i in range(0, 16): + for j in range(0, 16): + cmd = "set $za[%d][%d] = 0x01" % (i, j) + gdb.execute(cmd) + report(True, "%s" % cmd) + + # Reading from the ZA register, byte by byte. + for i in range(0, 16): + for j in range(0, 16): + reg = "$za[%d][%d]" % (i, j) + v = gdb.parse_and_eval(reg) + report(str(v.type) == "uint8_t", "size of %s" % (reg)) + report(v == 0x1, "%s is 0x%x" % (reg, 0x1)) + + +def run_basic_sme_za_tile_slice_gdbstub_support_test(): + """Test reads and writes of SME ZA horizontal and vertical tile slices + + Test if SME ZA tile slices, both horizontal and vertical, + can be correctly read and written to. The sizes to test + are quadwords and doublewords. + """ + + sizes = {} + sizes["q"] = "uint128_t" + sizes["d"] = "uint64_t" + + # Accessing requested sizes of elements of ZA + for size in sizes: + + # Accessing various ZA tiles + for i in range(0, 4): + + # Accessing various horizontal slices for each ZA tile + for j in range(0, 4): + # Writing to various elements in each tile slice + for k in range(0, 4): + cmd = "set $za%dh%c%d[%d] = 0x%x" % (i, size, j, k, MAGIC) + gdb.execute(cmd) + report(True, "%s" % cmd) + + # Reading from the written elements in each tile slice + for k in range(0, 4): + reg = "$za%dh%c%d[%d]" % (i, size, j, k) + v = gdb.parse_and_eval(reg) + report(str(v.type) == sizes[size], "size of %s" % (reg)) + report(v == MAGIC, "%s is 0x%x" % (reg, MAGIC)) + + # Accessing various vertical slices for each ZA tile + for j in range(0, 4): + # Writing to various elements in each tile slice + for k in range(0, 4): + cmd = "set $za%dv%c%d[%d] = 0x%x" % (i, size, j, k, MAGIC) + gdb.execute(cmd) + report(True, "%s" % cmd) + + # Reading from the written elements in each tile slice + for k in range(0, 4): + reg = "$za%dv%c%d[%d]" % (i, size, j, k) + v = gdb.parse_and_eval(reg) + report(str(v.type) == sizes[size], "size of %s" % (reg)) + report(v == MAGIC, "%s is 0x%x" % (reg, MAGIC)) + + +parser = argparse.ArgumentParser(description="A gdbstub test for SME support") +parser.add_argument("--gdb_basic_za_test", + help="Enable test for basic SME ZA support", + action="store_true") +parser.add_argument("--gdb_tile_slice_test", + help="Enable test for ZA tile slice support", + action="store_true") +args = parser.parse_args() + +if args.gdb_basic_za_test: + BASIC_ZA_TEST = 1 +if args.gdb_tile_slice_test: + TILE_SLICE_TEST = 1 + +main(run_test, expected_arch="aarch64") From d9e6b8424fd2523a0361972d5dd841471879479c Mon Sep 17 00:00:00 2001 From: Shameer Kolothum Date: Fri, 29 Aug 2025 09:25:23 +0100 Subject: [PATCH 0426/2396] hw/arm/smmu-common: Check SMMU has PCIe Root Complex association We only allow default PCIe Root Complex(pcie.0) or pxb-pcie based extra root complexes to be associated with SMMU. Although this change does not affect functionality at present, it is required when we add support for user-creatable SMMUv3 devices in future patches. Note: Added a specific check to identify pxb-pcie to avoid matching pxb-cxl host bridges, which are also of type PCI_HOST_BRIDGE. This restriction can be relaxed once support for CXL devices on arm/virt is added and validated with SMMUv3. Reviewed-by: Jonathan Cameron Reviewed-by: Eric Auger Tested-by: Nathan Chen Tested-by: Eric Auger Reviewed-by: Nicolin Chen Tested-by: Nicolin Chen Signed-off-by: Shameer Kolothum Signed-off-by: Shameer Kolothum Reviewed-by: Donald Dutile Message-id: 20250829082543.7680-2-skolothumtho@nvidia.com Signed-off-by: Peter Maydell --- hw/arm/smmu-common.c | 31 ++++++++++++++++++++++++++--- hw/pci-bridge/pci_expander_bridge.c | 1 - include/hw/pci/pci_bridge.h | 1 + 3 files changed, 29 insertions(+), 4 deletions(-) diff --git a/hw/arm/smmu-common.c b/hw/arm/smmu-common.c index 0dcaf2f589..7f64ea48d0 100644 --- a/hw/arm/smmu-common.c +++ b/hw/arm/smmu-common.c @@ -20,6 +20,7 @@ #include "trace.h" #include "exec/target_page.h" #include "hw/core/cpu.h" +#include "hw/pci/pci_bridge.h" #include "hw/qdev-properties.h" #include "qapi/error.h" #include "qemu/jhash.h" @@ -925,6 +926,7 @@ static void smmu_base_realize(DeviceState *dev, Error **errp) { SMMUState *s = ARM_SMMU(dev); SMMUBaseClass *sbc = ARM_SMMU_GET_CLASS(dev); + PCIBus *pci_bus = s->primary_bus; Error *local_err = NULL; sbc->parent_realize(dev, &local_err); @@ -937,11 +939,34 @@ static void smmu_base_realize(DeviceState *dev, Error **errp) g_free, g_free); s->smmu_pcibus_by_busptr = g_hash_table_new(NULL, NULL); - if (s->primary_bus) { - pci_setup_iommu(s->primary_bus, &smmu_ops, s); - } else { + if (!pci_bus) { error_setg(errp, "SMMU is not attached to any PCI bus!"); + return; } + + /* + * We only allow default PCIe Root Complex(pcie.0) or pxb-pcie based extra + * root complexes to be associated with SMMU. + */ + if (pci_bus_is_express(pci_bus) && pci_bus_is_root(pci_bus) && + object_dynamic_cast(OBJECT(pci_bus)->parent, TYPE_PCI_HOST_BRIDGE)) { + /* + * This condition matches either the default pcie.0, pxb-pcie, or + * pxb-cxl. For both pxb-pcie and pxb-cxl, parent_dev will be set. + * Currently, we don't allow pxb-cxl as it requires further + * verification. Therefore, make sure this is indeed pxb-pcie. + */ + if (pci_bus->parent_dev) { + if (!object_dynamic_cast(OBJECT(pci_bus), TYPE_PXB_PCIE_BUS)) { + goto out_err; + } + } + pci_setup_iommu(pci_bus, &smmu_ops, s); + return; + } +out_err: + error_setg(errp, "SMMU should be attached to a default PCIe root complex" + "(pcie.0) or a pxb-pcie based root complex"); } /* diff --git a/hw/pci-bridge/pci_expander_bridge.c b/hw/pci-bridge/pci_expander_bridge.c index 3a29dfefc2..1bcceddbc4 100644 --- a/hw/pci-bridge/pci_expander_bridge.c +++ b/hw/pci-bridge/pci_expander_bridge.c @@ -34,7 +34,6 @@ typedef struct PXBBus PXBBus; DECLARE_INSTANCE_CHECKER(PXBBus, PXB_BUS, TYPE_PXB_BUS) -#define TYPE_PXB_PCIE_BUS "pxb-pcie-bus" DECLARE_INSTANCE_CHECKER(PXBBus, PXB_PCIE_BUS, TYPE_PXB_PCIE_BUS) diff --git a/include/hw/pci/pci_bridge.h b/include/hw/pci/pci_bridge.h index 8cdacbc4e1..a055fd8d32 100644 --- a/include/hw/pci/pci_bridge.h +++ b/include/hw/pci/pci_bridge.h @@ -104,6 +104,7 @@ typedef struct PXBPCIEDev { PXBDev parent_obj; } PXBPCIEDev; +#define TYPE_PXB_PCIE_BUS "pxb-pcie-bus" #define TYPE_PXB_CXL_BUS "pxb-cxl-bus" #define TYPE_PXB_DEV "pxb" OBJECT_DECLARE_SIMPLE_TYPE(PXBDev, PXB_DEV) From 0e6a5bfb0eb17f57fb923b7905bd1435204bdd62 Mon Sep 17 00:00:00 2001 From: Shameer Kolothum Date: Fri, 29 Aug 2025 09:25:24 +0100 Subject: [PATCH 0427/2396] hw/arm/virt-acpi-build: Re-arrange SMMUv3 IORT build Introduce a new struct AcpiIortSMMUv3Dev to hold all the information required for SMMUv3 IORT node and use that for populating the node. The current machine wide SMMUv3 is named as legacy SMMUv3 as we will soon add support for user-creatable SMMUv3 devices. These changes will be useful to have common code paths when we add that support. Tested-by: Nathan Chen Reviewed-by: Nicolin Chen Reviewed-by: Jonathan Cameron Reviewed-by: Eric Auger Tested-by: Eric Auger Tested-by: Nicolin Chen Signed-off-by: Shameer Kolothum Signed-off-by: Shameer Kolothum Reviewed-by: Donald Dutile Message-id: 20250829082543.7680-3-skolothumtho@nvidia.com Signed-off-by: Peter Maydell --- hw/arm/virt-acpi-build.c | 137 ++++++++++++++++++++++++++------------- hw/arm/virt.c | 1 + include/hw/arm/virt.h | 1 + 3 files changed, 94 insertions(+), 45 deletions(-) diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c index b01fc4f8ef..bef4fabe56 100644 --- a/hw/arm/virt-acpi-build.c +++ b/hw/arm/virt-acpi-build.c @@ -305,29 +305,65 @@ static int iort_idmap_compare(gconstpointer a, gconstpointer b) return idmap_a->input_base - idmap_b->input_base; } +typedef struct AcpiIortSMMUv3Dev { + int irq; + hwaddr base; + GArray *rc_smmu_idmaps; + /* Offset of the SMMUv3 IORT Node relative to the start of the IORT */ + size_t offset; +} AcpiIortSMMUv3Dev; + +/* + * Populate the struct AcpiIortSMMUv3Dev for the legacy SMMUv3 and + * return the total number of associated idmaps. + */ +static int populate_smmuv3_legacy_dev(GArray *sdev_blob) +{ + VirtMachineState *vms = VIRT_MACHINE(qdev_get_machine()); + AcpiIortSMMUv3Dev sdev; + + sdev.rc_smmu_idmaps = g_array_new(false, true, sizeof(AcpiIortIdMapping)); + object_child_foreach_recursive(object_get_root(), iort_host_bridges, + sdev.rc_smmu_idmaps); + /* + * There can be only one legacy SMMUv3("iommu=smmuv3") as it is a machine + * wide one. Since it may cover multiple PCIe RCs(based on "bypass_iommu" + * property), may have multiple SMMUv3 idmaps. Sort it by input_base. + */ + g_array_sort(sdev.rc_smmu_idmaps, iort_idmap_compare); + + sdev.base = vms->memmap[VIRT_SMMU].base; + sdev.irq = vms->irqmap[VIRT_SMMU] + ARM_SPI_BASE; + g_array_append_val(sdev_blob, sdev); + return sdev.rc_smmu_idmaps->len; +} + /* Compute ID ranges (RIDs) from RC that are directed to the ITS Group node */ -static void create_rc_its_idmaps(GArray *its_idmaps, GArray *smmu_idmaps) +static void create_rc_its_idmaps(GArray *its_idmaps, GArray *smmuv3_devs) { AcpiIortIdMapping *idmap; AcpiIortIdMapping next_range = {0}; + AcpiIortSMMUv3Dev *sdev; - /* - * Based on the RID ranges that are directed to the SMMU, determine the - * bypassed RID ranges, i.e., the ones that are directed to the ITS Group - * node and do not pass through the SMMU, by subtracting the SMMU-bound - * ranges from the full RID range (0x0000–0xFFFF). - */ - for (int i = 0; i < smmu_idmaps->len; i++) { - idmap = &g_array_index(smmu_idmaps, AcpiIortIdMapping, i); + for (int i = 0; i < smmuv3_devs->len; i++) { + sdev = &g_array_index(smmuv3_devs, AcpiIortSMMUv3Dev, i); + /* + * Based on the RID ranges that are directed to the SMMU, determine the + * bypassed RID ranges, i.e., the ones that are directed to the ITS + * Group node and do not pass through the SMMU, by subtracting the + * SMMU-bound ranges from the full RID range (0x0000–0xFFFF). + */ + for (int j = 0; j < sdev->rc_smmu_idmaps->len; j++) { + idmap = &g_array_index(sdev->rc_smmu_idmaps, AcpiIortIdMapping, j); - if (next_range.input_base < idmap->input_base) { - next_range.id_count = idmap->input_base - next_range.input_base; - g_array_append_val(its_idmaps, next_range); + if (next_range.input_base < idmap->input_base) { + next_range.id_count = idmap->input_base - next_range.input_base; + g_array_append_val(its_idmaps, next_range); + } + + next_range.input_base = idmap->input_base + idmap->id_count; } - - next_range.input_base = idmap->input_base + idmap->id_count; } - /* * Append the last RC -> ITS ID mapping. * @@ -341,7 +377,6 @@ static void create_rc_its_idmaps(GArray *its_idmaps, GArray *smmu_idmaps) } } - /* * Input Output Remapping Table (IORT) * Conforms to "IO Remapping Table System Software on ARM Platforms", @@ -351,9 +386,12 @@ static void build_iort(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) { int i, nb_nodes, rc_mapping_count; - size_t node_size, smmu_offset = 0; + AcpiIortSMMUv3Dev *sdev; + size_t node_size; + int num_smmus = 0; uint32_t id = 0; - GArray *rc_smmu_idmaps = g_array_new(false, true, sizeof(AcpiIortIdMapping)); + int rc_smmu_idmaps_len = 0; + GArray *smmuv3_devs = g_array_new(false, true, sizeof(AcpiIortSMMUv3Dev)); GArray *rc_its_idmaps = g_array_new(false, true, sizeof(AcpiIortIdMapping)); AcpiTable table = { .sig = "IORT", .rev = 3, .oem_id = vms->oem_id, @@ -361,22 +399,21 @@ build_iort(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) /* Table 2 The IORT */ acpi_table_begin(&table, table_data); - if (vms->iommu == VIRT_IOMMU_SMMUV3) { - object_child_foreach_recursive(object_get_root(), - iort_host_bridges, rc_smmu_idmaps); + if (vms->legacy_smmuv3_present) { + rc_smmu_idmaps_len = populate_smmuv3_legacy_dev(smmuv3_devs); + } - /* Sort the smmu idmap by input_base */ - g_array_sort(rc_smmu_idmaps, iort_idmap_compare); - - nb_nodes = 2; /* RC and SMMUv3 */ - rc_mapping_count = rc_smmu_idmaps->len; + num_smmus = smmuv3_devs->len; + if (num_smmus) { + nb_nodes = num_smmus + 1; /* RC and SMMUv3 */ + rc_mapping_count = rc_smmu_idmaps_len; if (vms->its) { /* * Knowing the ID ranges from the RC to the SMMU, it's possible to * determine the ID ranges from RC that go directly to ITS. */ - create_rc_its_idmaps(rc_its_idmaps, rc_smmu_idmaps); + create_rc_its_idmaps(rc_its_idmaps, smmuv3_devs); nb_nodes++; /* ITS */ rc_mapping_count += rc_its_idmaps->len; @@ -411,9 +448,10 @@ build_iort(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) build_append_int_noprefix(table_data, 0 /* MADT translation_id */, 4); } - if (vms->iommu == VIRT_IOMMU_SMMUV3) { - int irq = vms->irqmap[VIRT_SMMU] + ARM_SPI_BASE; + for (i = 0; i < num_smmus; i++) { + sdev = &g_array_index(smmuv3_devs, AcpiIortSMMUv3Dev, i); int smmu_mapping_count, offset_to_id_array; + int irq = sdev->irq; if (vms->its) { smmu_mapping_count = 1; /* ITS Group node */ @@ -422,7 +460,7 @@ build_iort(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) smmu_mapping_count = 0; /* No ID mappings */ offset_to_id_array = 0; /* No ID mappings array */ } - smmu_offset = table_data->len - table.table_offset; + sdev->offset = table_data->len - table.table_offset; /* Table 9 SMMUv3 Format */ build_append_int_noprefix(table_data, 4 /* SMMUv3 */, 1); /* Type */ node_size = SMMU_V3_ENTRY_SIZE + @@ -435,7 +473,7 @@ build_iort(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) /* Reference to ID Array */ build_append_int_noprefix(table_data, offset_to_id_array, 4); /* Base address */ - build_append_int_noprefix(table_data, vms->memmap[VIRT_SMMU].base, 8); + build_append_int_noprefix(table_data, sdev->base, 8); /* Flags */ build_append_int_noprefix(table_data, 1 /* COHACC Override */, 4); build_append_int_noprefix(table_data, 0, 4); /* Reserved */ @@ -486,21 +524,26 @@ build_iort(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) build_append_int_noprefix(table_data, 0, 3); /* Reserved */ /* Output Reference */ - if (vms->iommu == VIRT_IOMMU_SMMUV3) { + if (num_smmus) { AcpiIortIdMapping *range; - /* - * Map RIDs (input) from RC to SMMUv3 nodes: RC -> SMMUv3. - * - * N.B.: The mapping from SMMUv3 to ITS Group node (SMMUv3 -> ITS) is - * defined in the SMMUv3 table, where all SMMUv3 IDs are mapped to the - * ITS Group node, if ITS is available. - */ - for (i = 0; i < rc_smmu_idmaps->len; i++) { - range = &g_array_index(rc_smmu_idmaps, AcpiIortIdMapping, i); - /* Output IORT node is the SMMUv3 node. */ - build_iort_id_mapping(table_data, range->input_base, - range->id_count, smmu_offset); + for (i = 0; i < num_smmus; i++) { + sdev = &g_array_index(smmuv3_devs, AcpiIortSMMUv3Dev, i); + + /* + * Map RIDs (input) from RC to SMMUv3 nodes: RC -> SMMUv3. + * + * N.B.: The mapping from SMMUv3 to ITS Group node (SMMUv3 -> ITS) + * is defined in the SMMUv3 table, where all SMMUv3 IDs are mapped + * to the ITS Group node, if ITS is available. + */ + for (int j = 0; j < sdev->rc_smmu_idmaps->len; j++) { + range = &g_array_index(sdev->rc_smmu_idmaps, + AcpiIortIdMapping, j); + /* Output IORT node is the SMMUv3 node. */ + build_iort_id_mapping(table_data, range->input_base, + range->id_count, sdev->offset); + } } if (vms->its) { @@ -525,8 +568,12 @@ build_iort(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) } acpi_table_end(linker, &table); - g_array_free(rc_smmu_idmaps, true); g_array_free(rc_its_idmaps, true); + for (i = 0; i < num_smmus; i++) { + sdev = &g_array_index(smmuv3_devs, AcpiIortSMMUv3Dev, i); + g_array_free(sdev->rc_smmu_idmaps, true); + } + g_array_free(smmuv3_devs, true); } /* diff --git a/hw/arm/virt.c b/hw/arm/virt.c index e5c4142e82..16a1ac3c2d 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -1651,6 +1651,7 @@ static void create_pcie(VirtMachineState *vms) qemu_fdt_setprop_cells(ms->fdt, nodename, "iommu-map", 0x0, vms->iommu_phandle, 0x0, 0x10000); } + vms->legacy_smmuv3_present = true; break; default: g_assert_not_reached(); diff --git a/include/hw/arm/virt.h b/include/hw/arm/virt.h index 365a28b082..ea2cff05b0 100644 --- a/include/hw/arm/virt.h +++ b/include/hw/arm/virt.h @@ -179,6 +179,7 @@ struct VirtMachineState { char *oem_table_id; bool ns_el2_virt_timer_irq; CXLState cxl_devices_state; + bool legacy_smmuv3_present; }; #define VIRT_ECAM_ID(high) (high ? VIRT_HIGH_PCIE_ECAM : VIRT_PCIE_ECAM) From 01e9a18730e6f56f713ed074603a8b0f2982ed26 Mon Sep 17 00:00:00 2001 From: Shameer Kolothum Date: Fri, 29 Aug 2025 09:25:25 +0100 Subject: [PATCH 0428/2396] hw/arm/virt-acpi-build: Update IORT for multiple smmuv3 devices With the soon to be introduced user-creatable SMMUv3 devices for virt, it is possible to have multiple SMMUv3 devices associated with different PCIe root complexes. Update IORT nodes accordingly. An example IORT Id mappings for a Qemu virt machine with two PCIe Root Complexes each assocaited with a SMMUv3 will be something like below, -device arm-smmuv3,primary-bus=pcie.0,id=smmuv3.0 -device arm-smmuv3,primary-bus=pcie.1,id=smmuv3.1 ... +--------------------+ +--------------------+ | Root Complex 0 | | Root Complex 1 | | | | | | Requestor IDs | | Requestor IDs | | 0x0000 - 0x00FF | | 0x0100 - 0x01FF | +---------+----------+ +---------+----------+ | | | | | Stream ID Mapping | v v +--------------------+ +--------------------+ | SMMUv3 Node 0 | | SMMUv3 Node 1 | | | | | | Stream IDs 0x0000- | | Stream IDs 0x0100- | | 0x00FF mapped from | | 0x01FF mapped from | | RC0 Requestor IDs | | RC1 Requestor IDs | +--------------------+ +--------------------+ | | | | +----------------+---------------+ | |Device ID Mapping v +----------------------------+ | ITS Node 0 | | | | Device IDs: | | 0x0000 - 0x00FF (from RC0) | | 0x0100 - 0x01FF (from RC1) | | 0x0200 - 0xFFFF (No SMMU) | +----------------------------+ Tested-by: Nathan Chen Reviewed-by: Nicolin Chen Reviewed-by: Jonathan Cameron Reviewed-by: Eric Auger Tested-by: Eric Auger Tested-by: Nicolin Chen Signed-off-by: Shameer Kolothum Signed-off-by: Shameer Kolothum Reviewed-by: Donald Dutile Message-id: 20250829082543.7680-4-skolothumtho@nvidia.com Signed-off-by: Peter Maydell --- hw/arm/virt-acpi-build.c | 64 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c index bef4fabe56..96830f7c4e 100644 --- a/hw/arm/virt-acpi-build.c +++ b/hw/arm/virt-acpi-build.c @@ -45,6 +45,7 @@ #include "hw/acpi/generic_event_device.h" #include "hw/acpi/tpm.h" #include "hw/acpi/hmat.h" +#include "hw/arm/smmuv3.h" #include "hw/cxl/cxl.h" #include "hw/pci/pcie_host.h" #include "hw/pci/pci.h" @@ -338,6 +339,67 @@ static int populate_smmuv3_legacy_dev(GArray *sdev_blob) return sdev.rc_smmu_idmaps->len; } +static int smmuv3_dev_idmap_compare(gconstpointer a, gconstpointer b) +{ + AcpiIortSMMUv3Dev *sdev_a = (AcpiIortSMMUv3Dev *)a; + AcpiIortSMMUv3Dev *sdev_b = (AcpiIortSMMUv3Dev *)b; + AcpiIortIdMapping *map_a = &g_array_index(sdev_a->rc_smmu_idmaps, + AcpiIortIdMapping, 0); + AcpiIortIdMapping *map_b = &g_array_index(sdev_b->rc_smmu_idmaps, + AcpiIortIdMapping, 0); + return map_a->input_base - map_b->input_base; +} + +static int iort_smmuv3_devices(Object *obj, void *opaque) +{ + VirtMachineState *vms = VIRT_MACHINE(qdev_get_machine()); + GArray *sdev_blob = opaque; + AcpiIortIdMapping idmap; + PlatformBusDevice *pbus; + AcpiIortSMMUv3Dev sdev; + int min_bus, max_bus; + SysBusDevice *sbdev; + PCIBus *bus; + + if (!object_dynamic_cast(obj, TYPE_ARM_SMMUV3)) { + return 0; + } + + bus = PCI_BUS(object_property_get_link(obj, "primary-bus", &error_abort)); + pbus = PLATFORM_BUS_DEVICE(vms->platform_bus_dev); + sbdev = SYS_BUS_DEVICE(obj); + sdev.base = platform_bus_get_mmio_addr(pbus, sbdev, 0); + sdev.base += vms->memmap[VIRT_PLATFORM_BUS].base; + sdev.irq = platform_bus_get_irqn(pbus, sbdev, 0); + sdev.irq += vms->irqmap[VIRT_PLATFORM_BUS]; + sdev.irq += ARM_SPI_BASE; + + pci_bus_range(bus, &min_bus, &max_bus); + sdev.rc_smmu_idmaps = g_array_new(false, true, sizeof(AcpiIortIdMapping)); + idmap.input_base = min_bus << 8, + idmap.id_count = (max_bus - min_bus + 1) << 8, + g_array_append_val(sdev.rc_smmu_idmaps, idmap); + g_array_append_val(sdev_blob, sdev); + return 0; +} + +/* + * Populate the struct AcpiIortSMMUv3Dev for all SMMUv3 devices and + * return the total number of idmaps. + */ +static int populate_smmuv3_dev(GArray *sdev_blob) +{ + object_child_foreach_recursive(object_get_root(), + iort_smmuv3_devices, sdev_blob); + /* Sort the smmuv3 devices(if any) by smmu idmap input_base */ + g_array_sort(sdev_blob, smmuv3_dev_idmap_compare); + /* + * Since each SMMUv3 dev is assocaited with specific host bridge, + * total number of idmaps equals to total number of smmuv3 devices. + */ + return sdev_blob->len; +} + /* Compute ID ranges (RIDs) from RC that are directed to the ITS Group node */ static void create_rc_its_idmaps(GArray *its_idmaps, GArray *smmuv3_devs) { @@ -401,6 +463,8 @@ build_iort(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) if (vms->legacy_smmuv3_present) { rc_smmu_idmaps_len = populate_smmuv3_legacy_dev(smmuv3_devs); + } else { + rc_smmu_idmaps_len = populate_smmuv3_dev(smmuv3_devs); } num_smmus = smmuv3_devs->len; From 7a276b7570266ec39611f9d91089741ec7e9295b Mon Sep 17 00:00:00 2001 From: Shameer Kolothum Date: Fri, 29 Aug 2025 09:25:26 +0100 Subject: [PATCH 0429/2396] hw/arm/virt: Factor out common SMMUV3 dt bindings code No functional changes intended. This will be useful when we add support for user-creatable smmuv3 device. Reviewed-by: Nicolin Chen Reviewed-by: Eric Auger Tested-by: Nathan Chen Reviewed-by: Jonathan Cameron Tested-by: Eric Auger Tested-by: Nicolin Chen Signed-off-by: Shameer Kolothum Signed-off-by: Shameer Kolothum Reviewed-by: Donald Dutile Message-id: 20250829082543.7680-5-skolothumtho@nvidia.com Signed-off-by: Peter Maydell --- hw/arm/virt.c | 54 +++++++++++++++++++++++++++------------------------ 1 file changed, 29 insertions(+), 25 deletions(-) diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 16a1ac3c2d..bebe2d8cea 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -1444,19 +1444,43 @@ static void create_pcie_irq_map(const MachineState *ms, 0x7 /* PCI irq */); } +static void create_smmuv3_dt_bindings(const VirtMachineState *vms, hwaddr base, + hwaddr size, int irq) +{ + char *node; + const char compat[] = "arm,smmu-v3"; + const char irq_names[] = "eventq\0priq\0cmdq-sync\0gerror"; + MachineState *ms = MACHINE(vms); + + node = g_strdup_printf("/smmuv3@%" PRIx64, base); + qemu_fdt_add_subnode(ms->fdt, node); + qemu_fdt_setprop(ms->fdt, node, "compatible", compat, sizeof(compat)); + qemu_fdt_setprop_sized_cells(ms->fdt, node, "reg", 2, base, 2, size); + + qemu_fdt_setprop_cells(ms->fdt, node, "interrupts", + GIC_FDT_IRQ_TYPE_SPI, irq , GIC_FDT_IRQ_FLAGS_EDGE_LO_HI, + GIC_FDT_IRQ_TYPE_SPI, irq + 1, GIC_FDT_IRQ_FLAGS_EDGE_LO_HI, + GIC_FDT_IRQ_TYPE_SPI, irq + 2, GIC_FDT_IRQ_FLAGS_EDGE_LO_HI, + GIC_FDT_IRQ_TYPE_SPI, irq + 3, GIC_FDT_IRQ_FLAGS_EDGE_LO_HI); + + qemu_fdt_setprop(ms->fdt, node, "interrupt-names", irq_names, + sizeof(irq_names)); + + qemu_fdt_setprop(ms->fdt, node, "dma-coherent", NULL, 0); + qemu_fdt_setprop_cell(ms->fdt, node, "#iommu-cells", 1); + qemu_fdt_setprop_cell(ms->fdt, node, "phandle", vms->iommu_phandle); + g_free(node); +} + static void create_smmu(const VirtMachineState *vms, PCIBus *bus) { VirtMachineClass *vmc = VIRT_MACHINE_GET_CLASS(vms); - char *node; - const char compat[] = "arm,smmu-v3"; int irq = vms->irqmap[VIRT_SMMU]; int i; hwaddr base = vms->memmap[VIRT_SMMU].base; hwaddr size = vms->memmap[VIRT_SMMU].size; - const char irq_names[] = "eventq\0priq\0cmdq-sync\0gerror"; DeviceState *dev; - MachineState *ms = MACHINE(vms); if (vms->iommu != VIRT_IOMMU_SMMUV3 || !vms->iommu_phandle) { return; @@ -1475,27 +1499,7 @@ static void create_smmu(const VirtMachineState *vms, sysbus_connect_irq(SYS_BUS_DEVICE(dev), i, qdev_get_gpio_in(vms->gic, irq + i)); } - - node = g_strdup_printf("/smmuv3@%" PRIx64, base); - qemu_fdt_add_subnode(ms->fdt, node); - qemu_fdt_setprop(ms->fdt, node, "compatible", compat, sizeof(compat)); - qemu_fdt_setprop_sized_cells(ms->fdt, node, "reg", 2, base, 2, size); - - qemu_fdt_setprop_cells(ms->fdt, node, "interrupts", - GIC_FDT_IRQ_TYPE_SPI, irq , GIC_FDT_IRQ_FLAGS_EDGE_LO_HI, - GIC_FDT_IRQ_TYPE_SPI, irq + 1, GIC_FDT_IRQ_FLAGS_EDGE_LO_HI, - GIC_FDT_IRQ_TYPE_SPI, irq + 2, GIC_FDT_IRQ_FLAGS_EDGE_LO_HI, - GIC_FDT_IRQ_TYPE_SPI, irq + 3, GIC_FDT_IRQ_FLAGS_EDGE_LO_HI); - - qemu_fdt_setprop(ms->fdt, node, "interrupt-names", irq_names, - sizeof(irq_names)); - - qemu_fdt_setprop(ms->fdt, node, "dma-coherent", NULL, 0); - - qemu_fdt_setprop_cell(ms->fdt, node, "#iommu-cells", 1); - - qemu_fdt_setprop_cell(ms->fdt, node, "phandle", vms->iommu_phandle); - g_free(node); + create_smmuv3_dt_bindings(vms, base, size, irq); } static void create_virtio_iommu_dt_bindings(VirtMachineState *vms) From 466197fc7a25658f9187d538c26887f5738d1ac9 Mon Sep 17 00:00:00 2001 From: Nicolin Chen Date: Fri, 29 Aug 2025 09:25:27 +0100 Subject: [PATCH 0430/2396] hw/arm/virt: Add an SMMU_IO_LEN macro This is useful as the subsequent support for new SMMUv3 dev will also use the same. Signed-off-by: Nicolin Chen Reviewed-by: Donald Dutile Reviewed-by: Eric Auger Tested-by: Nathan Chen Reviewed-by: Jonathan Cameron Tested-by: Eric Auger Tested-by: Nicolin Chen Signed-off-by: Shameer Kolothum Signed-off-by: Shameer Kolothum Reviewed-by: Nicolin Chen Message-id: 20250829082543.7680-6-skolothumtho@nvidia.com Signed-off-by: Peter Maydell --- hw/arm/virt.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/hw/arm/virt.c b/hw/arm/virt.c index bebe2d8cea..64b4dcf607 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -151,6 +151,9 @@ static void arm_virt_compat_set(MachineClass *mc) #define LEGACY_RAMLIMIT_GB 255 #define LEGACY_RAMLIMIT_BYTES (LEGACY_RAMLIMIT_GB * GiB) +/* MMIO region size for SMMUv3 */ +#define SMMU_IO_LEN 0x20000 + /* Addresses and sizes of our components. * 0..128MB is space for a flash device so we can run bootrom code such as UEFI. * 128MB..256MB is used for miscellaneous device I/O. @@ -182,7 +185,7 @@ static const MemMapEntry base_memmap[] = { [VIRT_FW_CFG] = { 0x09020000, 0x00000018 }, [VIRT_GPIO] = { 0x09030000, 0x00001000 }, [VIRT_UART1] = { 0x09040000, 0x00001000 }, - [VIRT_SMMU] = { 0x09050000, 0x00020000 }, + [VIRT_SMMU] = { 0x09050000, SMMU_IO_LEN }, [VIRT_PCDIMM_ACPI] = { 0x09070000, MEMORY_HOTPLUG_IO_LEN }, [VIRT_ACPI_GED] = { 0x09080000, ACPI_GED_EVT_SEL_LEN }, [VIRT_NVDIMM_ACPI] = { 0x09090000, NVDIMM_ACPI_IO_LEN}, From 951bc76fb669eab96cc60e38a50097ad4435163e Mon Sep 17 00:00:00 2001 From: Shameer Kolothum Date: Fri, 29 Aug 2025 09:25:28 +0100 Subject: [PATCH 0431/2396] hw/pci: Introduce pci_setup_iommu_per_bus() for per-bus IOMMU ops retrieval Currently, pci_setup_iommu() registers IOMMU ops for a given PCIBus. However, when retrieving IOMMU ops for a device using pci_device_get_iommu_bus_devfn(), the function checks the parent_dev and fetches IOMMU ops from the parent device, even if the current bus does not have any associated IOMMU ops. This behavior works for now because QEMU's IOMMU implementations are globally scoped, and host bridges rely on the bypass_iommu property to skip IOMMU translation when needed. However, this model will break with the soon to be introduced arm-smmuv3 device, which allows users to associate the IOMMU with a specific PCIe root complex (e.g., the default pcie.0 or a pxb-pcie root complex). For example, consider the following setup with multiple root complexes: -device arm-smmuv3,primary-bus=pcie.0,id=smmuv3.0 \ ... -device pxb-pcie,id=pcie.1,bus_nr=8,bus=pcie.0 \ -device pcie-root-port,id=pcie.port1,bus=pcie.1 \ -device virtio-net-pci,bus=pcie.port1 In Qemu, pxb-pcie acts as a special root complex whose parent is effectively the default root complex(pcie.0). Hence, though pcie.1 has no associated SMMUv3 as per above, pci_device_get_iommu_bus_devfn() will incorrectly return the IOMMU ops from pcie.0 due to the fallback via parent_dev. To fix this, introduce a new helper pci_setup_iommu_per_bus() that explicitly sets the new iommu_per_bus field in the PCIBus structure. This helper will be used in a subsequent patch that adds support for the new arm-smmuv3 device. Update pci_device_get_iommu_bus_devfn() to use iommu_per_bus when determining the correct IOMMU ops, ensuring accurate behavior for per-bus IOMMUs. Reviewed-by: Jonathan Cameron Reviewed-by: Eric Auger Tested-by: Nathan Chen Tested-by: Eric Auger Reviewed-by: Nicolin Chen Tested-by: Nicolin Chen Signed-off-by: Shameer Kolothum Signed-off-by: Shameer Kolothum Reviewed-by: Donald Dutile Message-id: 20250829082543.7680-7-skolothumtho@nvidia.com Signed-off-by: Peter Maydell --- hw/pci/pci.c | 31 +++++++++++++++++++++++++++++++ include/hw/pci/pci.h | 2 ++ include/hw/pci/pci_bus.h | 1 + 3 files changed, 34 insertions(+) diff --git a/hw/pci/pci.c b/hw/pci/pci.c index 297196b242..c3df9d6656 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -2912,6 +2912,19 @@ static void pci_device_get_iommu_bus_devfn(PCIDevice *dev, } } + /* + * When multiple PCI Express Root Buses are defined using pxb-pcie, + * the IOMMU configuration may be specific to each root bus. However, + * pxb-pcie acts as a special root complex whose parent is effectively + * the default root complex(pcie.0). Ensure that we retrieve the + * correct IOMMU ops(if any) in such cases. + */ + if (pci_bus_is_express(iommu_bus) && pci_bus_is_root(iommu_bus)) { + if (parent_bus->iommu_per_bus) { + break; + } + } + iommu_bus = parent_bus; } @@ -3172,6 +3185,24 @@ void pci_setup_iommu(PCIBus *bus, const PCIIOMMUOps *ops, void *opaque) bus->iommu_opaque = opaque; } +/* + * Similar to pci_setup_iommu(), but sets iommu_per_bus to true, + * indicating that the IOMMU is specific to this bus. This is used by + * IOMMU implementations that are tied to a specific PCIe root complex. + * + * In QEMU, pxb-pcie behaves as a special root complex whose parent is + * effectively the default root complex (pcie.0). The iommu_per_bus + * is checked in pci_device_get_iommu_bus_devfn() to ensure the correct + * IOMMU ops are returned, avoiding the use of the parent’s IOMMU when + * it's not appropriate. + */ +void pci_setup_iommu_per_bus(PCIBus *bus, const PCIIOMMUOps *ops, + void *opaque) +{ + pci_setup_iommu(bus, ops, opaque); + bus->iommu_per_bus = true; +} + static void pci_dev_get_w64(PCIBus *b, PCIDevice *dev, void *opaque) { Range *range = opaque; diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h index 6b7d3ac8a3..6bccb25ac2 100644 --- a/include/hw/pci/pci.h +++ b/include/hw/pci/pci.h @@ -773,6 +773,8 @@ int pci_iommu_unregister_iotlb_notifier(PCIDevice *dev, uint32_t pasid, */ void pci_setup_iommu(PCIBus *bus, const PCIIOMMUOps *ops, void *opaque); +void pci_setup_iommu_per_bus(PCIBus *bus, const PCIIOMMUOps *ops, void *opaque); + pcibus_t pci_bar_address(PCIDevice *d, int reg, uint8_t type, pcibus_t size); diff --git a/include/hw/pci/pci_bus.h b/include/hw/pci/pci_bus.h index 2261312546..c738446788 100644 --- a/include/hw/pci/pci_bus.h +++ b/include/hw/pci/pci_bus.h @@ -35,6 +35,7 @@ struct PCIBus { enum PCIBusFlags flags; const PCIIOMMUOps *iommu_ops; void *iommu_opaque; + bool iommu_per_bus; uint8_t devfn_min; uint32_t slot_reserved_mask; pci_set_irq_fn set_irq; From 66d2f665e163cf1afccd171e3c16f8d3acb3d94a Mon Sep 17 00:00:00 2001 From: Shameer Kolothum Date: Fri, 29 Aug 2025 09:25:29 +0100 Subject: [PATCH 0432/2396] hw/arm/virt: Allow user-creatable SMMUv3 dev instantiation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Allow cold-plugging of an SMMUv3 device on the virt machine when no global (legacy) SMMUv3 is present or when a virtio-iommu is specified. This user-created SMMUv3 device is tied to a specific PCI bus provided by the user, so ensure the IOMMU ops are configured accordingly. Due to current limitations in QEMU’s device tree support, specifically its inability to properly present pxb-pcie based root complexes and their devices, the device tree support for the new SMMUv3 device is limited to cases where it is attached to the default pcie.0 root complex. Reviewed-by: Jonathan Cameron Reviewed-by: Eric Auger Tested-by: Nathan Chen Tested-by: Eric Auger Tested-by: Nicolin Chen Signed-off-by: Shameer Kolothum Signed-off-by: Shameer Kolothum Reviewed-by: Donald Dutile Reviewed-by: Nicolin Chen Message-id: 20250829082543.7680-8-skolothumtho@nvidia.com Signed-off-by: Peter Maydell --- hw/arm/smmu-common.c | 8 +++++- hw/arm/smmuv3.c | 2 ++ hw/arm/virt.c | 51 ++++++++++++++++++++++++++++++++++++ hw/core/sysbus-fdt.c | 3 +++ include/hw/arm/smmu-common.h | 1 + 5 files changed, 64 insertions(+), 1 deletion(-) diff --git a/hw/arm/smmu-common.c b/hw/arm/smmu-common.c index 7f64ea48d0..62a7612184 100644 --- a/hw/arm/smmu-common.c +++ b/hw/arm/smmu-common.c @@ -961,7 +961,12 @@ static void smmu_base_realize(DeviceState *dev, Error **errp) goto out_err; } } - pci_setup_iommu(pci_bus, &smmu_ops, s); + + if (s->smmu_per_bus) { + pci_setup_iommu_per_bus(pci_bus, &smmu_ops, s); + } else { + pci_setup_iommu(pci_bus, &smmu_ops, s); + } return; } out_err: @@ -986,6 +991,7 @@ static void smmu_base_reset_exit(Object *obj, ResetType type) static const Property smmu_dev_properties[] = { DEFINE_PROP_UINT8("bus_num", SMMUState, bus_num, 0), + DEFINE_PROP_BOOL("smmu_per_bus", SMMUState, smmu_per_bus, false), DEFINE_PROP_LINK("primary-bus", SMMUState, primary_bus, TYPE_PCI_BUS, PCIBus *), }; diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c index ab67972353..bcf8af8dc7 100644 --- a/hw/arm/smmuv3.c +++ b/hw/arm/smmuv3.c @@ -1996,6 +1996,8 @@ static void smmuv3_class_init(ObjectClass *klass, const void *data) device_class_set_parent_realize(dc, smmu_realize, &c->parent_realize); device_class_set_props(dc, smmuv3_properties); + dc->hotpluggable = false; + dc->user_creatable = true; } static int smmuv3_notify_flag_changed(IOMMUMemoryRegion *iommu, diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 64b4dcf607..7b3f9b1cdf 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -57,6 +57,7 @@ #include "qemu/cutils.h" #include "qemu/error-report.h" #include "qemu/module.h" +#include "hw/pci/pci_bus.h" #include "hw/pci-host/gpex.h" #include "hw/pci-bridge/pci_expander_bridge.h" #include "hw/virtio/virtio-pci.h" @@ -1475,6 +1476,29 @@ static void create_smmuv3_dt_bindings(const VirtMachineState *vms, hwaddr base, g_free(node); } +static void create_smmuv3_dev_dtb(VirtMachineState *vms, + DeviceState *dev, PCIBus *bus) +{ + PlatformBusDevice *pbus = PLATFORM_BUS_DEVICE(vms->platform_bus_dev); + SysBusDevice *sbdev = SYS_BUS_DEVICE(dev); + int irq = platform_bus_get_irqn(pbus, sbdev, 0); + hwaddr base = platform_bus_get_mmio_addr(pbus, sbdev, 0); + MachineState *ms = MACHINE(vms); + + if (!(vms->bootinfo.firmware_loaded && virt_is_acpi_enabled(vms)) && + strcmp("pcie.0", bus->qbus.name)) { + warn_report("SMMUv3 device only supported with pcie.0 for DT"); + return; + } + base += vms->memmap[VIRT_PLATFORM_BUS].base; + irq += vms->irqmap[VIRT_PLATFORM_BUS]; + + vms->iommu_phandle = qemu_fdt_alloc_phandle(ms->fdt); + create_smmuv3_dt_bindings(vms, base, SMMU_IO_LEN, irq); + qemu_fdt_setprop_cells(ms->fdt, vms->pciehb_nodename, "iommu-map", + 0x0, vms->iommu_phandle, 0x0, 0x10000); +} + static void create_smmu(const VirtMachineState *vms, PCIBus *bus) { @@ -3006,6 +3030,16 @@ static void virt_machine_device_pre_plug_cb(HotplugHandler *hotplug_dev, qlist_append_str(reserved_regions, resv_prop_str); qdev_prop_set_array(dev, "reserved-regions", reserved_regions); g_free(resv_prop_str); + } else if (object_dynamic_cast(OBJECT(dev), TYPE_ARM_SMMUV3)) { + if (vms->legacy_smmuv3_present || vms->iommu == VIRT_IOMMU_VIRTIO) { + error_setg(errp, "virt machine already has %s set. " + "Doesn't support incompatible iommus", + (vms->legacy_smmuv3_present) ? + "iommu=smmuv3" : "virtio-iommu"); + } else if (vms->iommu == VIRT_IOMMU_NONE) { + /* The new SMMUv3 device is specific to the PCI bus */ + object_property_set_bool(OBJECT(dev), "smmu_per_bus", true, NULL); + } } } @@ -3029,6 +3063,22 @@ static void virt_machine_device_plug_cb(HotplugHandler *hotplug_dev, virtio_md_pci_plug(VIRTIO_MD_PCI(dev), MACHINE(hotplug_dev), errp); } + if (object_dynamic_cast(OBJECT(dev), TYPE_ARM_SMMUV3)) { + if (!vms->legacy_smmuv3_present && vms->platform_bus_dev) { + PCIBus *bus; + + bus = PCI_BUS(object_property_get_link(OBJECT(dev), "primary-bus", + &error_abort)); + if (pci_bus_bypass_iommu(bus)) { + error_setg(errp, "Bypass option cannot be set for SMMUv3 " + "associated PCIe RC"); + return; + } + + create_smmuv3_dev_dtb(vms, dev, bus); + } + } + if (object_dynamic_cast(OBJECT(dev), TYPE_VIRTIO_IOMMU_PCI)) { PCIDevice *pdev = PCI_DEVICE(dev); @@ -3231,6 +3281,7 @@ static void virt_machine_class_init(ObjectClass *oc, const void *data) machine_class_allow_dynamic_sysbus_dev(mc, TYPE_RAMFB_DEVICE); machine_class_allow_dynamic_sysbus_dev(mc, TYPE_VFIO_PLATFORM); machine_class_allow_dynamic_sysbus_dev(mc, TYPE_UEFI_VARS_SYSBUS); + machine_class_allow_dynamic_sysbus_dev(mc, TYPE_ARM_SMMUV3); #ifdef CONFIG_TPM machine_class_allow_dynamic_sysbus_dev(mc, TYPE_TPM_TIS_SYSBUS); #endif diff --git a/hw/core/sysbus-fdt.c b/hw/core/sysbus-fdt.c index c339a27875..e80776080b 100644 --- a/hw/core/sysbus-fdt.c +++ b/hw/core/sysbus-fdt.c @@ -31,6 +31,7 @@ #include "qemu/error-report.h" #include "system/device_tree.h" #include "system/tpm.h" +#include "hw/arm/smmuv3.h" #include "hw/platform-bus.h" #include "hw/vfio/vfio-platform.h" #include "hw/vfio/vfio-calxeda-xgmac.h" @@ -518,6 +519,8 @@ static const BindingEntry bindings[] = { #ifdef CONFIG_TPM TYPE_BINDING(TYPE_TPM_TIS_SYSBUS, add_tpm_tis_fdt_node), #endif + /* No generic DT support for smmuv3 dev. Support added for arm virt only */ + TYPE_BINDING(TYPE_ARM_SMMUV3, no_fdt_node), TYPE_BINDING(TYPE_RAMFB_DEVICE, no_fdt_node), TYPE_BINDING(TYPE_UEFI_VARS_SYSBUS, add_uefi_vars_node), TYPE_BINDING("", NULL), /* last element */ diff --git a/include/hw/arm/smmu-common.h b/include/hw/arm/smmu-common.h index e5e2d09294..80d0fecfde 100644 --- a/include/hw/arm/smmu-common.h +++ b/include/hw/arm/smmu-common.h @@ -161,6 +161,7 @@ struct SMMUState { QLIST_HEAD(, SMMUDevice) devices_with_notifiers; uint8_t bus_num; PCIBus *primary_bus; + bool smmu_per_bus; /* SMMU is specific to the primary_bus */ }; struct SMMUBaseClass { From 73d3d0187bc6b482d8b15116edce1475c7975b89 Mon Sep 17 00:00:00 2001 From: Shameer Kolothum Date: Fri, 29 Aug 2025 09:25:30 +0100 Subject: [PATCH 0433/2396] qemu-options.hx: Document the arm-smmuv3 device Now that arm,virt can have user-creatable smmuv3 devices, document it. Reviewed-by: Jonathan Cameron Reviewed-by: Eric Auger Tested-by: Eric Auger Tested-by: Nicolin Chen Signed-off-by: Shameer Kolothum Signed-off-by: Shameer Kolothum Reviewed-by: Donald Dutile Reviewed-by: Nicolin Chen Message-id: 20250829082543.7680-9-skolothumtho@nvidia.com Signed-off-by: Peter Maydell --- qemu-options.hx | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/qemu-options.hx b/qemu-options.hx index aa44b0e34a..075f4be2e3 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -1231,6 +1231,13 @@ SRST ``aw-bits=val`` (val between 32 and 64, default depends on machine) This decides the address width of the IOVA address space. +``-device arm-smmuv3,primary-bus=id`` + This is only supported by ``-machine virt`` (ARM). + + ``primary-bus=id`` + Accepts either the default root complex (pcie.0) or a + pxb-pcie based root complex. + ERST DEF("name", HAS_ARG, QEMU_OPTION_name, From c69520c13d6ea45a69a7a49361806fa05b19046d Mon Sep 17 00:00:00 2001 From: Shameer Kolothum Date: Fri, 29 Aug 2025 09:25:31 +0100 Subject: [PATCH 0434/2396] bios-tables-test: Allow for smmuv3 test data. The tests to be added exercise both legacy(iommu=smmuv3) and new -device arm-smmuv3,.. cases. Reviewed-by: Jonathan Cameron Reviewed-by: Eric Auger Tested-by: Eric Auger Tested-by: Nicolin Chen Signed-off-by: Shameer Kolothum Signed-off-by: Shameer Kolothum Reviewed-by: Donald Dutile Reviewed-by: Nicolin Chen Message-id: 20250829082543.7680-10-skolothumtho@nvidia.com Signed-off-by: Peter Maydell --- tests/data/acpi/aarch64/virt/DSDT.smmuv3-dev | 0 tests/data/acpi/aarch64/virt/DSDT.smmuv3-legacy | 0 tests/data/acpi/aarch64/virt/IORT.smmuv3-dev | 0 tests/data/acpi/aarch64/virt/IORT.smmuv3-legacy | 0 tests/qtest/bios-tables-test-allowed-diff.h | 4 ++++ 5 files changed, 4 insertions(+) create mode 100644 tests/data/acpi/aarch64/virt/DSDT.smmuv3-dev create mode 100644 tests/data/acpi/aarch64/virt/DSDT.smmuv3-legacy create mode 100644 tests/data/acpi/aarch64/virt/IORT.smmuv3-dev create mode 100644 tests/data/acpi/aarch64/virt/IORT.smmuv3-legacy diff --git a/tests/data/acpi/aarch64/virt/DSDT.smmuv3-dev b/tests/data/acpi/aarch64/virt/DSDT.smmuv3-dev new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/data/acpi/aarch64/virt/DSDT.smmuv3-legacy b/tests/data/acpi/aarch64/virt/DSDT.smmuv3-legacy new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/data/acpi/aarch64/virt/IORT.smmuv3-dev b/tests/data/acpi/aarch64/virt/IORT.smmuv3-dev new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/data/acpi/aarch64/virt/IORT.smmuv3-legacy b/tests/data/acpi/aarch64/virt/IORT.smmuv3-legacy new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/qtest/bios-tables-test-allowed-diff.h b/tests/qtest/bios-tables-test-allowed-diff.h index dfb8523c8b..2e3e3ccdce 100644 --- a/tests/qtest/bios-tables-test-allowed-diff.h +++ b/tests/qtest/bios-tables-test-allowed-diff.h @@ -1 +1,5 @@ /* List of comma-separated changed AML files to ignore */ +"tests/data/acpi/aarch64/virt/DSDT.smmuv3-legacy", +"tests/data/acpi/aarch64/virt/DSDT.smmuv3-dev", +"tests/data/acpi/aarch64/virt/IORT.smmuv3-legacy", +"tests/data/acpi/aarch64/virt/IORT.smmuv3-dev", From 3f8cd046c151c471d9a34181320f4a7d3f72b32a Mon Sep 17 00:00:00 2001 From: Shameer Kolothum Date: Fri, 29 Aug 2025 09:25:32 +0100 Subject: [PATCH 0435/2396] qtest/bios-tables-test: Add tests for legacy smmuv3 and smmuv3 device For the legacy SMMUv3 test, the setup includes three PCIe Root Complexes, one of which has bypass_iommu enabled. The generated IORT table contains a single SMMUv3 node, a Root Complex(RC) node and 1 ITS node. RC node features 4 ID mappings, of which 2 points to SMMU node and the remaining ones points to ITS. pcie.0 -> {SMMU0} -> {ITS} {RC} pcie.1 -> {SMMU0} -> {ITS} pcie.2 -> {ITS} [all other ids] -> {ITS} For the -device arm-smmuv3,... test, the configuration also includes three Root Complexes, with two connected to separate SMMUv3 devices. The resulting IORT table contains 1 RC node, 2 SMMU nodes and 1 ITS node. RC node features 4 ID mappings. 2 of them target the 2 SMMU nodes while the others targets the ITS. pcie.0 -> {SMMU0} -> {ITS} {RC} pcie.1 -> {SMMU1} -> {ITS} pcie.2 -> {ITS} [all other ids] -> {ITS} Reviewed-by: Jonathan Cameron Reviewed-by: Eric Auger Tested-by: Eric Auger Tested-by: Nicolin Chen Signed-off-by: Shameer Kolothum Signed-off-by: Shameer Kolothum Reviewed-by: Donald Dutile Reviewed-by: Nicolin Chen Message-id: 20250829082543.7680-11-skolothumtho@nvidia.com Signed-off-by: Peter Maydell --- tests/qtest/bios-tables-test.c | 86 ++++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) diff --git a/tests/qtest/bios-tables-test.c b/tests/qtest/bios-tables-test.c index e7e6926c81..4fa8ac5096 100644 --- a/tests/qtest/bios-tables-test.c +++ b/tests/qtest/bios-tables-test.c @@ -2337,6 +2337,86 @@ static void test_acpi_aarch64_virt_viot(void) free_test_data(&data); } +static void test_acpi_aarch64_virt_smmuv3_legacy(void) +{ + test_data data = { + .machine = "virt", + .arch = "aarch64", + .tcg_only = true, + .uefi_fl1 = "pc-bios/edk2-aarch64-code.fd", + .uefi_fl2 = "pc-bios/edk2-arm-vars.fd", + .ram_start = 0x40000000ULL, + .scan_len = 128ULL * MiB, + }; + + /* + * cdrom is plugged into scsi controller to avoid conflict + * with pxb-pcie. See comments in test_acpi_aarch64_virt_tcg_pxb() for + * details. + * + * The setup includes three PCIe root complexes, one of which has + * bypass_iommu enabled. The generated IORT table contains a single + * SMMUv3 node and a Root Complex node with three ID mappings. Two + * of the ID mappings have output references pointing to the SMMUv3 + * node and the remaining one points to ITS. + */ + data.variant = ".smmuv3-legacy"; + test_acpi_one(" -device pcie-root-port,chassis=1,id=pci.1" + " -device virtio-scsi-pci,id=scsi0,bus=pci.1" + " -drive file=" + "tests/data/uefi-boot-images/bios-tables-test.aarch64.iso.qcow2," + "if=none,media=cdrom,id=drive-scsi0-0-0-1,readonly=on" + " -device scsi-cd,bus=scsi0.0,scsi-id=0," + "drive=drive-scsi0-0-0-1,id=scsi0-0-0-1,bootindex=1" + " -cpu cortex-a57" + " -M iommu=smmuv3" + " -device pxb-pcie,id=pcie.1,bus=pcie.0,bus_nr=0x10" + " -device pxb-pcie,id=pcie.2,bus=pcie.0,bus_nr=0x20,bypass_iommu=on", + &data); + free_test_data(&data); +} + +static void test_acpi_aarch64_virt_smmuv3_dev(void) +{ + test_data data = { + .machine = "virt", + .arch = "aarch64", + .tcg_only = true, + .uefi_fl1 = "pc-bios/edk2-aarch64-code.fd", + .uefi_fl2 = "pc-bios/edk2-arm-vars.fd", + .ram_start = 0x40000000ULL, + .scan_len = 128ULL * MiB, + }; + + /* + * cdrom is plugged into scsi controller to avoid conflict + * with pxb-pcie. See comments in test_acpi_aarch64_virt_tcg_pxb() + * for details. + * + * The setup includes three PCie root complexes, two of which are + * connected to separate SMMUv3 devices. The resulting IORT table + * contains two SMMUv3 nodes and a Root Complex node with ID mappings + * of which two of the ID mappings have output references pointing + * to two different SMMUv3 nodes and the remaining ones pointing to + * ITS. + */ + data.variant = ".smmuv3-dev"; + test_acpi_one(" -device pcie-root-port,chassis=1,id=pci.1" + " -device virtio-scsi-pci,id=scsi0,bus=pci.1" + " -drive file=" + "tests/data/uefi-boot-images/bios-tables-test.aarch64.iso.qcow2," + "if=none,media=cdrom,id=drive-scsi0-0-0-1,readonly=on" + " -device scsi-cd,bus=scsi0.0,scsi-id=0," + "drive=drive-scsi0-0-0-1,id=scsi0-0-0-1,bootindex=1" + " -cpu cortex-a57" + " -device arm-smmuv3,primary-bus=pcie.0,id=smmuv3.0" + " -device pxb-pcie,id=pcie.1,bus=pcie.0,bus_nr=0x10" + " -device arm-smmuv3,primary-bus=pcie.1,id=smmuv3.1" + " -device pxb-pcie,id=pcie.2,bus=pcie.0,bus_nr=0x20", + &data); + free_test_data(&data); +} + #ifndef _WIN32 # define DEV_NULL "/dev/null" #else @@ -2768,6 +2848,12 @@ int main(int argc, char *argv[]) if (qtest_has_device("virtio-iommu-pci")) { qtest_add_func("acpi/virt/viot", test_acpi_aarch64_virt_viot); } + qtest_add_func("acpi/virt/smmuv3-legacy", + test_acpi_aarch64_virt_smmuv3_legacy); + if (qtest_has_device("arm-smmuv3")) { + qtest_add_func("acpi/virt/smmuv3-dev", + test_acpi_aarch64_virt_smmuv3_dev); + } } } else if (strcmp(arch, "riscv64") == 0) { if (has_tcg && qtest_has_device("virtio-blk-pci")) { From d35146a6606cf6ebb4e24bb97dfc0330f074f6e3 Mon Sep 17 00:00:00 2001 From: Shameer Kolothum Date: Fri, 29 Aug 2025 09:25:33 +0100 Subject: [PATCH 0436/2396] qtest/bios-tables-test: Update tables for smmuv3 tests For the legacy smmuv3 test case, generated IORT has a single SMMUv3 node, a Root Complex(RC) node and 1 ITS node. RC node features 4 ID mappings, of which 2 points to SMMU node and the remaining ones points to ITS. pcie.0 -> {SMMU0} -> {ITS} {RC} pcie.1 -> {SMMU0} -> {ITS} pcie.2 -> {ITS} [all other ids] -> {ITS} ... [030h 0048 1] Type : 00 [031h 0049 2] Length : 0018 [033h 0051 1] Revision : 01 [034h 0052 4] Identifier : 00000000 [038h 0056 4] Mapping Count : 00000000 [03Ch 0060 4] Mapping Offset : 00000000 [040h 0064 4] ItsCount : 00000001 [044h 0068 4] Identifiers : 00000000 [048h 0072 1] Type : 04 [049h 0073 2] Length : 0058 [04Bh 0075 1] Revision : 04 [04Ch 0076 4] Identifier : 00000001 [050h 0080 4] Mapping Count : 00000001 [054h 0084 4] Mapping Offset : 00000044 [058h 0088 8] Base Address : 0000000009050000 [060h 0096 4] Flags (decoded below) : 00000001 COHACC Override : 1 HTTU Override : 0 Proximity Domain Valid : 0 [064h 0100 4] Reserved : 00000000 [068h 0104 8] VATOS Address : 0000000000000000 [070h 0112 4] Model : 00000000 [074h 0116 4] Event GSIV : 0000006A [078h 0120 4] PRI GSIV : 0000006B [07Ch 0124 4] GERR GSIV : 0000006D [080h 0128 4] Sync GSIV : 0000006C [084h 0132 4] Proximity Domain : 00000000 [088h 0136 4] Device ID Mapping Index : 00000000 [08Ch 0140 4] Input base : 00000000 [090h 0144 4] ID Count : 0000FFFF [094h 0148 4] Output Base : 00000000 [098h 0152 4] Output Reference : 00000030 [09Ch 0156 4] Flags (decoded below) : 00000000 Single Mapping : 0 [0A0h 0160 1] Type : 02 [0A1h 0161 2] Length : 0074 [0A3h 0163 1] Revision : 03 [0A4h 0164 4] Identifier : 00000002 [0A8h 0168 4] Mapping Count : 00000004 [0ACh 0172 4] Mapping Offset : 00000024 [0B0h 0176 8] Memory Properties : [IORT Memory Access Properties] [0B0h 0176 4] Cache Coherency : 00000001 [0B4h 0180 1] Hints (decoded below) : 00 Transient : 0 Write Allocate : 0 Read Allocate : 0 Override : 0 [0B5h 0181 2] Reserved : 0000 [0B7h 0183 1] Memory Flags (decoded below) : 03 Coherency : 1 Device Attribute : 1 [0B8h 0184 4] ATS Attribute : 00000000 [0BCh 0188 4] PCI Segment Number : 00000000 [0C0h 0192 1] Memory Size Limit : 40 [0C1h 0193 2] PASID Capabilities : 0000 [0C3h 0195 1] Reserved : 00 [0C4h 0196 4] Input base : 00000000 [0C8h 0200 4] ID Count : 000001FF [0CCh 0204 4] Output Base : 00000000 [0D0h 0208 4] Output Reference : 00000048 [0D4h 0212 4] Flags (decoded below) : 00000000 Single Mapping : 0 [0D8h 0216 4] Input base : 00001000 [0DCh 0220 4] ID Count : 000000FF [0E0h 0224 4] Output Base : 00001000 [0E4h 0228 4] Output Reference : 00000048 [0E8h 0232 4] Flags (decoded below) : 00000000 Single Mapping : 0 [0ECh 0236 4] Input base : 00000200 [0F0h 0240 4] ID Count : 00000DFF [0F4h 0244 4] Output Base : 00000200 [0F8h 0248 4] Output Reference : 00000030 [0FCh 0252 4] Flags (decoded below) : 00000000 Single Mapping : 0 [100h 0256 4] Input base : 00001100 [104h 0260 4] ID Count : 0000EEFF [108h 0264 4] Output Base : 00001100 [10Ch 0268 4] Output Reference : 00000030 [110h 0272 4] Flags (decoded below) : 00000000 Single Mapping : 0 For the smmuv3-dev test case, IORT has 2 SMMUV3 nodes, 1 RC node and 1 ITS node. RC node features 4 ID mappings. 2 of them target the 2 SMMU nodes while the others targets the ITS. pcie.0 -> {SMMU0} -> {ITS} {RC} pcie.1 -> {SMMU1} -> {ITS} pcie.2 -> {ITS} [all other ids] -> {ITS} ... [030h 0048 1] Type : 00 [031h 0049 2] Length : 0018 [033h 0051 1] Revision : 01 [034h 0052 4] Identifier : 00000000 [038h 0056 4] Mapping Count : 00000000 [03Ch 0060 4] Mapping Offset : 00000000 [040h 0064 4] ItsCount : 00000001 [044h 0068 4] Identifiers : 00000000 [048h 0072 1] Type : 04 [049h 0073 2] Length : 0058 [04Bh 0075 1] Revision : 04 [04Ch 0076 4] Identifier : 00000001 [050h 0080 4] Mapping Count : 00000001 [054h 0084 4] Mapping Offset : 00000044 [058h 0088 8] Base Address : 000000000C000000 [060h 0096 4] Flags (decoded below) : 00000001 COHACC Override : 1 HTTU Override : 0 Proximity Domain Valid : 0 [064h 0100 4] Reserved : 00000000 [068h 0104 8] VATOS Address : 0000000000000000 [070h 0112 4] Model : 00000000 [074h 0116 4] Event GSIV : 00000090 [078h 0120 4] PRI GSIV : 00000091 [07Ch 0124 4] GERR GSIV : 00000093 [080h 0128 4] Sync GSIV : 00000092 [084h 0132 4] Proximity Domain : 00000000 [088h 0136 4] Device ID Mapping Index : 00000000 [08Ch 0140 4] Input base : 00000000 [090h 0144 4] ID Count : 0000FFFF [094h 0148 4] Output Base : 00000000 [098h 0152 4] Output Reference : 00000030 [09Ch 0156 4] Flags (decoded below) : 00000000 Single Mapping : 0 [0A0h 0160 1] Type : 04 [0A1h 0161 2] Length : 0058 [0A3h 0163 1] Revision : 04 [0A4h 0164 4] Identifier : 00000002 [0A8h 0168 4] Mapping Count : 00000001 [0ACh 0172 4] Mapping Offset : 00000044 [0B0h 0176 8] Base Address : 000000000C020000 [0B8h 0184 4] Flags (decoded below) : 00000001 COHACC Override : 1 HTTU Override : 0 Proximity Domain Valid : 0 [0BCh 0188 4] Reserved : 00000000 [0C0h 0192 8] VATOS Address : 0000000000000000 [0C8h 0200 4] Model : 00000000 [0CCh 0204 4] Event GSIV : 00000094 [0D0h 0208 4] PRI GSIV : 00000095 [0D4h 0212 4] GERR GSIV : 00000097 [0D8h 0216 4] Sync GSIV : 00000096 [0DCh 0220 4] Proximity Domain : 00000000 [0E0h 0224 4] Device ID Mapping Index : 00000000 [0E4h 0228 4] Input base : 00000000 [0E8h 0232 4] ID Count : 0000FFFF [0ECh 0236 4] Output Base : 00000000 [0F0h 0240 4] Output Reference : 00000030 [0F4h 0244 4] Flags (decoded below) : 00000000 Single Mapping : 0 [0F8h 0248 1] Type : 02 [0F9h 0249 2] Length : 0074 [0FBh 0251 1] Revision : 03 [0FCh 0252 4] Identifier : 00000003 [100h 0256 4] Mapping Count : 00000004 [104h 0260 4] Mapping Offset : 00000024 [108h 0264 8] Memory Properties : [IORT Memory Access Properties] [108h 0264 4] Cache Coherency : 00000001 [10Ch 0268 1] Hints (decoded below) : 00 Transient : 0 Write Allocate : 0 Read Allocate : 0 Override : 0 [10Dh 0269 2] Reserved : 0000 [10Fh 0271 1] Memory Flags (decoded below) : 03 Coherency : 1 Device Attribute : 1 [110h 0272 4] ATS Attribute : 00000000 [114h 0276 4] PCI Segment Number : 00000000 [118h 0280 1] Memory Size Limit : 40 [119h 0281 2] PASID Capabilities : 0000 [11Bh 0283 1] Reserved : 00 [11Ch 0284 4] Input base : 00000000 [120h 0288 4] ID Count : 000001FF [124h 0292 4] Output Base : 00000000 [128h 0296 4] Output Reference : 00000048 [12Ch 0300 4] Flags (decoded below) : 00000000 Single Mapping : 0 [130h 0304 4] Input base : 00001000 [134h 0308 4] ID Count : 000000FF [138h 0312 4] Output Base : 00001000 [13Ch 0316 4] Output Reference : 000000A0 [140h 0320 4] Flags (decoded below) : 00000000 Single Mapping : 0 [144h 0324 4] Input base : 00000200 [148h 0328 4] ID Count : 00000DFF [14Ch 0332 4] Output Base : 00000200 [150h 0336 4] Output Reference : 00000030 [154h 0340 4] Flags (decoded below) : 00000000 Single Mapping : 0 [158h 0344 4] Input base : 00001100 [15Ch 0348 4] ID Count : 0000EEFF [160h 0352 4] Output Base : 00001100 [164h 0356 4] Output Reference : 00000030 [168h 0360 4] Flags (decoded below) : 00000000 Single Mapping : 0 Note: DSDT changes are not described here as it is not impacted by the way the SMMUv3 is instantiated. Reviewed-by: Jonathan Cameron Reviewed-by: Eric Auger Tested-by: Eric Auger Tested-by: Nicolin Chen Signed-off-by: Shameer Kolothum Signed-off-by: Shameer Kolothum Reviewed-by: Donald Dutile Reviewed-by: Nicolin Chen Message-id: 20250829082543.7680-12-skolothumtho@nvidia.com Signed-off-by: Peter Maydell --- tests/data/acpi/aarch64/virt/DSDT.smmuv3-dev | Bin 0 -> 10230 bytes tests/data/acpi/aarch64/virt/DSDT.smmuv3-legacy | Bin 0 -> 10230 bytes tests/data/acpi/aarch64/virt/IORT.smmuv3-dev | Bin 0 -> 364 bytes tests/data/acpi/aarch64/virt/IORT.smmuv3-legacy | Bin 0 -> 276 bytes tests/qtest/bios-tables-test-allowed-diff.h | 4 ---- 5 files changed, 4 deletions(-) diff --git a/tests/data/acpi/aarch64/virt/DSDT.smmuv3-dev b/tests/data/acpi/aarch64/virt/DSDT.smmuv3-dev index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..53d4c07f423886d8c4b57f1da6498eef5a08b556 100644 GIT binary patch literal 10230 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 E0Qi|ec>n+a literal 0 HcmV?d00001 diff --git a/tests/data/acpi/aarch64/virt/DSDT.smmuv3-legacy b/tests/data/acpi/aarch64/virt/DSDT.smmuv3-legacy index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..53d4c07f423886d8c4b57f1da6498eef5a08b556 100644 GIT binary patch literal 10230 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 E0Qi|ec>n+a literal 0 HcmV?d00001 diff --git a/tests/data/acpi/aarch64/virt/IORT.smmuv3-dev b/tests/data/acpi/aarch64/virt/IORT.smmuv3-dev index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..67be268f62afbf2d9459540984da5e9340afdaaa 100644 GIT binary patch literal 364 zcmebD4+_a)WME)E<>c?|5v<@85#X$#prF9Wz`y`vgJ>281_lELD3D+P2|+mwa1KiZ z0}Dt3gk2!AJRl0h2hqrQ0s{lXL&RE)!03 zr!X)uOl4qTn9jh!Fb!f1oJ2R5sf2-xC9F#LbVz`!7g G5CZ^ox)K2Z literal 0 HcmV?d00001 diff --git a/tests/qtest/bios-tables-test-allowed-diff.h b/tests/qtest/bios-tables-test-allowed-diff.h index 2e3e3ccdce..dfb8523c8b 100644 --- a/tests/qtest/bios-tables-test-allowed-diff.h +++ b/tests/qtest/bios-tables-test-allowed-diff.h @@ -1,5 +1 @@ /* List of comma-separated changed AML files to ignore */ -"tests/data/acpi/aarch64/virt/DSDT.smmuv3-legacy", -"tests/data/acpi/aarch64/virt/DSDT.smmuv3-dev", -"tests/data/acpi/aarch64/virt/IORT.smmuv3-legacy", -"tests/data/acpi/aarch64/virt/IORT.smmuv3-dev", From aaf042299acf83919862c7d7dd5fc36acf4e0671 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Graber?= Date: Thu, 11 Sep 2025 13:14:15 +0100 Subject: [PATCH 0437/2396] hw/usb/network: Remove hardcoded 0x40 prefix in STRING_ETHADDR response MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit USB NICs have a "40:" prefix hardcoded for all MAC addresses when we return the guest the MAC address if it queries the STRING_ETHADDR USB string property. This doesn't match what we use for the OID_802_3_PERMANENT_ADDRESS or OID_802_3_CURRENT_ADDRESS OIDs for NDIS, or the MAC address we actually use in the QEMU networking code to send/receive packets for this device, or the NIC info string we print for users. In all those other places we directly use s->conf.macaddr.a, which is the full thing the user asks for. This overrides user-provided configuration and leads to an inconsistent experience. I couldn't find any documented reason (comment or git commits) for this behavior. It seems like everyone is just expecting the MAC address to be fully passed through to the guest, but it isn't. This may have been a debugging hack that accidentally made it through to the accepted patch: it has been in the code since it was originally added back in 2008. This is also particularly problematic as the "40:" prefix isn't a reserved prefix for MAC addresses (IEEE OUI). There are a number of valid allocations out there which use this prefix, meaning that QEMU may be causing MAC address conflicts. Cc: qemu-stable@nongnu.org Fixes: 6c9f886ceae5b ("Add CDC-Ethernet usb NIC (original patch from Thomas Sailer)" Signed-off-by: Stéphane Graber Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2951 Reviewed-by: Daniel P. Berrangé [PMM: beef up commit message based on mailing list discussion] Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/usb/dev-network.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/usb/dev-network.c b/hw/usb/dev-network.c index 81cc09dcac..1df2454181 100644 --- a/hw/usb/dev-network.c +++ b/hw/usb/dev-network.c @@ -1383,7 +1383,7 @@ static void usb_net_realize(USBDevice *dev, Error **errp) qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); snprintf(s->usbstring_mac, sizeof(s->usbstring_mac), "%02x%02x%02x%02x%02x%02x", - 0x40, + s->conf.macaddr.a[0], s->conf.macaddr.a[1], s->conf.macaddr.a[2], s->conf.macaddr.a[3], From aa3c761c7095476acde9b7140cc1dfff2ee0e170 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 15 Sep 2025 11:48:03 -0700 Subject: [PATCH 0438/2396] tests/functional/x86_64: Accept a few locked pages in test_memlock.py Startup of libgcrypt locks a small pool of pages -- by default 16k. Testing for zero locked pages is isn't correct, while testing for 32k is a decent compromise. Reviewed-by: Thomas Huth Signed-off-by: Richard Henderson --- tests/functional/x86_64/test_memlock.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/functional/x86_64/test_memlock.py b/tests/functional/x86_64/test_memlock.py index 2b515ff979..81bce80b0c 100755 --- a/tests/functional/x86_64/test_memlock.py +++ b/tests/functional/x86_64/test_memlock.py @@ -37,7 +37,8 @@ class MemlockTest(QemuSystemTest): status = self.get_process_status_values(self.vm.get_pid()) - self.assertTrue(status['VmLck'] == 0) + # libgcrypt may mlock a few pages + self.assertTrue(status['VmLck'] < 32) def test_memlock_on(self): self.common_vm_setup_with_memlock('on') From a11d1847d5ef8a7db58e6d4e44f36fec708f0981 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Mon, 8 Sep 2025 15:19:11 +0100 Subject: [PATCH 0439/2396] .gitmodules: move u-boot mirrors to qemu-project-mirrors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To continue our GitLab Open Source Program license we need to pass an automated license check for all repos under qemu-project. While U-Boot is clearly GPLv2 rather than fight with the automated validation script just move the mirror across to a separate project. Signed-off-by: Alex Bennée Suggested-by: Daniel P. Berrangé Cc: qemu-stable@nongnu.org Reviewed-by: Daniel P. Berrangé Signed-off-by: Richard Henderson Message-ID: <20250908141911.2546063-1-alex.bennee@linaro.org> Signed-off-by: Richard Henderson --- .gitmodules | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitmodules b/.gitmodules index 73cae4cd4d..e27dfe8c2c 100644 --- a/.gitmodules +++ b/.gitmodules @@ -15,7 +15,7 @@ url = https://gitlab.com/qemu-project/qemu-palcode.git [submodule "roms/u-boot"] path = roms/u-boot - url = https://gitlab.com/qemu-project/u-boot.git + url = https://gitlab.com/qemu-project-mirrors/u-boot.git [submodule "roms/skiboot"] path = roms/skiboot url = https://gitlab.com/qemu-project/skiboot.git @@ -27,7 +27,7 @@ url = https://gitlab.com/qemu-project/seabios-hppa.git [submodule "roms/u-boot-sam460ex"] path = roms/u-boot-sam460ex - url = https://gitlab.com/qemu-project/u-boot-sam460ex.git + url = https://gitlab.com/qemu-project-mirrors/u-boot-sam460ex.git [submodule "roms/edk2"] path = roms/edk2 url = https://gitlab.com/qemu-project/edk2.git From 4b948222c0801ddd428ca2e4260181ba35b219a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 16 Sep 2025 09:16:31 +0100 Subject: [PATCH 0440/2396] checkpatch: cull trailing '*/' in SPDX check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sometimes SPDX expressions appear inside C comments, and this confuses checkpatch.pl. Drop the closing C comment characters to avoid this. Reviewed-by: Stefan Hajnoczi Signed-off-by: Daniel P. Berrangé Message-id: 20250916081638.764020-2-berrange@redhat.com Signed-off-by: Stefan Hajnoczi --- scripts/checkpatch.pl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index 833f20f555..91616c974f 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -1368,6 +1368,9 @@ sub checkspdx { $expr =~ s/^\s*//g; $expr =~ s/\s*$//g; + # Cull C comment end + $expr =~ s/\*\/.*//; + my @bits = split / +/, $expr; my $prefer = "GPL-2.0-or-later"; From acf7882d194562983fd7a85df5d4b99d15c81766 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 16 Sep 2025 09:16:32 +0100 Subject: [PATCH 0441/2396] tracetool: eliminate trailing whitespace in C format MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Stefan Hajnoczi Signed-off-by: Daniel P. Berrangé Message-id: 20250916081638.764020-3-berrange@redhat.com Signed-off-by: Stefan Hajnoczi --- scripts/tracetool/format/c.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/tracetool/format/c.py b/scripts/tracetool/format/c.py index 69edf0d588..7aa51cd41a 100644 --- a/scripts/tracetool/format/c.py +++ b/scripts/tracetool/format/c.py @@ -36,7 +36,7 @@ def generate(events, backend, group): ' .id = 0,', ' .name = \"%(name)s\",', ' .sstate = %(sstate)s,', - ' .dstate = &%(dstate)s ', + ' .dstate = &%(dstate)s', '};', event = e.api(e.QEMU_EVENT), name = e.name, From 69e22a5e20518fbb68b892b2189d525cf9d90a3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 16 Sep 2025 09:16:33 +0100 Subject: [PATCH 0442/2396] tracetool: avoid space after "*" in arg types MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit QEMU code style is to have no whitespace between "*" and the arg name. Since generated trace code will soon be added to git, make it comply with code style. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Stefan Hajnoczi Signed-off-by: Daniel P. Berrangé Message-id: 20250916081638.764020-4-berrange@redhat.com Signed-off-by: Stefan Hajnoczi --- scripts/tracetool/__init__.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/scripts/tracetool/__init__.py b/scripts/tracetool/__init__.py index 2ae2e562d6..0f33758870 100644 --- a/scripts/tracetool/__init__.py +++ b/scripts/tracetool/__init__.py @@ -170,10 +170,16 @@ class Arguments: def __str__(self): """String suitable for declaring function arguments.""" + def onearg(t, n): + if t[-1] == '*': + return "".join([t, n]) + else: + return " ".join([t, n]) + if len(self._args) == 0: return "void" else: - return ", ".join([ " ".join([t, n]) for t,n in self._args ]) + return ", ".join([ onearg(t, n) for t,n in self._args ]) def __repr__(self): """Evaluable string representation for this object.""" From 52d1ec2929e5f43fe650bdbc85653e05f0aa9ffc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 16 Sep 2025 09:16:34 +0100 Subject: [PATCH 0443/2396] tracetool: include SPDX-License-Identifier in generated files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While these files are auto-generated, a later commit will add reference output to git, so having SPDX-License-Identifier is desirable. Reviewed-by: Philippe Mathieu-Daudé Acked-by: Thomas Huth Reviewed-by: Stefan Hajnoczi Signed-off-by: Daniel P. Berrangé Message-id: 20250916081638.764020-5-berrange@redhat.com Signed-off-by: Stefan Hajnoczi --- scripts/tracetool/format/c.py | 1 + scripts/tracetool/format/d.py | 3 ++- scripts/tracetool/format/h.py | 1 + scripts/tracetool/format/log_stap.py | 1 + scripts/tracetool/format/simpletrace_stap.py | 1 + scripts/tracetool/format/stap.py | 1 + scripts/tracetool/format/ust_events_c.py | 1 + scripts/tracetool/format/ust_events_h.py | 1 + 8 files changed, 9 insertions(+), 1 deletion(-) diff --git a/scripts/tracetool/format/c.py b/scripts/tracetool/format/c.py index 7aa51cd41a..e473fb6c6e 100644 --- a/scripts/tracetool/format/c.py +++ b/scripts/tracetool/format/c.py @@ -22,6 +22,7 @@ def generate(events, backend, group): header = "trace-" + group + ".h" out('/* This file is autogenerated by tracetool, do not edit. */', + '/* SPDX-License-Identifier: GPL-2.0-or-later */', '', '#include "qemu/osdep.h"', '#include "qemu/module.h"', diff --git a/scripts/tracetool/format/d.py b/scripts/tracetool/format/d.py index ebfb714200..a5e096e214 100644 --- a/scripts/tracetool/format/d.py +++ b/scripts/tracetool/format/d.py @@ -39,7 +39,8 @@ def generate(events, backend, group): if not events and platform != "darwin": return - out('/* This file is autogenerated by tracetool, do not edit. */' + out('/* This file is autogenerated by tracetool, do not edit. */', + '/* SPDX-License-Identifier: GPL-2.0-or-later */', '', 'provider qemu {') diff --git a/scripts/tracetool/format/h.py b/scripts/tracetool/format/h.py index ea126b07ea..a00ae475f7 100644 --- a/scripts/tracetool/format/h.py +++ b/scripts/tracetool/format/h.py @@ -19,6 +19,7 @@ def generate(events, backend, group): header = "trace/control.h" out('/* This file is autogenerated by tracetool, do not edit. */', + '/* SPDX-License-Identifier: GPL-2.0-or-later */', '', '#ifndef TRACE_%s_GENERATED_TRACERS_H' % group.upper(), '#define TRACE_%s_GENERATED_TRACERS_H' % group.upper(), diff --git a/scripts/tracetool/format/log_stap.py b/scripts/tracetool/format/log_stap.py index b49afababd..710d62bffe 100644 --- a/scripts/tracetool/format/log_stap.py +++ b/scripts/tracetool/format/log_stap.py @@ -88,6 +88,7 @@ def c_fmt_to_stap(fmt): def generate(events, backend, group): out('/* This file is autogenerated by tracetool, do not edit. */', + '/* SPDX-License-Identifier: GPL-2.0-or-later */', '') for event_id, e in enumerate(events): diff --git a/scripts/tracetool/format/simpletrace_stap.py b/scripts/tracetool/format/simpletrace_stap.py index 4f4633b4e6..72971133bf 100644 --- a/scripts/tracetool/format/simpletrace_stap.py +++ b/scripts/tracetool/format/simpletrace_stap.py @@ -22,6 +22,7 @@ def global_var_name(name): def generate(events, backend, group): out('/* This file is autogenerated by tracetool, do not edit. */', + '/* SPDX-License-Identifier: GPL-2.0-or-later */', '') for event_id, e in enumerate(events): diff --git a/scripts/tracetool/format/stap.py b/scripts/tracetool/format/stap.py index a218b0445c..4d77fbc11a 100644 --- a/scripts/tracetool/format/stap.py +++ b/scripts/tracetool/format/stap.py @@ -38,6 +38,7 @@ def generate(events, backend, group): if "disable" not in e.properties] out('/* This file is autogenerated by tracetool, do not edit. */', + '/* SPDX-License-Identifier: GPL-2.0-or-later */', '') for e in events: diff --git a/scripts/tracetool/format/ust_events_c.py b/scripts/tracetool/format/ust_events_c.py index deced9533d..569754a304 100644 --- a/scripts/tracetool/format/ust_events_c.py +++ b/scripts/tracetool/format/ust_events_c.py @@ -20,6 +20,7 @@ def generate(events, backend, group): if "disabled" not in e.properties] out('/* This file is autogenerated by tracetool, do not edit. */', + '/* SPDX-License-Identifier: GPL-2.0-or-later */', '', '#include "qemu/osdep.h"', '', diff --git a/scripts/tracetool/format/ust_events_h.py b/scripts/tracetool/format/ust_events_h.py index b99fe6896b..2a31fefeca 100644 --- a/scripts/tracetool/format/ust_events_h.py +++ b/scripts/tracetool/format/ust_events_h.py @@ -25,6 +25,7 @@ def generate(events, backend, group): include = "trace-ust.h" out('/* This file is autogenerated by tracetool, do not edit. */', + '/* SPDX-License-Identifier: GPL-2.0-or-later */', '', '#undef TRACEPOINT_PROVIDER', '#define TRACEPOINT_PROVIDER qemu', From da949d495ddd4d36f2c9750eb4d70f4135018199 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 16 Sep 2025 09:16:35 +0100 Subject: [PATCH 0444/2396] tracetool: add test suite for tracetool with reference output MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When reviewing tracetool patches it is often very unclear what the expected output will be for the generated backends. Compounding this is that a default build will only enable the 'log' trace backend, so developers won't see generated code for other backends without making a special effort. Some backends are also platform specific, so can't be enabled in QEMU builds, even though tracetool could generate the code. To address this, introduce a test suite for tracetool which is conceptually similar to the qapi-schema test. It is a simple python program that runs tracetool and compares the actual output to historical reference output kept in git. The test directly emits TAP format logs for ease of integration with meson. This can be run with make check-tracetool to make it easier for developers changing generated output, the sample expected content can be auto-recreated QEMU_TEST_REGENERATE=1 make check-tracetool and the changes reviewed and added to the commit. This will also assist reviewers interpreting the change. Developers are reminded of this in the test output on failure: $ make check-tracetool 1/6 qemu:tracetool / dtrace OK 0.14s 2/6 qemu:tracetool / ftrace FAIL 0.06s exit status 1 ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― 1..2 ok 1 - ftrace.c # not ok 1 - ftrace.h (set QEMU_TEST_REGENERATE=1 to recreate reference output if tracetool generator was intentionally changed) ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― 3/6 qemu:tracetool / log OK 0.06s 4/6 qemu:tracetool / simple OK 0.06s 5/6 qemu:tracetool / syslog OK 0.06s 6/6 qemu:tracetool / ust OK 0.11s Summary of Failures: 2/6 qemu:tracetool / ftrace FAIL 0.06s exit status 1 Signed-off-by: Daniel P. Berrangé Message-id: 20250916081638.764020-6-berrange@redhat.com Signed-off-by: Stefan Hajnoczi --- MAINTAINERS | 1 + docs/devel/testing/main.rst | 28 +++++++ tests/Makefile.include | 1 + tests/meson.build | 1 + tests/tracetool/dtrace.c | 32 ++++++++ tests/tracetool/dtrace.d | 10 +++ tests/tracetool/dtrace.h | 59 +++++++++++++ tests/tracetool/dtrace.log-stap | 15 ++++ tests/tracetool/dtrace.simpletrace-stap | 16 ++++ tests/tracetool/dtrace.stap | 14 ++++ tests/tracetool/ftrace.c | 32 ++++++++ tests/tracetool/ftrace.h | 73 ++++++++++++++++ tests/tracetool/log.c | 32 ++++++++ tests/tracetool/log.h | 57 +++++++++++++ tests/tracetool/meson.build | 28 +++++++ tests/tracetool/simple.c | 61 ++++++++++++++ tests/tracetool/simple.h | 54 ++++++++++++ tests/tracetool/syslog.c | 32 ++++++++ tests/tracetool/syslog.h | 57 +++++++++++++ tests/tracetool/trace-events | 5 ++ tests/tracetool/tracetool-test.py | 105 ++++++++++++++++++++++++ tests/tracetool/ust.c | 32 ++++++++ tests/tracetool/ust.h | 55 +++++++++++++ tests/tracetool/ust.ust-events-c | 14 ++++ tests/tracetool/ust.ust-events-h | 56 +++++++++++++ 25 files changed, 870 insertions(+) create mode 100644 tests/tracetool/dtrace.c create mode 100644 tests/tracetool/dtrace.d create mode 100644 tests/tracetool/dtrace.h create mode 100644 tests/tracetool/dtrace.log-stap create mode 100644 tests/tracetool/dtrace.simpletrace-stap create mode 100644 tests/tracetool/dtrace.stap create mode 100644 tests/tracetool/ftrace.c create mode 100644 tests/tracetool/ftrace.h create mode 100644 tests/tracetool/log.c create mode 100644 tests/tracetool/log.h create mode 100644 tests/tracetool/meson.build create mode 100644 tests/tracetool/simple.c create mode 100644 tests/tracetool/simple.h create mode 100644 tests/tracetool/syslog.c create mode 100644 tests/tracetool/syslog.h create mode 100644 tests/tracetool/trace-events create mode 100755 tests/tracetool/tracetool-test.py create mode 100644 tests/tracetool/ust.c create mode 100644 tests/tracetool/ust.h create mode 100644 tests/tracetool/ust.ust-events-c create mode 100644 tests/tracetool/ust.ust-events-h diff --git a/MAINTAINERS b/MAINTAINERS index fb045388b9..f8cd513d8b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3562,6 +3562,7 @@ F: scripts/tracetool/ F: scripts/qemu-trace-stap* F: docs/tools/qemu-trace-stap.rst F: docs/devel/tracing.rst +F: tests/tracetool/ T: git https://github.com/stefanha/qemu.git tracing Simpletrace diff --git a/docs/devel/testing/main.rst b/docs/devel/testing/main.rst index 2b5cb0c148..11f05c0006 100644 --- a/docs/devel/testing/main.rst +++ b/docs/devel/testing/main.rst @@ -178,6 +178,34 @@ parser (either fixing a bug or extending/modifying the syntax). To do this: ``qapi-schema += foo.json`` +.. _tracetool-tests: + +Tracetool tests +~~~~~~~~~~~~~~~ + +The tracetool tests validate the generated source files used for defining +probes for various tracing backends and source formats. The test operates +by running the tracetool program against a sample trace-events file, and +comparing the generated output against known good reference output. The +tests can be run with: + +.. code:: + + make check-tracetool + +The reference output is stored in files under tests/tracetool, and when +the tracetool backend/format output is intentionally changed, the reference +files need to be updated. This can be automated by setting the +QEMU_TEST_REGENERATE=1 environment variable: + +.. code:: + + QEMU_TEST_REGENERATE=1 make check-tracetool + +The resulting changes must be reviewed by the author to ensure they match +the intended results, before adding the updated reference output to the +same commit that alters the generator code. + check-block ~~~~~~~~~~~ diff --git a/tests/Makefile.include b/tests/Makefile.include index 23fb722d42..3538c0c740 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -13,6 +13,7 @@ check-help: @echo " $(MAKE) check-functional-TARGET Run functional tests for a given target" @echo " $(MAKE) check-unit Run qobject tests" @echo " $(MAKE) check-qapi-schema Run QAPI schema tests" + @echo " $(MAKE) check-tracetool Run tracetool generator tests" @echo " $(MAKE) check-block Run block tests" ifneq ($(filter $(all-check-targets), check-softfloat),) @echo " $(MAKE) check-tcg Run TCG tests" diff --git a/tests/meson.build b/tests/meson.build index c59619220f..cbe7916241 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -88,3 +88,4 @@ subdir('qapi-schema') subdir('qtest') subdir('migration-stress') subdir('functional') +subdir('tracetool') diff --git a/tests/tracetool/dtrace.c b/tests/tracetool/dtrace.c new file mode 100644 index 0000000000..9f862fa14d --- /dev/null +++ b/tests/tracetool/dtrace.c @@ -0,0 +1,32 @@ +/* This file is autogenerated by tracetool, do not edit. */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "qemu/osdep.h" +#include "qemu/module.h" +#include "trace-testsuite.h" + +uint16_t _TRACE_TEST_BLAH_DSTATE; +uint16_t _TRACE_TEST_WIBBLE_DSTATE; +TraceEvent _TRACE_TEST_BLAH_EVENT = { + .id = 0, + .name = "test_blah", + .sstate = TRACE_TEST_BLAH_ENABLED, + .dstate = &_TRACE_TEST_BLAH_DSTATE +}; +TraceEvent _TRACE_TEST_WIBBLE_EVENT = { + .id = 0, + .name = "test_wibble", + .sstate = TRACE_TEST_WIBBLE_ENABLED, + .dstate = &_TRACE_TEST_WIBBLE_DSTATE +}; +TraceEvent *testsuite_trace_events[] = { + &_TRACE_TEST_BLAH_EVENT, + &_TRACE_TEST_WIBBLE_EVENT, + NULL, +}; + +static void trace_testsuite_register_events(void) +{ + trace_event_register_group(testsuite_trace_events); +} +trace_init(trace_testsuite_register_events) diff --git a/tests/tracetool/dtrace.d b/tests/tracetool/dtrace.d new file mode 100644 index 0000000000..5cc06f9f4f --- /dev/null +++ b/tests/tracetool/dtrace.d @@ -0,0 +1,10 @@ +/* This file is autogenerated by tracetool, do not edit. */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +provider qemu { + +probe test_blah(void * context,const char * filename); + +probe test_wibble(void * context,int value); + +}; diff --git a/tests/tracetool/dtrace.h b/tests/tracetool/dtrace.h new file mode 100644 index 0000000000..c2e5110672 --- /dev/null +++ b/tests/tracetool/dtrace.h @@ -0,0 +1,59 @@ +/* This file is autogenerated by tracetool, do not edit. */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef TRACE_TESTSUITE_GENERATED_TRACERS_H +#define TRACE_TESTSUITE_GENERATED_TRACERS_H + +#include "trace/control.h" + +extern TraceEvent _TRACE_TEST_BLAH_EVENT; +extern TraceEvent _TRACE_TEST_WIBBLE_EVENT; +extern uint16_t _TRACE_TEST_BLAH_DSTATE; +extern uint16_t _TRACE_TEST_WIBBLE_DSTATE; +#define TRACE_TEST_BLAH_ENABLED 1 +#define TRACE_TEST_WIBBLE_ENABLED 1 +#ifndef SDT_USE_VARIADIC +#define SDT_USE_VARIADIC 1 +#endif +#include "trace-dtrace-testsuite.h" + +#undef SDT_USE_VARIADIC +#ifndef QEMU_TEST_BLAH_ENABLED +#define QEMU_TEST_BLAH_ENABLED() true +#endif +#ifndef QEMU_TEST_WIBBLE_ENABLED +#define QEMU_TEST_WIBBLE_ENABLED() true +#endif + +#define TRACE_TEST_BLAH_BACKEND_DSTATE() ( \ + QEMU_TEST_BLAH_ENABLED() || \ + false) + +static inline void _nocheck__trace_test_blah(void *context, const char *filename) +{ + QEMU_TEST_BLAH(context, filename); +} + +static inline void trace_test_blah(void *context, const char *filename) +{ + if (true) { + _nocheck__trace_test_blah(context, filename); + } +} + +#define TRACE_TEST_WIBBLE_BACKEND_DSTATE() ( \ + QEMU_TEST_WIBBLE_ENABLED() || \ + false) + +static inline void _nocheck__trace_test_wibble(void *context, int value) +{ + QEMU_TEST_WIBBLE(context, value); +} + +static inline void trace_test_wibble(void *context, int value) +{ + if (true) { + _nocheck__trace_test_wibble(context, value); + } +} +#endif /* TRACE_TESTSUITE_GENERATED_TRACERS_H */ diff --git a/tests/tracetool/dtrace.log-stap b/tests/tracetool/dtrace.log-stap new file mode 100644 index 0000000000..092986e0b6 --- /dev/null +++ b/tests/tracetool/dtrace.log-stap @@ -0,0 +1,15 @@ +/* This file is autogenerated by tracetool, do not edit. */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +probe qemu.log.test_blah = qemu.test_blah ? +{ + try { + argfilename_str = filename ? user_string_n(filename, 512) : "" + } catch {} + printf("%d@%d test_blah Blah context=%p filename=%s\n", pid(), gettimeofday_ns(), context, argfilename_str) +} +probe qemu.log.test_wibble = qemu.test_wibble ? +{ + printf("%d@%d test_wibble Wibble context=%p value=%d\n", pid(), gettimeofday_ns(), context, value) +} + diff --git a/tests/tracetool/dtrace.simpletrace-stap b/tests/tracetool/dtrace.simpletrace-stap new file mode 100644 index 0000000000..d064e3e286 --- /dev/null +++ b/tests/tracetool/dtrace.simpletrace-stap @@ -0,0 +1,16 @@ +/* This file is autogenerated by tracetool, do not edit. */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +probe qemu.simpletrace.test_blah = qemu.test_blah ? +{ + try { + argfilename_str = filename ? user_string_n(filename, 512) : "" + } catch {} + argfilename_len = strlen(argfilename_str) + printf("%8b%8b%8b%4b%4b%8b%4b%.*s", 1, 0, gettimeofday_ns(), 24 + 8 + 4 + argfilename_len, pid(), context, argfilename_len, argfilename_len, argfilename_str) +} +probe qemu.simpletrace.test_wibble = qemu.test_wibble ? +{ + printf("%8b%8b%8b%4b%4b%8b%8b", 1, 1, gettimeofday_ns(), 24 + 8 + 8, pid(), context, value) +} + diff --git a/tests/tracetool/dtrace.stap b/tests/tracetool/dtrace.stap new file mode 100644 index 0000000000..9c5d8a527c --- /dev/null +++ b/tests/tracetool/dtrace.stap @@ -0,0 +1,14 @@ +/* This file is autogenerated by tracetool, do not edit. */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +probe qemu.test_blah = process("qemu").mark("test_blah") +{ + context = $arg1; + filename = $arg2; +} +probe qemu.test_wibble = process("qemu").mark("test_wibble") +{ + context = $arg1; + value = $arg2; +} + diff --git a/tests/tracetool/ftrace.c b/tests/tracetool/ftrace.c new file mode 100644 index 0000000000..9f862fa14d --- /dev/null +++ b/tests/tracetool/ftrace.c @@ -0,0 +1,32 @@ +/* This file is autogenerated by tracetool, do not edit. */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "qemu/osdep.h" +#include "qemu/module.h" +#include "trace-testsuite.h" + +uint16_t _TRACE_TEST_BLAH_DSTATE; +uint16_t _TRACE_TEST_WIBBLE_DSTATE; +TraceEvent _TRACE_TEST_BLAH_EVENT = { + .id = 0, + .name = "test_blah", + .sstate = TRACE_TEST_BLAH_ENABLED, + .dstate = &_TRACE_TEST_BLAH_DSTATE +}; +TraceEvent _TRACE_TEST_WIBBLE_EVENT = { + .id = 0, + .name = "test_wibble", + .sstate = TRACE_TEST_WIBBLE_ENABLED, + .dstate = &_TRACE_TEST_WIBBLE_DSTATE +}; +TraceEvent *testsuite_trace_events[] = { + &_TRACE_TEST_BLAH_EVENT, + &_TRACE_TEST_WIBBLE_EVENT, + NULL, +}; + +static void trace_testsuite_register_events(void) +{ + trace_event_register_group(testsuite_trace_events); +} +trace_init(trace_testsuite_register_events) diff --git a/tests/tracetool/ftrace.h b/tests/tracetool/ftrace.h new file mode 100644 index 0000000000..f1ff3b0a62 --- /dev/null +++ b/tests/tracetool/ftrace.h @@ -0,0 +1,73 @@ +/* This file is autogenerated by tracetool, do not edit. */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef TRACE_TESTSUITE_GENERATED_TRACERS_H +#define TRACE_TESTSUITE_GENERATED_TRACERS_H + +#include "trace/control.h" + +extern TraceEvent _TRACE_TEST_BLAH_EVENT; +extern TraceEvent _TRACE_TEST_WIBBLE_EVENT; +extern uint16_t _TRACE_TEST_BLAH_DSTATE; +extern uint16_t _TRACE_TEST_WIBBLE_DSTATE; +#define TRACE_TEST_BLAH_ENABLED 1 +#define TRACE_TEST_WIBBLE_ENABLED 1 +#include "trace/ftrace.h" + + +#define TRACE_TEST_BLAH_BACKEND_DSTATE() ( \ + trace_event_get_state_dynamic_by_id(TRACE_TEST_BLAH) || \ + false) + +static inline void _nocheck__trace_test_blah(void *context, const char *filename) +{ + { + char ftrace_buf[MAX_TRACE_STRLEN]; + int unused __attribute__ ((unused)); + int trlen; + if (trace_event_get_state(TRACE_TEST_BLAH)) { +#line 4 "trace-events" + trlen = snprintf(ftrace_buf, MAX_TRACE_STRLEN, + "test_blah " "Blah context=%p filename=%s" "\n" , context, filename); +#line 33 "ftrace.h" + trlen = MIN(trlen, MAX_TRACE_STRLEN - 1); + unused = write(trace_marker_fd, ftrace_buf, trlen); + } + } +} + +static inline void trace_test_blah(void *context, const char *filename) +{ + if (true) { + _nocheck__trace_test_blah(context, filename); + } +} + +#define TRACE_TEST_WIBBLE_BACKEND_DSTATE() ( \ + trace_event_get_state_dynamic_by_id(TRACE_TEST_WIBBLE) || \ + false) + +static inline void _nocheck__trace_test_wibble(void *context, int value) +{ + { + char ftrace_buf[MAX_TRACE_STRLEN]; + int unused __attribute__ ((unused)); + int trlen; + if (trace_event_get_state(TRACE_TEST_WIBBLE)) { +#line 5 "trace-events" + trlen = snprintf(ftrace_buf, MAX_TRACE_STRLEN, + "test_wibble " "Wibble context=%p value=%d" "\n" , context, value); +#line 61 "ftrace.h" + trlen = MIN(trlen, MAX_TRACE_STRLEN - 1); + unused = write(trace_marker_fd, ftrace_buf, trlen); + } + } +} + +static inline void trace_test_wibble(void *context, int value) +{ + if (true) { + _nocheck__trace_test_wibble(context, value); + } +} +#endif /* TRACE_TESTSUITE_GENERATED_TRACERS_H */ diff --git a/tests/tracetool/log.c b/tests/tracetool/log.c new file mode 100644 index 0000000000..9f862fa14d --- /dev/null +++ b/tests/tracetool/log.c @@ -0,0 +1,32 @@ +/* This file is autogenerated by tracetool, do not edit. */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "qemu/osdep.h" +#include "qemu/module.h" +#include "trace-testsuite.h" + +uint16_t _TRACE_TEST_BLAH_DSTATE; +uint16_t _TRACE_TEST_WIBBLE_DSTATE; +TraceEvent _TRACE_TEST_BLAH_EVENT = { + .id = 0, + .name = "test_blah", + .sstate = TRACE_TEST_BLAH_ENABLED, + .dstate = &_TRACE_TEST_BLAH_DSTATE +}; +TraceEvent _TRACE_TEST_WIBBLE_EVENT = { + .id = 0, + .name = "test_wibble", + .sstate = TRACE_TEST_WIBBLE_ENABLED, + .dstate = &_TRACE_TEST_WIBBLE_DSTATE +}; +TraceEvent *testsuite_trace_events[] = { + &_TRACE_TEST_BLAH_EVENT, + &_TRACE_TEST_WIBBLE_EVENT, + NULL, +}; + +static void trace_testsuite_register_events(void) +{ + trace_event_register_group(testsuite_trace_events); +} +trace_init(trace_testsuite_register_events) diff --git a/tests/tracetool/log.h b/tests/tracetool/log.h new file mode 100644 index 0000000000..4293f1010e --- /dev/null +++ b/tests/tracetool/log.h @@ -0,0 +1,57 @@ +/* This file is autogenerated by tracetool, do not edit. */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef TRACE_TESTSUITE_GENERATED_TRACERS_H +#define TRACE_TESTSUITE_GENERATED_TRACERS_H + +#include "trace/control.h" + +extern TraceEvent _TRACE_TEST_BLAH_EVENT; +extern TraceEvent _TRACE_TEST_WIBBLE_EVENT; +extern uint16_t _TRACE_TEST_BLAH_DSTATE; +extern uint16_t _TRACE_TEST_WIBBLE_DSTATE; +#define TRACE_TEST_BLAH_ENABLED 1 +#define TRACE_TEST_WIBBLE_ENABLED 1 +#include "qemu/log-for-trace.h" + + +#define TRACE_TEST_BLAH_BACKEND_DSTATE() ( \ + trace_event_get_state_dynamic_by_id(TRACE_TEST_BLAH) || \ + false) + +static inline void _nocheck__trace_test_blah(void *context, const char *filename) +{ + if (trace_event_get_state(TRACE_TEST_BLAH) && qemu_loglevel_mask(LOG_TRACE)) { +#line 4 "trace-events" + qemu_log("test_blah " "Blah context=%p filename=%s" "\n", context, filename); +#line 28 "log.h" + } +} + +static inline void trace_test_blah(void *context, const char *filename) +{ + if (true) { + _nocheck__trace_test_blah(context, filename); + } +} + +#define TRACE_TEST_WIBBLE_BACKEND_DSTATE() ( \ + trace_event_get_state_dynamic_by_id(TRACE_TEST_WIBBLE) || \ + false) + +static inline void _nocheck__trace_test_wibble(void *context, int value) +{ + if (trace_event_get_state(TRACE_TEST_WIBBLE) && qemu_loglevel_mask(LOG_TRACE)) { +#line 5 "trace-events" + qemu_log("test_wibble " "Wibble context=%p value=%d" "\n", context, value); +#line 48 "log.h" + } +} + +static inline void trace_test_wibble(void *context, int value) +{ + if (true) { + _nocheck__trace_test_wibble(context, value); + } +} +#endif /* TRACE_TESTSUITE_GENERATED_TRACERS_H */ diff --git a/tests/tracetool/meson.build b/tests/tracetool/meson.build new file mode 100644 index 0000000000..09bbaaa86b --- /dev/null +++ b/tests/tracetool/meson.build @@ -0,0 +1,28 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +test_env = environment() +test_env.set('PYTHONPATH', meson.project_source_root() / 'scripts') +test_env.set('PYTHONIOENCODING', 'utf-8') + +backends = [ + 'dtrace', + 'ftrace', + 'log', + 'simple', + 'syslog', + 'ust' +] + +# The tracetool-test.py program has portability problems on Windows. +if host_machine.system() != 'windows' + foreach backend: backends + test(backend, + python, + args: [meson.current_source_dir() / 'tracetool-test.py', + meson.project_source_root() / 'scripts' / 'tracetool.py', + backend, + meson.current_source_dir(), + meson.current_build_dir()], + suite: ['tracetool']) + endforeach +endif diff --git a/tests/tracetool/simple.c b/tests/tracetool/simple.c new file mode 100644 index 0000000000..0484177481 --- /dev/null +++ b/tests/tracetool/simple.c @@ -0,0 +1,61 @@ +/* This file is autogenerated by tracetool, do not edit. */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "qemu/osdep.h" +#include "qemu/module.h" +#include "trace-testsuite.h" + +uint16_t _TRACE_TEST_BLAH_DSTATE; +uint16_t _TRACE_TEST_WIBBLE_DSTATE; +TraceEvent _TRACE_TEST_BLAH_EVENT = { + .id = 0, + .name = "test_blah", + .sstate = TRACE_TEST_BLAH_ENABLED, + .dstate = &_TRACE_TEST_BLAH_DSTATE +}; +TraceEvent _TRACE_TEST_WIBBLE_EVENT = { + .id = 0, + .name = "test_wibble", + .sstate = TRACE_TEST_WIBBLE_ENABLED, + .dstate = &_TRACE_TEST_WIBBLE_DSTATE +}; +TraceEvent *testsuite_trace_events[] = { + &_TRACE_TEST_BLAH_EVENT, + &_TRACE_TEST_WIBBLE_EVENT, + NULL, +}; + +static void trace_testsuite_register_events(void) +{ + trace_event_register_group(testsuite_trace_events); +} +trace_init(trace_testsuite_register_events) +#include "qemu/osdep.h" +#include "trace/control.h" +#include "trace/simple.h" + +void _simple_trace_test_blah(void *context, const char *filename) +{ + TraceBufferRecord rec; + size_t argfilename_len = filename ? MIN(strlen(filename), MAX_TRACE_STRLEN) : 0; + + if (trace_record_start(&rec, _TRACE_TEST_BLAH_EVENT.id, 8 + 4 + argfilename_len)) { + return; /* Trace Buffer Full, Event Dropped ! */ + } + trace_record_write_u64(&rec, (uintptr_t)(uint64_t *)context); + trace_record_write_str(&rec, filename, argfilename_len); + trace_record_finish(&rec); +} + +void _simple_trace_test_wibble(void *context, int value) +{ + TraceBufferRecord rec; + + if (trace_record_start(&rec, _TRACE_TEST_WIBBLE_EVENT.id, 8 + 8)) { + return; /* Trace Buffer Full, Event Dropped ! */ + } + trace_record_write_u64(&rec, (uintptr_t)(uint64_t *)context); + trace_record_write_u64(&rec, (uint64_t)value); + trace_record_finish(&rec); +} + diff --git a/tests/tracetool/simple.h b/tests/tracetool/simple.h new file mode 100644 index 0000000000..3c9de68c43 --- /dev/null +++ b/tests/tracetool/simple.h @@ -0,0 +1,54 @@ +/* This file is autogenerated by tracetool, do not edit. */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef TRACE_TESTSUITE_GENERATED_TRACERS_H +#define TRACE_TESTSUITE_GENERATED_TRACERS_H + +#include "trace/control.h" + +extern TraceEvent _TRACE_TEST_BLAH_EVENT; +extern TraceEvent _TRACE_TEST_WIBBLE_EVENT; +extern uint16_t _TRACE_TEST_BLAH_DSTATE; +extern uint16_t _TRACE_TEST_WIBBLE_DSTATE; +#define TRACE_TEST_BLAH_ENABLED 1 +#define TRACE_TEST_WIBBLE_ENABLED 1 +void _simple_trace_test_blah(void *context, const char *filename); +void _simple_trace_test_wibble(void *context, int value); + + +#define TRACE_TEST_BLAH_BACKEND_DSTATE() ( \ + trace_event_get_state_dynamic_by_id(TRACE_TEST_BLAH) || \ + false) + +static inline void _nocheck__trace_test_blah(void *context, const char *filename) +{ + if (trace_event_get_state(TRACE_TEST_BLAH)) { + _simple_trace_test_blah(context, filename); + } +} + +static inline void trace_test_blah(void *context, const char *filename) +{ + if (true) { + _nocheck__trace_test_blah(context, filename); + } +} + +#define TRACE_TEST_WIBBLE_BACKEND_DSTATE() ( \ + trace_event_get_state_dynamic_by_id(TRACE_TEST_WIBBLE) || \ + false) + +static inline void _nocheck__trace_test_wibble(void *context, int value) +{ + if (trace_event_get_state(TRACE_TEST_WIBBLE)) { + _simple_trace_test_wibble(context, value); + } +} + +static inline void trace_test_wibble(void *context, int value) +{ + if (true) { + _nocheck__trace_test_wibble(context, value); + } +} +#endif /* TRACE_TESTSUITE_GENERATED_TRACERS_H */ diff --git a/tests/tracetool/syslog.c b/tests/tracetool/syslog.c new file mode 100644 index 0000000000..9f862fa14d --- /dev/null +++ b/tests/tracetool/syslog.c @@ -0,0 +1,32 @@ +/* This file is autogenerated by tracetool, do not edit. */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "qemu/osdep.h" +#include "qemu/module.h" +#include "trace-testsuite.h" + +uint16_t _TRACE_TEST_BLAH_DSTATE; +uint16_t _TRACE_TEST_WIBBLE_DSTATE; +TraceEvent _TRACE_TEST_BLAH_EVENT = { + .id = 0, + .name = "test_blah", + .sstate = TRACE_TEST_BLAH_ENABLED, + .dstate = &_TRACE_TEST_BLAH_DSTATE +}; +TraceEvent _TRACE_TEST_WIBBLE_EVENT = { + .id = 0, + .name = "test_wibble", + .sstate = TRACE_TEST_WIBBLE_ENABLED, + .dstate = &_TRACE_TEST_WIBBLE_DSTATE +}; +TraceEvent *testsuite_trace_events[] = { + &_TRACE_TEST_BLAH_EVENT, + &_TRACE_TEST_WIBBLE_EVENT, + NULL, +}; + +static void trace_testsuite_register_events(void) +{ + trace_event_register_group(testsuite_trace_events); +} +trace_init(trace_testsuite_register_events) diff --git a/tests/tracetool/syslog.h b/tests/tracetool/syslog.h new file mode 100644 index 0000000000..498bbfb99e --- /dev/null +++ b/tests/tracetool/syslog.h @@ -0,0 +1,57 @@ +/* This file is autogenerated by tracetool, do not edit. */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef TRACE_TESTSUITE_GENERATED_TRACERS_H +#define TRACE_TESTSUITE_GENERATED_TRACERS_H + +#include "trace/control.h" + +extern TraceEvent _TRACE_TEST_BLAH_EVENT; +extern TraceEvent _TRACE_TEST_WIBBLE_EVENT; +extern uint16_t _TRACE_TEST_BLAH_DSTATE; +extern uint16_t _TRACE_TEST_WIBBLE_DSTATE; +#define TRACE_TEST_BLAH_ENABLED 1 +#define TRACE_TEST_WIBBLE_ENABLED 1 +#include + + +#define TRACE_TEST_BLAH_BACKEND_DSTATE() ( \ + trace_event_get_state_dynamic_by_id(TRACE_TEST_BLAH) || \ + false) + +static inline void _nocheck__trace_test_blah(void *context, const char *filename) +{ + if (trace_event_get_state(TRACE_TEST_BLAH)) { +#line 4 "trace-events" + syslog(LOG_INFO, "test_blah " "Blah context=%p filename=%s" , context, filename); +#line 28 "syslog.h" + } +} + +static inline void trace_test_blah(void *context, const char *filename) +{ + if (true) { + _nocheck__trace_test_blah(context, filename); + } +} + +#define TRACE_TEST_WIBBLE_BACKEND_DSTATE() ( \ + trace_event_get_state_dynamic_by_id(TRACE_TEST_WIBBLE) || \ + false) + +static inline void _nocheck__trace_test_wibble(void *context, int value) +{ + if (trace_event_get_state(TRACE_TEST_WIBBLE)) { +#line 5 "trace-events" + syslog(LOG_INFO, "test_wibble " "Wibble context=%p value=%d" , context, value); +#line 48 "syslog.h" + } +} + +static inline void trace_test_wibble(void *context, int value) +{ + if (true) { + _nocheck__trace_test_wibble(context, value); + } +} +#endif /* TRACE_TESTSUITE_GENERATED_TRACERS_H */ diff --git a/tests/tracetool/trace-events b/tests/tracetool/trace-events new file mode 100644 index 0000000000..72cf4d6f70 --- /dev/null +++ b/tests/tracetool/trace-events @@ -0,0 +1,5 @@ +# See docs/devel/tracing.rst for syntax documentation. +# SPDX-License-Identifier: GPL-2.0-or-later + +test_blah(void *context, const char *filename) "Blah context=%p filename=%s" +test_wibble(void *context, int value) "Wibble context=%p value=%d" diff --git a/tests/tracetool/tracetool-test.py b/tests/tracetool/tracetool-test.py new file mode 100755 index 0000000000..a420597fc4 --- /dev/null +++ b/tests/tracetool/tracetool-test.py @@ -0,0 +1,105 @@ +#!/usr/bin/python3 +# SPDX-License-Identifier: GPL-2.0-or-later + +import os +from pathlib import Path +from shutil import copyfile +from subprocess import check_call +import sys + + +def get_formats(backend): + formats = [ + "c", + "h", + ] + if backend == "dtrace": + formats += [ + "d", + "log-stap", + "simpletrace-stap", + "stap", + ] + if backend == "ust": + formats += [ + "ust-events-c", + "ust-events-h", + ] + return formats + + +def test_tracetool_one(tracetool, backend, fmt, src_dir, build_dir): + rel_filename = backend + "." + fmt + actual_file = Path(build_dir, rel_filename) + expect_file = Path(src_dir, rel_filename) + + args = [tracetool, f"--format={fmt}", f"--backends={backend}", "--group=testsuite"] + + if fmt.find("stap") != -1: + args += ["--binary=qemu", "--probe-prefix=qemu"] + + # Use relative files for both, as these filenames end + # up in '#line' statements in the output + args += ["trace-events", rel_filename] + + try: + check_call(args, cwd=build_dir) + actual = actual_file.read_text() + finally: + actual_file.unlink() + + if os.getenv("QEMU_TEST_REGENERATE", False): + print(f"# regenerate {expect_file}") + expect_file.write_text(actual) + + expect = expect_file.read_text() + + assert expect == actual + + +def test_tracetool(tracetool, backend, source_dir, build_dir): + fail = False + scenarios = len(get_formats(backend)) + + print(f"1..{scenarios}") + + src_events = Path(source_dir, "trace-events") + build_events = Path(build_dir, "trace-events") + + try: + # We need a stable relative filename under build dir + # for the '#line' statements, so copy over the input + copyfile(src_events, build_events) + + num = 1 + for fmt in get_formats(backend): + status = "not ok" + hint = "" + try: + test_tracetool_one(tracetool, backend, fmt, source_dir, build_dir) + status = "ok" + except Exception as e: + print(f"# {e}") + fail = True + hint = ( + " (set QEMU_TEST_REGENERATE=1 to recreate reference " + + "output if tracetool generator was intentionally changed)" + ) + finally: + print(f"{status} {num} - {backend}.{fmt}{hint}") + finally: + build_events.unlink() + + return fail + + +if __name__ == "__main__": + if len(sys.argv) != 5: + argv0 = sys.argv[0] + print("syntax: {argv0} TRACE-TOOL BACKEND SRC-DIR BUILD-DIR", file=sys.stderr) + sys.exit(1) + + fail = test_tracetool(sys.argv[1], sys.argv[2], sys.argv[3], sys.argv[4]) + if fail: + sys.exit(1) + sys.exit(0) diff --git a/tests/tracetool/ust.c b/tests/tracetool/ust.c new file mode 100644 index 0000000000..9f862fa14d --- /dev/null +++ b/tests/tracetool/ust.c @@ -0,0 +1,32 @@ +/* This file is autogenerated by tracetool, do not edit. */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "qemu/osdep.h" +#include "qemu/module.h" +#include "trace-testsuite.h" + +uint16_t _TRACE_TEST_BLAH_DSTATE; +uint16_t _TRACE_TEST_WIBBLE_DSTATE; +TraceEvent _TRACE_TEST_BLAH_EVENT = { + .id = 0, + .name = "test_blah", + .sstate = TRACE_TEST_BLAH_ENABLED, + .dstate = &_TRACE_TEST_BLAH_DSTATE +}; +TraceEvent _TRACE_TEST_WIBBLE_EVENT = { + .id = 0, + .name = "test_wibble", + .sstate = TRACE_TEST_WIBBLE_ENABLED, + .dstate = &_TRACE_TEST_WIBBLE_DSTATE +}; +TraceEvent *testsuite_trace_events[] = { + &_TRACE_TEST_BLAH_EVENT, + &_TRACE_TEST_WIBBLE_EVENT, + NULL, +}; + +static void trace_testsuite_register_events(void) +{ + trace_event_register_group(testsuite_trace_events); +} +trace_init(trace_testsuite_register_events) diff --git a/tests/tracetool/ust.h b/tests/tracetool/ust.h new file mode 100644 index 0000000000..1184ddd870 --- /dev/null +++ b/tests/tracetool/ust.h @@ -0,0 +1,55 @@ +/* This file is autogenerated by tracetool, do not edit. */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef TRACE_TESTSUITE_GENERATED_TRACERS_H +#define TRACE_TESTSUITE_GENERATED_TRACERS_H + +#include "trace/control.h" + +extern TraceEvent _TRACE_TEST_BLAH_EVENT; +extern TraceEvent _TRACE_TEST_WIBBLE_EVENT; +extern uint16_t _TRACE_TEST_BLAH_DSTATE; +extern uint16_t _TRACE_TEST_WIBBLE_DSTATE; +#define TRACE_TEST_BLAH_ENABLED 1 +#define TRACE_TEST_WIBBLE_ENABLED 1 +#include +#include "trace-ust-testsuite.h" + +/* tracepoint_enabled() was introduced in LTTng UST 2.7 */ +#ifndef tracepoint_enabled +#define tracepoint_enabled(a, b) true +#endif + + +#define TRACE_TEST_BLAH_BACKEND_DSTATE() ( \ + tracepoint_enabled(qemu, test_blah) || \ + false) + +static inline void _nocheck__trace_test_blah(void *context, const char *filename) +{ + tracepoint(qemu, test_blah, context, filename); +} + +static inline void trace_test_blah(void *context, const char *filename) +{ + if (true) { + _nocheck__trace_test_blah(context, filename); + } +} + +#define TRACE_TEST_WIBBLE_BACKEND_DSTATE() ( \ + tracepoint_enabled(qemu, test_wibble) || \ + false) + +static inline void _nocheck__trace_test_wibble(void *context, int value) +{ + tracepoint(qemu, test_wibble, context, value); +} + +static inline void trace_test_wibble(void *context, int value) +{ + if (true) { + _nocheck__trace_test_wibble(context, value); + } +} +#endif /* TRACE_TESTSUITE_GENERATED_TRACERS_H */ diff --git a/tests/tracetool/ust.ust-events-c b/tests/tracetool/ust.ust-events-c new file mode 100644 index 0000000000..db23224056 --- /dev/null +++ b/tests/tracetool/ust.ust-events-c @@ -0,0 +1,14 @@ +/* This file is autogenerated by tracetool, do not edit. */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "qemu/osdep.h" + +#define TRACEPOINT_DEFINE +#define TRACEPOINT_CREATE_PROBES + +/* If gcc version 4.7 or older is used, LTTng ust gives a warning when compiling with + -Wredundant-decls. + */ +#pragma GCC diagnostic ignored "-Wredundant-decls" + +#include "trace-ust-all.h" diff --git a/tests/tracetool/ust.ust-events-h b/tests/tracetool/ust.ust-events-h new file mode 100644 index 0000000000..4621a995fc --- /dev/null +++ b/tests/tracetool/ust.ust-events-h @@ -0,0 +1,56 @@ +/* This file is autogenerated by tracetool, do not edit. */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#undef TRACEPOINT_PROVIDER +#define TRACEPOINT_PROVIDER qemu + +#undef TRACEPOINT_INCLUDE +#define TRACEPOINT_INCLUDE "./trace-ust.h" + +#if !defined (TRACE_TESTSUITE_GENERATED_UST_H) || \ + defined(TRACEPOINT_HEADER_MULTI_READ) +#define TRACE_TESTSUITE_GENERATED_UST_H + +#include + +/* + * LTTng ust 2.0 does not allow you to use TP_ARGS(void) for tracepoints + * requiring no arguments. We define these macros introduced in more recent * versions of LTTng ust as a workaround + */ +#ifndef _TP_EXPROTO1 +#define _TP_EXPROTO1(a) void +#endif +#ifndef _TP_EXDATA_PROTO1 +#define _TP_EXDATA_PROTO1(a) void *__tp_data +#endif +#ifndef _TP_EXDATA_VAR1 +#define _TP_EXDATA_VAR1(a) __tp_data +#endif +#ifndef _TP_EXVAR1 +#define _TP_EXVAR1(a) +#endif + +TRACEPOINT_EVENT( + qemu, + test_blah, + TP_ARGS(void *, context, const char *, filename), + TP_FIELDS( + ctf_integer_hex(void *, context, context) + ctf_string(filename, filename) + ) +) + +TRACEPOINT_EVENT( + qemu, + test_wibble, + TP_ARGS(void *, context, int, value), + TP_FIELDS( + ctf_integer_hex(void *, context, context) + ctf_integer(int, value, value) + ) +) + +#endif /* TRACE_TESTSUITE_GENERATED_UST_H */ + +/* This part must be outside ifdef protection */ +#include From c47db9b1db906bd05cf83e0d70df2beba2ef9eaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 16 Sep 2025 09:16:36 +0100 Subject: [PATCH 0445/2396] tracetool: drop the probe "__nocheck__" wrapping MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Every generated inline probe function is wrapped with a trivial caller that has a hard-coded condition test: static inline void _nocheck__trace_test_wibble(void * context, int value) { tracepoint(qemu, test_wibble, context, value); } static inline void trace_test_wibble(void * context, int value) { if (true) { _nocheck__trace_test_wibble(context, value); } } This was introduced for TCG probes back in 864a2178: trace: [tcg] Do not generate TCG code to trace dynamically-disabled events but is obsolete since 126d4123 tracing: excise the tcg related from tracetool This commit removes the wrapping such that we have static inline void trace_test_wibble(void * context, int value) { tracepoint(qemu, test_wibble, context, value); } The default build of qemu-system-x86_64 on Fedora with the 'log' backend, has its size reduced by 1 MB Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Stefan Hajnoczi Signed-off-by: Daniel P. Berrangé Message-id: 20250916081638.764020-7-berrange@redhat.com Signed-off-by: Stefan Hajnoczi --- scripts/tracetool/__init__.py | 1 - scripts/tracetool/format/h.py | 16 +--------------- tests/tracetool/dtrace.h | 18 ++---------------- tests/tracetool/ftrace.h | 20 +++----------------- tests/tracetool/log.h | 20 +++----------------- tests/tracetool/simple.h | 18 ++---------------- tests/tracetool/syslog.h | 20 +++----------------- tests/tracetool/ust.h | 18 ++---------------- 8 files changed, 16 insertions(+), 115 deletions(-) diff --git a/scripts/tracetool/__init__.py b/scripts/tracetool/__init__.py index 0f33758870..1d5238a084 100644 --- a/scripts/tracetool/__init__.py +++ b/scripts/tracetool/__init__.py @@ -338,7 +338,6 @@ class Event(object): return self._FMT.findall(self.fmt) QEMU_TRACE = "trace_%(name)s" - QEMU_TRACE_NOCHECK = "_nocheck__" + QEMU_TRACE QEMU_TRACE_TCG = QEMU_TRACE + "_tcg" QEMU_DSTATE = "_TRACE_%(NAME)s_DSTATE" QEMU_BACKEND_DSTATE = "TRACE_%(NAME)s_BACKEND_DSTATE" diff --git a/scripts/tracetool/format/h.py b/scripts/tracetool/format/h.py index a00ae475f7..b42a8268a8 100644 --- a/scripts/tracetool/format/h.py +++ b/scripts/tracetool/format/h.py @@ -64,7 +64,7 @@ def generate(events, backend, group): out('', 'static inline void %(api)s(%(args)s)', '{', - api=e.api(e.QEMU_TRACE_NOCHECK), + api=e.api(), args=e.args) if "disable" not in e.properties: @@ -72,20 +72,6 @@ def generate(events, backend, group): out('}') - cond = "true" - - out('', - 'static inline void %(api)s(%(args)s)', - '{', - ' if (%(cond)s) {', - ' %(api_nocheck)s(%(names)s);', - ' }', - '}', - api=e.api(), - api_nocheck=e.api(e.QEMU_TRACE_NOCHECK), - args=e.args, - names=", ".join(e.args.names()), - cond=cond) backend.generate_end(events, group) diff --git a/tests/tracetool/dtrace.h b/tests/tracetool/dtrace.h index c2e5110672..c8931a8d7b 100644 --- a/tests/tracetool/dtrace.h +++ b/tests/tracetool/dtrace.h @@ -29,31 +29,17 @@ extern uint16_t _TRACE_TEST_WIBBLE_DSTATE; QEMU_TEST_BLAH_ENABLED() || \ false) -static inline void _nocheck__trace_test_blah(void *context, const char *filename) -{ - QEMU_TEST_BLAH(context, filename); -} - static inline void trace_test_blah(void *context, const char *filename) { - if (true) { - _nocheck__trace_test_blah(context, filename); - } + QEMU_TEST_BLAH(context, filename); } #define TRACE_TEST_WIBBLE_BACKEND_DSTATE() ( \ QEMU_TEST_WIBBLE_ENABLED() || \ false) -static inline void _nocheck__trace_test_wibble(void *context, int value) +static inline void trace_test_wibble(void *context, int value) { QEMU_TEST_WIBBLE(context, value); } - -static inline void trace_test_wibble(void *context, int value) -{ - if (true) { - _nocheck__trace_test_wibble(context, value); - } -} #endif /* TRACE_TESTSUITE_GENERATED_TRACERS_H */ diff --git a/tests/tracetool/ftrace.h b/tests/tracetool/ftrace.h index f1ff3b0a62..fe22ea0f09 100644 --- a/tests/tracetool/ftrace.h +++ b/tests/tracetool/ftrace.h @@ -19,7 +19,7 @@ extern uint16_t _TRACE_TEST_WIBBLE_DSTATE; trace_event_get_state_dynamic_by_id(TRACE_TEST_BLAH) || \ false) -static inline void _nocheck__trace_test_blah(void *context, const char *filename) +static inline void trace_test_blah(void *context, const char *filename) { { char ftrace_buf[MAX_TRACE_STRLEN]; @@ -36,18 +36,11 @@ static inline void _nocheck__trace_test_blah(void *context, const char *filename } } -static inline void trace_test_blah(void *context, const char *filename) -{ - if (true) { - _nocheck__trace_test_blah(context, filename); - } -} - #define TRACE_TEST_WIBBLE_BACKEND_DSTATE() ( \ trace_event_get_state_dynamic_by_id(TRACE_TEST_WIBBLE) || \ false) -static inline void _nocheck__trace_test_wibble(void *context, int value) +static inline void trace_test_wibble(void *context, int value) { { char ftrace_buf[MAX_TRACE_STRLEN]; @@ -57,17 +50,10 @@ static inline void _nocheck__trace_test_wibble(void *context, int value) #line 5 "trace-events" trlen = snprintf(ftrace_buf, MAX_TRACE_STRLEN, "test_wibble " "Wibble context=%p value=%d" "\n" , context, value); -#line 61 "ftrace.h" +#line 54 "ftrace.h" trlen = MIN(trlen, MAX_TRACE_STRLEN - 1); unused = write(trace_marker_fd, ftrace_buf, trlen); } } } - -static inline void trace_test_wibble(void *context, int value) -{ - if (true) { - _nocheck__trace_test_wibble(context, value); - } -} #endif /* TRACE_TESTSUITE_GENERATED_TRACERS_H */ diff --git a/tests/tracetool/log.h b/tests/tracetool/log.h index 4293f1010e..edcc7f9d47 100644 --- a/tests/tracetool/log.h +++ b/tests/tracetool/log.h @@ -19,7 +19,7 @@ extern uint16_t _TRACE_TEST_WIBBLE_DSTATE; trace_event_get_state_dynamic_by_id(TRACE_TEST_BLAH) || \ false) -static inline void _nocheck__trace_test_blah(void *context, const char *filename) +static inline void trace_test_blah(void *context, const char *filename) { if (trace_event_get_state(TRACE_TEST_BLAH) && qemu_loglevel_mask(LOG_TRACE)) { #line 4 "trace-events" @@ -28,30 +28,16 @@ static inline void _nocheck__trace_test_blah(void *context, const char *filename } } -static inline void trace_test_blah(void *context, const char *filename) -{ - if (true) { - _nocheck__trace_test_blah(context, filename); - } -} - #define TRACE_TEST_WIBBLE_BACKEND_DSTATE() ( \ trace_event_get_state_dynamic_by_id(TRACE_TEST_WIBBLE) || \ false) -static inline void _nocheck__trace_test_wibble(void *context, int value) +static inline void trace_test_wibble(void *context, int value) { if (trace_event_get_state(TRACE_TEST_WIBBLE) && qemu_loglevel_mask(LOG_TRACE)) { #line 5 "trace-events" qemu_log("test_wibble " "Wibble context=%p value=%d" "\n", context, value); -#line 48 "log.h" - } -} - -static inline void trace_test_wibble(void *context, int value) -{ - if (true) { - _nocheck__trace_test_wibble(context, value); +#line 41 "log.h" } } #endif /* TRACE_TESTSUITE_GENERATED_TRACERS_H */ diff --git a/tests/tracetool/simple.h b/tests/tracetool/simple.h index 3c9de68c43..ec6fcb22c3 100644 --- a/tests/tracetool/simple.h +++ b/tests/tracetool/simple.h @@ -20,35 +20,21 @@ void _simple_trace_test_wibble(void *context, int value); trace_event_get_state_dynamic_by_id(TRACE_TEST_BLAH) || \ false) -static inline void _nocheck__trace_test_blah(void *context, const char *filename) +static inline void trace_test_blah(void *context, const char *filename) { if (trace_event_get_state(TRACE_TEST_BLAH)) { _simple_trace_test_blah(context, filename); } } -static inline void trace_test_blah(void *context, const char *filename) -{ - if (true) { - _nocheck__trace_test_blah(context, filename); - } -} - #define TRACE_TEST_WIBBLE_BACKEND_DSTATE() ( \ trace_event_get_state_dynamic_by_id(TRACE_TEST_WIBBLE) || \ false) -static inline void _nocheck__trace_test_wibble(void *context, int value) +static inline void trace_test_wibble(void *context, int value) { if (trace_event_get_state(TRACE_TEST_WIBBLE)) { _simple_trace_test_wibble(context, value); } } - -static inline void trace_test_wibble(void *context, int value) -{ - if (true) { - _nocheck__trace_test_wibble(context, value); - } -} #endif /* TRACE_TESTSUITE_GENERATED_TRACERS_H */ diff --git a/tests/tracetool/syslog.h b/tests/tracetool/syslog.h index 498bbfb99e..ed4305554c 100644 --- a/tests/tracetool/syslog.h +++ b/tests/tracetool/syslog.h @@ -19,7 +19,7 @@ extern uint16_t _TRACE_TEST_WIBBLE_DSTATE; trace_event_get_state_dynamic_by_id(TRACE_TEST_BLAH) || \ false) -static inline void _nocheck__trace_test_blah(void *context, const char *filename) +static inline void trace_test_blah(void *context, const char *filename) { if (trace_event_get_state(TRACE_TEST_BLAH)) { #line 4 "trace-events" @@ -28,30 +28,16 @@ static inline void _nocheck__trace_test_blah(void *context, const char *filename } } -static inline void trace_test_blah(void *context, const char *filename) -{ - if (true) { - _nocheck__trace_test_blah(context, filename); - } -} - #define TRACE_TEST_WIBBLE_BACKEND_DSTATE() ( \ trace_event_get_state_dynamic_by_id(TRACE_TEST_WIBBLE) || \ false) -static inline void _nocheck__trace_test_wibble(void *context, int value) +static inline void trace_test_wibble(void *context, int value) { if (trace_event_get_state(TRACE_TEST_WIBBLE)) { #line 5 "trace-events" syslog(LOG_INFO, "test_wibble " "Wibble context=%p value=%d" , context, value); -#line 48 "syslog.h" - } -} - -static inline void trace_test_wibble(void *context, int value) -{ - if (true) { - _nocheck__trace_test_wibble(context, value); +#line 41 "syslog.h" } } #endif /* TRACE_TESTSUITE_GENERATED_TRACERS_H */ diff --git a/tests/tracetool/ust.h b/tests/tracetool/ust.h index 1184ddd870..b7acd0c39b 100644 --- a/tests/tracetool/ust.h +++ b/tests/tracetool/ust.h @@ -25,31 +25,17 @@ extern uint16_t _TRACE_TEST_WIBBLE_DSTATE; tracepoint_enabled(qemu, test_blah) || \ false) -static inline void _nocheck__trace_test_blah(void *context, const char *filename) -{ - tracepoint(qemu, test_blah, context, filename); -} - static inline void trace_test_blah(void *context, const char *filename) { - if (true) { - _nocheck__trace_test_blah(context, filename); - } + tracepoint(qemu, test_blah, context, filename); } #define TRACE_TEST_WIBBLE_BACKEND_DSTATE() ( \ tracepoint_enabled(qemu, test_wibble) || \ false) -static inline void _nocheck__trace_test_wibble(void *context, int value) +static inline void trace_test_wibble(void *context, int value) { tracepoint(qemu, test_wibble, context, value); } - -static inline void trace_test_wibble(void *context, int value) -{ - if (true) { - _nocheck__trace_test_wibble(context, value); - } -} #endif /* TRACE_TESTSUITE_GENERATED_TRACERS_H */ From 4b1e71591254a7e924b6b295868edc6550ed0420 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 16 Sep 2025 09:16:37 +0100 Subject: [PATCH 0446/2396] qapi: switch to use QEMU_TEST_REGENERATE env var MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The QAPI_TEST_UPDATE env var can be set when running the QAPI schema tests to regenerate the reference output. For consistent naming with the tracetool test, change the env var name to QEMU_TEST_REGENERATE. The test is modified to provide a hint about use of the new env var and it is also added to the developer documentation.document its usage. Reviewed-by: Stefan Hajnoczi Signed-off-by: Daniel P. Berrangé Reviewed-by: Markus Armbruster Message-id: 20250916081638.764020-8-berrange@redhat.com Signed-off-by: Stefan Hajnoczi --- docs/devel/testing/main.rst | 12 ++++++++++++ tests/qapi-schema/test-qapi.py | 7 +++++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/docs/devel/testing/main.rst b/docs/devel/testing/main.rst index 11f05c0006..0662766b5c 100644 --- a/docs/devel/testing/main.rst +++ b/docs/devel/testing/main.rst @@ -178,6 +178,18 @@ parser (either fixing a bug or extending/modifying the syntax). To do this: ``qapi-schema += foo.json`` +The reference output can be automatically updated to match the latest QAPI +code generator by running the tests with the QEMU_TEST_REGENERATE environment +variable set. + +.. code:: + + QEMU_TEST_REGENERATE=1 make check-qapi-schema + +The resulting changes must be reviewed by the author to ensure they match +the intended results before adding the updated reference output to the +same commit that alters the generator code. + .. _tracetool-tests: Tracetool tests diff --git a/tests/qapi-schema/test-qapi.py b/tests/qapi-schema/test-qapi.py index 4be930228c..cf7fb8a6df 100755 --- a/tests/qapi-schema/test-qapi.py +++ b/tests/qapi-schema/test-qapi.py @@ -165,7 +165,7 @@ def test_and_diff(test_name, dir_name, update): if actual_out == expected_out and actual_err == expected_err: return 0 - print("%s %s" % (test_name, 'UPDATE' if update else 'FAIL'), + print("%s: %s" % (test_name, 'UPDATE' if update else 'FAIL'), file=sys.stderr) out_diff = difflib.unified_diff(expected_out, actual_out, outfp.name) err_diff = difflib.unified_diff(expected_err, actual_err, errfp.name) @@ -173,6 +173,9 @@ def test_and_diff(test_name, dir_name, update): sys.stdout.writelines(err_diff) if not update: + print(("\n%s: set QEMU_TEST_REGENERATE=1 to recreate reference output" + + "if the QAPI schema generator was intentionally changed") % test_name, + file=sys.stderr) return 1 try: @@ -197,7 +200,7 @@ def main(argv): parser.add_argument('-d', '--dir', action='store', default='', help="directory containing tests") parser.add_argument('-u', '--update', action='store_true', - default='QAPI_TEST_UPDATE' in os.environ, + default='QEMU_TEST_REGENERATE' in os.environ, help="update expected test results") parser.add_argument('tests', nargs='*', metavar='TEST', action='store') args = parser.parse_args() From 2c27d8523927b0965b7b3d265eee3baf9a15c9c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Tue, 16 Sep 2025 09:16:38 +0100 Subject: [PATCH 0447/2396] tracetool-test: allow to run in parallel MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Create a temporary build subdirectory, to avoid conflicting with other running tests. This fixes "meson test" with tracetool-test which is parallel default. Signed-off-by: Marc-André Lureau Signed-off-by: Stefan Hajnoczi Message-id: 20250916081638.764020-9-berrange@redhat.com Message-ID: <20250908114652.1880366-1-marcandre.lureau@redhat.com> Signed-off-by: Stefan Hajnoczi --- tests/tracetool/tracetool-test.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tests/tracetool/tracetool-test.py b/tests/tracetool/tracetool-test.py index a420597fc4..65430fdedc 100755 --- a/tests/tracetool/tracetool-test.py +++ b/tests/tracetool/tracetool-test.py @@ -6,6 +6,7 @@ from pathlib import Path from shutil import copyfile from subprocess import check_call import sys +import tempfile def get_formats(backend): @@ -99,7 +100,8 @@ if __name__ == "__main__": print("syntax: {argv0} TRACE-TOOL BACKEND SRC-DIR BUILD-DIR", file=sys.stderr) sys.exit(1) - fail = test_tracetool(sys.argv[1], sys.argv[2], sys.argv[3], sys.argv[4]) - if fail: - sys.exit(1) + with tempfile.TemporaryDirectory(prefix=sys.argv[4]) as tmpdir: + fail = test_tracetool(sys.argv[1], sys.argv[2], sys.argv[3], tmpdir) + if fail: + sys.exit(1) sys.exit(0) From 588ffa75eba30baf75d1ba246c5f917e8716fcaa Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 29 Aug 2025 10:53:55 +0200 Subject: [PATCH 0448/2396] target/ppc: limit cpu_interrupt_exittb to system emulation It is not used by user-mode emulation and is the only caller of cpu_interrupt() in qemu-ppc* binaries. Reviewed-by: Igor Mammedov Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- target/ppc/helper_regs.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/target/ppc/helper_regs.c b/target/ppc/helper_regs.c index 7e5726871e..5f21739749 100644 --- a/target/ppc/helper_regs.c +++ b/target/ppc/helper_regs.c @@ -274,6 +274,7 @@ TCGTBCPUState ppc_get_tb_cpu_state(CPUState *cs) return (TCGTBCPUState){ .pc = env->nip, .flags = hflags_current }; } +#ifndef CONFIG_USER_ONLY void cpu_interrupt_exittb(CPUState *cs) { /* @@ -285,6 +286,7 @@ void cpu_interrupt_exittb(CPUState *cs) cpu_interrupt(cs, CPU_INTERRUPT_EXITTB); } } +#endif int hreg_store_msr(CPUPPCState *env, target_ulong value, int alter_hv) { From a445d3b85c4119aac7ecb7771b5da0709ee1f1a4 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 29 Aug 2025 10:53:55 +0200 Subject: [PATCH 0449/2396] target/sparc: limit cpu_check_irqs to system emulation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It is not used by user-mode emulation and is the only caller of cpu_interrupt() in qemu-sparc* binaries. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- target/sparc/int32_helper.c | 2 ++ target/sparc/int64_helper.c | 2 ++ 2 files changed, 4 insertions(+) diff --git a/target/sparc/int32_helper.c b/target/sparc/int32_helper.c index 39db4ffa70..fdcaa0a578 100644 --- a/target/sparc/int32_helper.c +++ b/target/sparc/int32_helper.c @@ -65,6 +65,7 @@ static const char *excp_name_str(int32_t exception_index) return excp_names[exception_index]; } +#if !defined(CONFIG_USER_ONLY) void cpu_check_irqs(CPUSPARCState *env) { CPUState *cs; @@ -96,6 +97,7 @@ void cpu_check_irqs(CPUSPARCState *env) cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); } } +#endif void sparc_cpu_do_interrupt(CPUState *cs) { diff --git a/target/sparc/int64_helper.c b/target/sparc/int64_helper.c index 49e4e51c6d..23adda4cad 100644 --- a/target/sparc/int64_helper.c +++ b/target/sparc/int64_helper.c @@ -62,6 +62,7 @@ static const char * const excp_names[0x80] = { }; #endif +#if !defined(CONFIG_USER_ONLY) void cpu_check_irqs(CPUSPARCState *env) { CPUState *cs; @@ -127,6 +128,7 @@ void cpu_check_irqs(CPUSPARCState *env) cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); } } +#endif void sparc_cpu_do_interrupt(CPUState *cs) { From 3efe1a0f604cb7c4afd0381294acbdec75c65325 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 29 Aug 2025 10:43:44 +0200 Subject: [PATCH 0450/2396] target/i386: limit a20 to system emulation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It is not used by user-mode emulation and is the only caller of cpu_interrupt() in qemu-i386 and qemu-x86_64. Reviewed-by: Igor Mammedov Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- target/i386/helper.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/target/i386/helper.c b/target/i386/helper.c index e0aaed3c4c..651041ccfa 100644 --- a/target/i386/helper.c +++ b/target/i386/helper.c @@ -110,6 +110,7 @@ int cpu_x86_support_mca_broadcast(CPUX86State *env) /* x86 mmu */ /* XXX: add PGE support */ +#ifndef CONFIG_USER_ONLY void x86_cpu_set_a20(X86CPU *cpu, int a20_state) { CPUX86State *env = &cpu->env; @@ -129,6 +130,7 @@ void x86_cpu_set_a20(X86CPU *cpu, int a20_state) env->a20_mask = ~(1 << 20) | (a20_state << 20); } } +#endif void cpu_x86_update_cr0(CPUX86State *env, uint32_t new_cr0) { From bd1cefdd9f18bfbdcb597d7d552fbf31dee47a28 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 29 Aug 2025 12:43:55 +0200 Subject: [PATCH 0451/2396] target-arm: remove uses of cpu_interrupt() for user-mode emulation Arm leaves around some functions that use cpu_interrupt(), even for user-mode emulation when the code is unreachable. Pull out the system-mode implementation to a separate file, and add stubs for CONFIG_USER_ONLY. Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- target/arm/cpu-irq.c | 381 +++++++++++++++++++++++++++++++++++++++++ target/arm/cpu.c | 370 --------------------------------------- target/arm/el2-stubs.c | 37 ++++ target/arm/helper.c | 4 + target/arm/internals.h | 5 + target/arm/meson.build | 2 + 6 files changed, 429 insertions(+), 370 deletions(-) create mode 100644 target/arm/cpu-irq.c create mode 100644 target/arm/el2-stubs.c diff --git a/target/arm/cpu-irq.c b/target/arm/cpu-irq.c new file mode 100644 index 0000000000..fe514cc93a --- /dev/null +++ b/target/arm/cpu-irq.c @@ -0,0 +1,381 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/* + * QEMU ARM CPU - interrupt_request handling + * + * Copyright (c) 2003-2025 QEMU contributors + */ + +#include "qemu/osdep.h" +#include "cpu.h" +#include "accel/tcg/cpu-ops.h" +#include "internals.h" + +#ifdef CONFIG_TCG +static inline bool arm_excp_unmasked(CPUState *cs, unsigned int excp_idx, + unsigned int target_el, + unsigned int cur_el, bool secure, + uint64_t hcr_el2) +{ + CPUARMState *env = cpu_env(cs); + bool pstate_unmasked; + bool unmasked = false; + bool allIntMask = false; + + /* + * Don't take exceptions if they target a lower EL. + * This check should catch any exceptions that would not be taken + * but left pending. + */ + if (cur_el > target_el) { + return false; + } + + if (cpu_isar_feature(aa64_nmi, env_archcpu(env)) && + env->cp15.sctlr_el[target_el] & SCTLR_NMI && cur_el == target_el) { + allIntMask = env->pstate & PSTATE_ALLINT || + ((env->cp15.sctlr_el[target_el] & SCTLR_SPINTMASK) && + (env->pstate & PSTATE_SP)); + } + + switch (excp_idx) { + case EXCP_NMI: + pstate_unmasked = !allIntMask; + break; + + case EXCP_VINMI: + if (!(hcr_el2 & HCR_IMO) || (hcr_el2 & HCR_TGE)) { + /* VINMIs are only taken when hypervized. */ + return false; + } + return !allIntMask; + case EXCP_VFNMI: + if (!(hcr_el2 & HCR_FMO) || (hcr_el2 & HCR_TGE)) { + /* VFNMIs are only taken when hypervized. */ + return false; + } + return !allIntMask; + case EXCP_FIQ: + pstate_unmasked = (!(env->daif & PSTATE_F)) && (!allIntMask); + break; + + case EXCP_IRQ: + pstate_unmasked = (!(env->daif & PSTATE_I)) && (!allIntMask); + break; + + case EXCP_VFIQ: + if (!(hcr_el2 & HCR_FMO) || (hcr_el2 & HCR_TGE)) { + /* VFIQs are only taken when hypervized. */ + return false; + } + return !(env->daif & PSTATE_F) && (!allIntMask); + case EXCP_VIRQ: + if (!(hcr_el2 & HCR_IMO) || (hcr_el2 & HCR_TGE)) { + /* VIRQs are only taken when hypervized. */ + return false; + } + return !(env->daif & PSTATE_I) && (!allIntMask); + case EXCP_VSERR: + if (!(hcr_el2 & HCR_AMO) || (hcr_el2 & HCR_TGE)) { + /* VIRQs are only taken when hypervized. */ + return false; + } + return !(env->daif & PSTATE_A); + default: + g_assert_not_reached(); + } + + /* + * Use the target EL, current execution state and SCR/HCR settings to + * determine whether the corresponding CPSR bit is used to mask the + * interrupt. + */ + if ((target_el > cur_el) && (target_el != 1)) { + /* Exceptions targeting a higher EL may not be maskable */ + if (arm_feature(env, ARM_FEATURE_AARCH64)) { + switch (target_el) { + case 2: + /* + * According to ARM DDI 0487H.a, an interrupt can be masked + * when HCR_E2H and HCR_TGE are both set regardless of the + * current Security state. Note that we need to revisit this + * part again once we need to support NMI. + */ + if ((hcr_el2 & (HCR_E2H | HCR_TGE)) != (HCR_E2H | HCR_TGE)) { + unmasked = true; + } + break; + case 3: + /* Interrupt cannot be masked when the target EL is 3 */ + unmasked = true; + break; + default: + g_assert_not_reached(); + } + } else { + /* + * The old 32-bit-only environment has a more complicated + * masking setup. HCR and SCR bits not only affect interrupt + * routing but also change the behaviour of masking. + */ + bool hcr, scr; + + switch (excp_idx) { + case EXCP_FIQ: + /* + * If FIQs are routed to EL3 or EL2 then there are cases where + * we override the CPSR.F in determining if the exception is + * masked or not. If neither of these are set then we fall back + * to the CPSR.F setting otherwise we further assess the state + * below. + */ + hcr = hcr_el2 & HCR_FMO; + scr = (env->cp15.scr_el3 & SCR_FIQ); + + /* + * When EL3 is 32-bit, the SCR.FW bit controls whether the + * CPSR.F bit masks FIQ interrupts when taken in non-secure + * state. If SCR.FW is set then FIQs can be masked by CPSR.F + * when non-secure but only when FIQs are only routed to EL3. + */ + scr = scr && !((env->cp15.scr_el3 & SCR_FW) && !hcr); + break; + case EXCP_IRQ: + /* + * When EL3 execution state is 32-bit, if HCR.IMO is set then + * we may override the CPSR.I masking when in non-secure state. + * The SCR.IRQ setting has already been taken into consideration + * when setting the target EL, so it does not have a further + * affect here. + */ + hcr = hcr_el2 & HCR_IMO; + scr = false; + break; + default: + g_assert_not_reached(); + } + + if ((scr || hcr) && !secure) { + unmasked = true; + } + } + } + + /* + * The PSTATE bits only mask the interrupt if we have not overridden the + * ability above. + */ + return unmasked || pstate_unmasked; +} + +bool arm_cpu_exec_interrupt(CPUState *cs, int interrupt_request) +{ + CPUARMState *env = cpu_env(cs); + uint32_t cur_el = arm_current_el(env); + bool secure = arm_is_secure(env); + uint64_t hcr_el2 = arm_hcr_el2_eff(env); + uint32_t target_el; + uint32_t excp_idx; + + /* The prioritization of interrupts is IMPLEMENTATION DEFINED. */ + + if (cpu_isar_feature(aa64_nmi, env_archcpu(env)) && + (arm_sctlr(env, cur_el) & SCTLR_NMI)) { + if (interrupt_request & CPU_INTERRUPT_NMI) { + excp_idx = EXCP_NMI; + target_el = arm_phys_excp_target_el(cs, excp_idx, cur_el, secure); + if (arm_excp_unmasked(cs, excp_idx, target_el, + cur_el, secure, hcr_el2)) { + goto found; + } + } + if (interrupt_request & CPU_INTERRUPT_VINMI) { + excp_idx = EXCP_VINMI; + target_el = 1; + if (arm_excp_unmasked(cs, excp_idx, target_el, + cur_el, secure, hcr_el2)) { + goto found; + } + } + if (interrupt_request & CPU_INTERRUPT_VFNMI) { + excp_idx = EXCP_VFNMI; + target_el = 1; + if (arm_excp_unmasked(cs, excp_idx, target_el, + cur_el, secure, hcr_el2)) { + goto found; + } + } + } else { + /* + * NMI disabled: interrupts with superpriority are handled + * as if they didn't have it + */ + if (interrupt_request & CPU_INTERRUPT_NMI) { + interrupt_request |= CPU_INTERRUPT_HARD; + } + if (interrupt_request & CPU_INTERRUPT_VINMI) { + interrupt_request |= CPU_INTERRUPT_VIRQ; + } + if (interrupt_request & CPU_INTERRUPT_VFNMI) { + interrupt_request |= CPU_INTERRUPT_VFIQ; + } + } + + if (interrupt_request & CPU_INTERRUPT_FIQ) { + excp_idx = EXCP_FIQ; + target_el = arm_phys_excp_target_el(cs, excp_idx, cur_el, secure); + if (arm_excp_unmasked(cs, excp_idx, target_el, + cur_el, secure, hcr_el2)) { + goto found; + } + } + if (interrupt_request & CPU_INTERRUPT_HARD) { + excp_idx = EXCP_IRQ; + target_el = arm_phys_excp_target_el(cs, excp_idx, cur_el, secure); + if (arm_excp_unmasked(cs, excp_idx, target_el, + cur_el, secure, hcr_el2)) { + goto found; + } + } + if (interrupt_request & CPU_INTERRUPT_VIRQ) { + excp_idx = EXCP_VIRQ; + target_el = 1; + if (arm_excp_unmasked(cs, excp_idx, target_el, + cur_el, secure, hcr_el2)) { + goto found; + } + } + if (interrupt_request & CPU_INTERRUPT_VFIQ) { + excp_idx = EXCP_VFIQ; + target_el = 1; + if (arm_excp_unmasked(cs, excp_idx, target_el, + cur_el, secure, hcr_el2)) { + goto found; + } + } + if (interrupt_request & CPU_INTERRUPT_VSERR) { + excp_idx = EXCP_VSERR; + target_el = 1; + if (arm_excp_unmasked(cs, excp_idx, target_el, + cur_el, secure, hcr_el2)) { + /* Taking a virtual abort clears HCR_EL2.VSE */ + env->cp15.hcr_el2 &= ~HCR_VSE; + cpu_reset_interrupt(cs, CPU_INTERRUPT_VSERR); + goto found; + } + } + return false; + + found: + cs->exception_index = excp_idx; + env->exception.target_el = target_el; + cs->cc->tcg_ops->do_interrupt(cs); + return true; +} +#endif /* CONFIG_TCG */ + +void arm_cpu_update_virq(ARMCPU *cpu) +{ + /* + * Update the interrupt level for VIRQ, which is the logical OR of + * the HCR_EL2.VI bit and the input line level from the GIC. + */ + CPUARMState *env = &cpu->env; + CPUState *cs = CPU(cpu); + + bool new_state = ((arm_hcr_el2_eff(env) & HCR_VI) && + !(arm_hcrx_el2_eff(env) & HCRX_VINMI)) || + (env->irq_line_state & CPU_INTERRUPT_VIRQ); + + if (new_state != cpu_test_interrupt(cs, CPU_INTERRUPT_VIRQ)) { + if (new_state) { + cpu_interrupt(cs, CPU_INTERRUPT_VIRQ); + } else { + cpu_reset_interrupt(cs, CPU_INTERRUPT_VIRQ); + } + } +} + +void arm_cpu_update_vfiq(ARMCPU *cpu) +{ + /* + * Update the interrupt level for VFIQ, which is the logical OR of + * the HCR_EL2.VF bit and the input line level from the GIC. + */ + CPUARMState *env = &cpu->env; + CPUState *cs = CPU(cpu); + + bool new_state = ((arm_hcr_el2_eff(env) & HCR_VF) && + !(arm_hcrx_el2_eff(env) & HCRX_VFNMI)) || + (env->irq_line_state & CPU_INTERRUPT_VFIQ); + + if (new_state != cpu_test_interrupt(cs, CPU_INTERRUPT_VFIQ)) { + if (new_state) { + cpu_interrupt(cs, CPU_INTERRUPT_VFIQ); + } else { + cpu_reset_interrupt(cs, CPU_INTERRUPT_VFIQ); + } + } +} + +void arm_cpu_update_vinmi(ARMCPU *cpu) +{ + /* + * Update the interrupt level for VINMI, which is the logical OR of + * the HCRX_EL2.VINMI bit and the input line level from the GIC. + */ + CPUARMState *env = &cpu->env; + CPUState *cs = CPU(cpu); + + bool new_state = ((arm_hcr_el2_eff(env) & HCR_VI) && + (arm_hcrx_el2_eff(env) & HCRX_VINMI)) || + (env->irq_line_state & CPU_INTERRUPT_VINMI); + + if (new_state != cpu_test_interrupt(cs, CPU_INTERRUPT_VINMI)) { + if (new_state) { + cpu_interrupt(cs, CPU_INTERRUPT_VINMI); + } else { + cpu_reset_interrupt(cs, CPU_INTERRUPT_VINMI); + } + } +} + +void arm_cpu_update_vfnmi(ARMCPU *cpu) +{ + /* + * Update the interrupt level for VFNMI, which is the HCRX_EL2.VFNMI bit. + */ + CPUARMState *env = &cpu->env; + CPUState *cs = CPU(cpu); + + bool new_state = (arm_hcr_el2_eff(env) & HCR_VF) && + (arm_hcrx_el2_eff(env) & HCRX_VFNMI); + + if (new_state != cpu_test_interrupt(cs, CPU_INTERRUPT_VFNMI)) { + if (new_state) { + cpu_interrupt(cs, CPU_INTERRUPT_VFNMI); + } else { + cpu_reset_interrupt(cs, CPU_INTERRUPT_VFNMI); + } + } +} + +void arm_cpu_update_vserr(ARMCPU *cpu) +{ + /* + * Update the interrupt level for VSERR, which is the HCR_EL2.VSE bit. + */ + CPUARMState *env = &cpu->env; + CPUState *cs = CPU(cpu); + + bool new_state = env->cp15.hcr_el2 & HCR_VSE; + + if (new_state != cpu_test_interrupt(cs, CPU_INTERRUPT_VSERR)) { + if (new_state) { + cpu_interrupt(cs, CPU_INTERRUPT_VSERR); + } else { + cpu_reset_interrupt(cs, CPU_INTERRUPT_VSERR); + } + } +} + diff --git a/target/arm/cpu.c b/target/arm/cpu.c index d0f6fcdfce..633ec55a57 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -686,376 +686,6 @@ void arm_emulate_firmware_reset(CPUState *cpustate, int target_el) } -#if defined(CONFIG_TCG) && !defined(CONFIG_USER_ONLY) - -static inline bool arm_excp_unmasked(CPUState *cs, unsigned int excp_idx, - unsigned int target_el, - unsigned int cur_el, bool secure, - uint64_t hcr_el2) -{ - CPUARMState *env = cpu_env(cs); - bool pstate_unmasked; - bool unmasked = false; - bool allIntMask = false; - - /* - * Don't take exceptions if they target a lower EL. - * This check should catch any exceptions that would not be taken - * but left pending. - */ - if (cur_el > target_el) { - return false; - } - - if (cpu_isar_feature(aa64_nmi, env_archcpu(env)) && - env->cp15.sctlr_el[target_el] & SCTLR_NMI && cur_el == target_el) { - allIntMask = env->pstate & PSTATE_ALLINT || - ((env->cp15.sctlr_el[target_el] & SCTLR_SPINTMASK) && - (env->pstate & PSTATE_SP)); - } - - switch (excp_idx) { - case EXCP_NMI: - pstate_unmasked = !allIntMask; - break; - - case EXCP_VINMI: - if (!(hcr_el2 & HCR_IMO) || (hcr_el2 & HCR_TGE)) { - /* VINMIs are only taken when hypervized. */ - return false; - } - return !allIntMask; - case EXCP_VFNMI: - if (!(hcr_el2 & HCR_FMO) || (hcr_el2 & HCR_TGE)) { - /* VFNMIs are only taken when hypervized. */ - return false; - } - return !allIntMask; - case EXCP_FIQ: - pstate_unmasked = (!(env->daif & PSTATE_F)) && (!allIntMask); - break; - - case EXCP_IRQ: - pstate_unmasked = (!(env->daif & PSTATE_I)) && (!allIntMask); - break; - - case EXCP_VFIQ: - if (!(hcr_el2 & HCR_FMO) || (hcr_el2 & HCR_TGE)) { - /* VFIQs are only taken when hypervized. */ - return false; - } - return !(env->daif & PSTATE_F) && (!allIntMask); - case EXCP_VIRQ: - if (!(hcr_el2 & HCR_IMO) || (hcr_el2 & HCR_TGE)) { - /* VIRQs are only taken when hypervized. */ - return false; - } - return !(env->daif & PSTATE_I) && (!allIntMask); - case EXCP_VSERR: - if (!(hcr_el2 & HCR_AMO) || (hcr_el2 & HCR_TGE)) { - /* VIRQs are only taken when hypervized. */ - return false; - } - return !(env->daif & PSTATE_A); - default: - g_assert_not_reached(); - } - - /* - * Use the target EL, current execution state and SCR/HCR settings to - * determine whether the corresponding CPSR bit is used to mask the - * interrupt. - */ - if ((target_el > cur_el) && (target_el != 1)) { - /* Exceptions targeting a higher EL may not be maskable */ - if (arm_feature(env, ARM_FEATURE_AARCH64)) { - switch (target_el) { - case 2: - /* - * According to ARM DDI 0487H.a, an interrupt can be masked - * when HCR_E2H and HCR_TGE are both set regardless of the - * current Security state. Note that we need to revisit this - * part again once we need to support NMI. - */ - if ((hcr_el2 & (HCR_E2H | HCR_TGE)) != (HCR_E2H | HCR_TGE)) { - unmasked = true; - } - break; - case 3: - /* Interrupt cannot be masked when the target EL is 3 */ - unmasked = true; - break; - default: - g_assert_not_reached(); - } - } else { - /* - * The old 32-bit-only environment has a more complicated - * masking setup. HCR and SCR bits not only affect interrupt - * routing but also change the behaviour of masking. - */ - bool hcr, scr; - - switch (excp_idx) { - case EXCP_FIQ: - /* - * If FIQs are routed to EL3 or EL2 then there are cases where - * we override the CPSR.F in determining if the exception is - * masked or not. If neither of these are set then we fall back - * to the CPSR.F setting otherwise we further assess the state - * below. - */ - hcr = hcr_el2 & HCR_FMO; - scr = (env->cp15.scr_el3 & SCR_FIQ); - - /* - * When EL3 is 32-bit, the SCR.FW bit controls whether the - * CPSR.F bit masks FIQ interrupts when taken in non-secure - * state. If SCR.FW is set then FIQs can be masked by CPSR.F - * when non-secure but only when FIQs are only routed to EL3. - */ - scr = scr && !((env->cp15.scr_el3 & SCR_FW) && !hcr); - break; - case EXCP_IRQ: - /* - * When EL3 execution state is 32-bit, if HCR.IMO is set then - * we may override the CPSR.I masking when in non-secure state. - * The SCR.IRQ setting has already been taken into consideration - * when setting the target EL, so it does not have a further - * affect here. - */ - hcr = hcr_el2 & HCR_IMO; - scr = false; - break; - default: - g_assert_not_reached(); - } - - if ((scr || hcr) && !secure) { - unmasked = true; - } - } - } - - /* - * The PSTATE bits only mask the interrupt if we have not overridden the - * ability above. - */ - return unmasked || pstate_unmasked; -} - -static bool arm_cpu_exec_interrupt(CPUState *cs, int interrupt_request) -{ - CPUARMState *env = cpu_env(cs); - uint32_t cur_el = arm_current_el(env); - bool secure = arm_is_secure(env); - uint64_t hcr_el2 = arm_hcr_el2_eff(env); - uint32_t target_el; - uint32_t excp_idx; - - /* The prioritization of interrupts is IMPLEMENTATION DEFINED. */ - - if (cpu_isar_feature(aa64_nmi, env_archcpu(env)) && - (arm_sctlr(env, cur_el) & SCTLR_NMI)) { - if (interrupt_request & CPU_INTERRUPT_NMI) { - excp_idx = EXCP_NMI; - target_el = arm_phys_excp_target_el(cs, excp_idx, cur_el, secure); - if (arm_excp_unmasked(cs, excp_idx, target_el, - cur_el, secure, hcr_el2)) { - goto found; - } - } - if (interrupt_request & CPU_INTERRUPT_VINMI) { - excp_idx = EXCP_VINMI; - target_el = 1; - if (arm_excp_unmasked(cs, excp_idx, target_el, - cur_el, secure, hcr_el2)) { - goto found; - } - } - if (interrupt_request & CPU_INTERRUPT_VFNMI) { - excp_idx = EXCP_VFNMI; - target_el = 1; - if (arm_excp_unmasked(cs, excp_idx, target_el, - cur_el, secure, hcr_el2)) { - goto found; - } - } - } else { - /* - * NMI disabled: interrupts with superpriority are handled - * as if they didn't have it - */ - if (interrupt_request & CPU_INTERRUPT_NMI) { - interrupt_request |= CPU_INTERRUPT_HARD; - } - if (interrupt_request & CPU_INTERRUPT_VINMI) { - interrupt_request |= CPU_INTERRUPT_VIRQ; - } - if (interrupt_request & CPU_INTERRUPT_VFNMI) { - interrupt_request |= CPU_INTERRUPT_VFIQ; - } - } - - if (interrupt_request & CPU_INTERRUPT_FIQ) { - excp_idx = EXCP_FIQ; - target_el = arm_phys_excp_target_el(cs, excp_idx, cur_el, secure); - if (arm_excp_unmasked(cs, excp_idx, target_el, - cur_el, secure, hcr_el2)) { - goto found; - } - } - if (interrupt_request & CPU_INTERRUPT_HARD) { - excp_idx = EXCP_IRQ; - target_el = arm_phys_excp_target_el(cs, excp_idx, cur_el, secure); - if (arm_excp_unmasked(cs, excp_idx, target_el, - cur_el, secure, hcr_el2)) { - goto found; - } - } - if (interrupt_request & CPU_INTERRUPT_VIRQ) { - excp_idx = EXCP_VIRQ; - target_el = 1; - if (arm_excp_unmasked(cs, excp_idx, target_el, - cur_el, secure, hcr_el2)) { - goto found; - } - } - if (interrupt_request & CPU_INTERRUPT_VFIQ) { - excp_idx = EXCP_VFIQ; - target_el = 1; - if (arm_excp_unmasked(cs, excp_idx, target_el, - cur_el, secure, hcr_el2)) { - goto found; - } - } - if (interrupt_request & CPU_INTERRUPT_VSERR) { - excp_idx = EXCP_VSERR; - target_el = 1; - if (arm_excp_unmasked(cs, excp_idx, target_el, - cur_el, secure, hcr_el2)) { - /* Taking a virtual abort clears HCR_EL2.VSE */ - env->cp15.hcr_el2 &= ~HCR_VSE; - cpu_reset_interrupt(cs, CPU_INTERRUPT_VSERR); - goto found; - } - } - return false; - - found: - cs->exception_index = excp_idx; - env->exception.target_el = target_el; - cs->cc->tcg_ops->do_interrupt(cs); - return true; -} - -#endif /* CONFIG_TCG && !CONFIG_USER_ONLY */ - -void arm_cpu_update_virq(ARMCPU *cpu) -{ - /* - * Update the interrupt level for VIRQ, which is the logical OR of - * the HCR_EL2.VI bit and the input line level from the GIC. - */ - CPUARMState *env = &cpu->env; - CPUState *cs = CPU(cpu); - - bool new_state = ((arm_hcr_el2_eff(env) & HCR_VI) && - !(arm_hcrx_el2_eff(env) & HCRX_VINMI)) || - (env->irq_line_state & CPU_INTERRUPT_VIRQ); - - if (new_state != cpu_test_interrupt(cs, CPU_INTERRUPT_VIRQ)) { - if (new_state) { - cpu_interrupt(cs, CPU_INTERRUPT_VIRQ); - } else { - cpu_reset_interrupt(cs, CPU_INTERRUPT_VIRQ); - } - } -} - -void arm_cpu_update_vfiq(ARMCPU *cpu) -{ - /* - * Update the interrupt level for VFIQ, which is the logical OR of - * the HCR_EL2.VF bit and the input line level from the GIC. - */ - CPUARMState *env = &cpu->env; - CPUState *cs = CPU(cpu); - - bool new_state = ((arm_hcr_el2_eff(env) & HCR_VF) && - !(arm_hcrx_el2_eff(env) & HCRX_VFNMI)) || - (env->irq_line_state & CPU_INTERRUPT_VFIQ); - - if (new_state != cpu_test_interrupt(cs, CPU_INTERRUPT_VFIQ)) { - if (new_state) { - cpu_interrupt(cs, CPU_INTERRUPT_VFIQ); - } else { - cpu_reset_interrupt(cs, CPU_INTERRUPT_VFIQ); - } - } -} - -void arm_cpu_update_vinmi(ARMCPU *cpu) -{ - /* - * Update the interrupt level for VINMI, which is the logical OR of - * the HCRX_EL2.VINMI bit and the input line level from the GIC. - */ - CPUARMState *env = &cpu->env; - CPUState *cs = CPU(cpu); - - bool new_state = ((arm_hcr_el2_eff(env) & HCR_VI) && - (arm_hcrx_el2_eff(env) & HCRX_VINMI)) || - (env->irq_line_state & CPU_INTERRUPT_VINMI); - - if (new_state != cpu_test_interrupt(cs, CPU_INTERRUPT_VINMI)) { - if (new_state) { - cpu_interrupt(cs, CPU_INTERRUPT_VINMI); - } else { - cpu_reset_interrupt(cs, CPU_INTERRUPT_VINMI); - } - } -} - -void arm_cpu_update_vfnmi(ARMCPU *cpu) -{ - /* - * Update the interrupt level for VFNMI, which is the HCRX_EL2.VFNMI bit. - */ - CPUARMState *env = &cpu->env; - CPUState *cs = CPU(cpu); - - bool new_state = (arm_hcr_el2_eff(env) & HCR_VF) && - (arm_hcrx_el2_eff(env) & HCRX_VFNMI); - - if (new_state != cpu_test_interrupt(cs, CPU_INTERRUPT_VFNMI)) { - if (new_state) { - cpu_interrupt(cs, CPU_INTERRUPT_VFNMI); - } else { - cpu_reset_interrupt(cs, CPU_INTERRUPT_VFNMI); - } - } -} - -void arm_cpu_update_vserr(ARMCPU *cpu) -{ - /* - * Update the interrupt level for VSERR, which is the HCR_EL2.VSE bit. - */ - CPUARMState *env = &cpu->env; - CPUState *cs = CPU(cpu); - - bool new_state = env->cp15.hcr_el2 & HCR_VSE; - - if (new_state != cpu_test_interrupt(cs, CPU_INTERRUPT_VSERR)) { - if (new_state) { - cpu_interrupt(cs, CPU_INTERRUPT_VSERR); - } else { - cpu_reset_interrupt(cs, CPU_INTERRUPT_VSERR); - } - } -} - #ifndef CONFIG_USER_ONLY static void arm_cpu_set_irq(void *opaque, int irq, int level) { diff --git a/target/arm/el2-stubs.c b/target/arm/el2-stubs.c new file mode 100644 index 0000000000..972023c337 --- /dev/null +++ b/target/arm/el2-stubs.c @@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/* QEMU ARM CPU - user-mode emulation stubs for EL2 interrupts + * + * These should not really be needed, but CP registers for EL2 + * are not elided by user-mode emulation and they call these + * functions. Leave them as stubs until it's cleaned up. + */ + +#include "qemu/osdep.h" +#include "cpu.h" +#include "internals.h" + +void arm_cpu_update_virq(ARMCPU *cpu) +{ + g_assert_not_reached(); +} + +void arm_cpu_update_vfiq(ARMCPU *cpu) +{ + g_assert_not_reached(); +} + +void arm_cpu_update_vinmi(ARMCPU *cpu) +{ + g_assert_not_reached(); +} + +void arm_cpu_update_vfnmi(ARMCPU *cpu) +{ + g_assert_not_reached(); +} + +void arm_cpu_update_vserr(ARMCPU *cpu) +{ + g_assert_not_reached(); +} diff --git a/target/arm/helper.c b/target/arm/helper.c index 19637e7301..fb62742d98 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -2868,8 +2868,12 @@ static void omap_threadid_write(CPUARMState *env, const ARMCPRegInfo *ri, static void omap_wfi_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { +#ifdef CONFIG_USER_ONLY + g_assert_not_reached(); +#else /* Wait-for-interrupt (deprecated) */ cpu_interrupt(env_cpu(env), CPU_INTERRUPT_HALT); +#endif } static void omap_cachemaint_write(CPUARMState *env, const ARMCPRegInfo *ri, diff --git a/target/arm/internals.h b/target/arm/internals.h index f5a1e75db3..f0aa26c511 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -1292,6 +1292,11 @@ static inline const char *aarch32_mode_name(uint32_t psr) return cpu_mode_names[psr & 0xf]; } +/** + * arm_cpu_exec_interrupt(): Implementation of the cpu_exec_inrerrupt hook. + */ +bool arm_cpu_exec_interrupt(CPUState *cs, int interrupt_request); + /** * arm_cpu_update_virq: Update CPU_INTERRUPT_VIRQ bit in cs->interrupt_request * diff --git a/target/arm/meson.build b/target/arm/meson.build index 07d9271aa4..914f1498fc 100644 --- a/target/arm/meson.build +++ b/target/arm/meson.build @@ -26,6 +26,7 @@ arm_user_ss.add(files( 'debug_helper.c', 'helper.c', 'vfp_fpscr.c', + 'el2-stubs.c', )) arm_common_system_ss.add(files('cpu.c')) @@ -38,6 +39,7 @@ arm_common_system_ss.add(files( 'arm-powerctl.c', 'cortex-regs.c', 'cpregs-pmu.c', + 'cpu-irq.c', 'debug_helper.c', 'helper.c', 'machine.c', From 11a73c6ea37483f7be85f6afebb4334d97d3050c Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 29 Aug 2025 12:49:00 +0200 Subject: [PATCH 0452/2396] user-exec: remove cpu_interrupt() stub MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- accel/tcg/user-exec.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/accel/tcg/user-exec.c b/accel/tcg/user-exec.c index 748bfab04a..66c25fba7d 100644 --- a/accel/tcg/user-exec.c +++ b/accel/tcg/user-exec.c @@ -46,11 +46,6 @@ __thread uintptr_t helper_retaddr; //#define DEBUG_SIGNAL -void cpu_interrupt(CPUState *cpu, int mask) -{ - g_assert_not_reached(); -} - /* * Adjust the pc to pass to cpu_restore_state; return the memop type. */ From 602d5ebba26b245730a0b6a4855b1812d587725c Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 29 Aug 2025 12:09:09 +0200 Subject: [PATCH 0453/2396] treewide: clear bits of cs->interrupt_request with cpu_reset_interrupt() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Open coding cpu_reset_interrupt() can cause bugs if the BQL is not taken, for example i386 has the call chain kvm_cpu_exec() -> kvm_put_vcpu_events() -> kvm_arch_put_registers(). Reviewed-by: Igor Mammedov Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- accel/tcg/cpu-exec.c | 6 +++--- hw/core/cpu-system.c | 2 +- target/avr/helper.c | 4 ++-- target/i386/hvf/x86hvf.c | 8 ++++---- target/i386/kvm/kvm.c | 14 +++++++------- target/i386/nvmm/nvmm-all.c | 10 +++++----- target/i386/tcg/system/seg_helper.c | 13 ++++++------- target/i386/tcg/system/svm_helper.c | 2 +- target/i386/whpx/whpx-all.c | 12 ++++++------ target/openrisc/sys_helper.c | 2 +- target/rx/helper.c | 4 ++-- target/s390x/tcg/excp_helper.c | 2 +- 12 files changed, 39 insertions(+), 40 deletions(-) diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index 8491e5badd..508d2d2d9e 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -784,7 +784,7 @@ static inline bool cpu_handle_interrupt(CPUState *cpu, if (unlikely(cpu_test_interrupt(cpu, ~0))) { bql_lock(); if (cpu_test_interrupt(cpu, CPU_INTERRUPT_DEBUG)) { - cpu->interrupt_request &= ~CPU_INTERRUPT_DEBUG; + cpu_reset_interrupt(cpu, CPU_INTERRUPT_DEBUG); cpu->exception_index = EXCP_DEBUG; bql_unlock(); return true; @@ -793,7 +793,7 @@ static inline bool cpu_handle_interrupt(CPUState *cpu, /* Do nothing */ } else if (cpu_test_interrupt(cpu, CPU_INTERRUPT_HALT)) { replay_interrupt(); - cpu->interrupt_request &= ~CPU_INTERRUPT_HALT; + cpu_reset_interrupt(cpu, CPU_INTERRUPT_HALT); cpu->halted = 1; cpu->exception_index = EXCP_HLT; bql_unlock(); @@ -840,7 +840,7 @@ static inline bool cpu_handle_interrupt(CPUState *cpu, } } if (cpu_test_interrupt(cpu, CPU_INTERRUPT_EXITTB)) { - cpu->interrupt_request &= ~CPU_INTERRUPT_EXITTB; + cpu_reset_interrupt(cpu, CPU_INTERRUPT_EXITTB); /* ensure that no TB jump will be modified as the program flow was changed */ *last_tb = NULL; diff --git a/hw/core/cpu-system.c b/hw/core/cpu-system.c index a975405d3a..09c928c1f9 100644 --- a/hw/core/cpu-system.c +++ b/hw/core/cpu-system.c @@ -204,7 +204,7 @@ static int cpu_common_post_load(void *opaque, int version_id) * 0x01 was CPU_INTERRUPT_EXIT. This line can be removed when the * version_id is increased. */ - cpu->interrupt_request &= ~0x01; + cpu_reset_interrupt(cpu, 0x01); tlb_flush(cpu); diff --git a/target/avr/helper.c b/target/avr/helper.c index b9cd6d5ef2..4b29ab3526 100644 --- a/target/avr/helper.c +++ b/target/avr/helper.c @@ -47,7 +47,7 @@ bool avr_cpu_exec_interrupt(CPUState *cs, int interrupt_request) cs->exception_index = EXCP_RESET; avr_cpu_do_interrupt(cs); - cs->interrupt_request &= ~CPU_INTERRUPT_RESET; + cpu_reset_interrupt(cs, CPU_INTERRUPT_RESET); return true; } } @@ -59,7 +59,7 @@ bool avr_cpu_exec_interrupt(CPUState *cs, int interrupt_request) env->intsrc &= env->intsrc - 1; /* clear the interrupt */ if (!env->intsrc) { - cs->interrupt_request &= ~CPU_INTERRUPT_HARD; + cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); } return true; } diff --git a/target/i386/hvf/x86hvf.c b/target/i386/hvf/x86hvf.c index 9e05e0e576..a502437c30 100644 --- a/target/i386/hvf/x86hvf.c +++ b/target/i386/hvf/x86hvf.c @@ -397,7 +397,7 @@ bool hvf_inject_interrupts(CPUState *cs) if (cpu_test_interrupt(cs, CPU_INTERRUPT_NMI)) { if (!(env->hflags2 & HF2_NMI_MASK) && !(info & VMCS_INTR_VALID)) { - cs->interrupt_request &= ~CPU_INTERRUPT_NMI; + cpu_reset_interrupt(cs, CPU_INTERRUPT_NMI); info = VMCS_INTR_VALID | VMCS_INTR_T_NMI | EXCP02_NMI; wvmcs(cs->accel->fd, VMCS_ENTRY_INTR_INFO, info); } else { @@ -409,7 +409,7 @@ bool hvf_inject_interrupts(CPUState *cs) cpu_test_interrupt(cs, CPU_INTERRUPT_HARD) && (env->eflags & IF_MASK) && !(info & VMCS_INTR_VALID)) { int line = cpu_get_pic_interrupt(env); - cs->interrupt_request &= ~CPU_INTERRUPT_HARD; + cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); if (line >= 0) { wvmcs(cs->accel->fd, VMCS_ENTRY_INTR_INFO, line | VMCS_INTR_VALID | VMCS_INTR_T_HWINTR); @@ -437,7 +437,7 @@ int hvf_process_events(CPUState *cs) } if (cpu_test_interrupt(cs, CPU_INTERRUPT_POLL)) { - cs->interrupt_request &= ~CPU_INTERRUPT_POLL; + cpu_reset_interrupt(cs, CPU_INTERRUPT_POLL); apic_poll_irq(cpu->apic_state); } if ((cpu_test_interrupt(cs, CPU_INTERRUPT_HARD) && @@ -450,7 +450,7 @@ int hvf_process_events(CPUState *cs) do_cpu_sipi(cpu); } if (cpu_test_interrupt(cs, CPU_INTERRUPT_TPR)) { - cs->interrupt_request &= ~CPU_INTERRUPT_TPR; + cpu_reset_interrupt(cs, CPU_INTERRUPT_TPR); cpu_synchronize_state(cs); apic_handle_tpr_access_report(cpu->apic_state, env->eip, env->tpr_access_type); diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index 306430a052..8420c4090e 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -5066,7 +5066,7 @@ static int kvm_put_vcpu_events(X86CPU *cpu, int level) */ events.smi.pending = cs->interrupt_request & CPU_INTERRUPT_SMI; events.smi.latched_init = cs->interrupt_request & CPU_INTERRUPT_INIT; - cs->interrupt_request &= ~(CPU_INTERRUPT_INIT | CPU_INTERRUPT_SMI); + cpu_reset_interrupt(cs, CPU_INTERRUPT_INIT | CPU_INTERRUPT_SMI); } else { /* Keep these in cs->interrupt_request. */ events.smi.pending = 0; @@ -5456,7 +5456,7 @@ void kvm_arch_pre_run(CPUState *cpu, struct kvm_run *run) if (cpu_test_interrupt(cpu, CPU_INTERRUPT_NMI | CPU_INTERRUPT_SMI)) { if (cpu_test_interrupt(cpu, CPU_INTERRUPT_NMI)) { bql_lock(); - cpu->interrupt_request &= ~CPU_INTERRUPT_NMI; + cpu_reset_interrupt(cpu, CPU_INTERRUPT_NMI); bql_unlock(); DPRINTF("injected NMI\n"); ret = kvm_vcpu_ioctl(cpu, KVM_NMI); @@ -5467,7 +5467,7 @@ void kvm_arch_pre_run(CPUState *cpu, struct kvm_run *run) } if (cpu_test_interrupt(cpu, CPU_INTERRUPT_SMI)) { bql_lock(); - cpu->interrupt_request &= ~CPU_INTERRUPT_SMI; + cpu_reset_interrupt(cpu, CPU_INTERRUPT_SMI); bql_unlock(); DPRINTF("injected SMI\n"); ret = kvm_vcpu_ioctl(cpu, KVM_SMI); @@ -5502,7 +5502,7 @@ void kvm_arch_pre_run(CPUState *cpu, struct kvm_run *run) bql_lock(); - cpu->interrupt_request &= ~CPU_INTERRUPT_HARD; + cpu_reset_interrupt(cpu, CPU_INTERRUPT_HARD); irq = cpu_get_pic_interrupt(env); if (irq >= 0) { struct kvm_interrupt intr; @@ -5597,7 +5597,7 @@ int kvm_arch_process_async_events(CPUState *cs) /* We must not raise CPU_INTERRUPT_MCE if it's not supported. */ assert(env->mcg_cap); - cs->interrupt_request &= ~CPU_INTERRUPT_MCE; + cpu_reset_interrupt(cs, CPU_INTERRUPT_MCE); kvm_cpu_synchronize_state(cs); @@ -5627,7 +5627,7 @@ int kvm_arch_process_async_events(CPUState *cs) } if (cpu_test_interrupt(cs, CPU_INTERRUPT_POLL)) { - cs->interrupt_request &= ~CPU_INTERRUPT_POLL; + cpu_reset_interrupt(cs, CPU_INTERRUPT_POLL); apic_poll_irq(cpu->apic_state); } if ((cpu_test_interrupt(cs, CPU_INTERRUPT_HARD) && @@ -5640,7 +5640,7 @@ int kvm_arch_process_async_events(CPUState *cs) do_cpu_sipi(cpu); } if (cpu_test_interrupt(cs, CPU_INTERRUPT_TPR)) { - cs->interrupt_request &= ~CPU_INTERRUPT_TPR; + cpu_reset_interrupt(cs, CPU_INTERRUPT_TPR); kvm_cpu_synchronize_state(cs); apic_handle_tpr_access_report(cpu->apic_state, env->eip, env->tpr_access_type); diff --git a/target/i386/nvmm/nvmm-all.c b/target/i386/nvmm/nvmm-all.c index c1ac74c4f0..e1151b04c6 100644 --- a/target/i386/nvmm/nvmm-all.c +++ b/target/i386/nvmm/nvmm-all.c @@ -419,7 +419,7 @@ nvmm_vcpu_pre_run(CPUState *cpu) if (!has_event && cpu_test_interrupt(cpu, CPU_INTERRUPT_NMI)) { if (nvmm_can_take_nmi(cpu)) { - cpu->interrupt_request &= ~CPU_INTERRUPT_NMI; + cpu_reset_interrupt(cpu, CPU_INTERRUPT_NMI); event->type = NVMM_VCPU_EVENT_INTR; event->vector = 2; has_event = true; @@ -428,7 +428,7 @@ nvmm_vcpu_pre_run(CPUState *cpu) if (!has_event && cpu_test_interrupt(cpu, CPU_INTERRUPT_HARD)) { if (nvmm_can_take_int(cpu)) { - cpu->interrupt_request &= ~CPU_INTERRUPT_HARD; + cpu_reset_interrupt(cpu, CPU_INTERRUPT_HARD); event->type = NVMM_VCPU_EVENT_INTR; event->vector = cpu_get_pic_interrupt(env); has_event = true; @@ -437,7 +437,7 @@ nvmm_vcpu_pre_run(CPUState *cpu) /* Don't want SMIs. */ if (cpu_test_interrupt(cpu, CPU_INTERRUPT_SMI)) { - cpu->interrupt_request &= ~CPU_INTERRUPT_SMI; + cpu_reset_interrupt(cpu, CPU_INTERRUPT_SMI); } if (sync_tpr) { @@ -697,7 +697,7 @@ nvmm_vcpu_loop(CPUState *cpu) /* set int/nmi windows back to the reset state */ } if (cpu_test_interrupt(cpu, CPU_INTERRUPT_POLL)) { - cpu->interrupt_request &= ~CPU_INTERRUPT_POLL; + cpu_reset_interrupt(cpu, CPU_INTERRUPT_POLL); apic_poll_irq(x86_cpu->apic_state); } if ((cpu_test_interrupt(cpu, CPU_INTERRUPT_HARD) && @@ -710,7 +710,7 @@ nvmm_vcpu_loop(CPUState *cpu) do_cpu_sipi(x86_cpu); } if (cpu_test_interrupt(cpu, CPU_INTERRUPT_TPR)) { - cpu->interrupt_request &= ~CPU_INTERRUPT_TPR; + cpu_reset_interrupt(cpu, CPU_INTERRUPT_TPR); nvmm_cpu_synchronize_state(cpu); apic_handle_tpr_access_report(x86_cpu->apic_state, env->eip, env->tpr_access_type); diff --git a/target/i386/tcg/system/seg_helper.c b/target/i386/tcg/system/seg_helper.c index 794a23ddfc..38072e51d7 100644 --- a/target/i386/tcg/system/seg_helper.c +++ b/target/i386/tcg/system/seg_helper.c @@ -178,7 +178,7 @@ bool x86_cpu_exec_interrupt(CPUState *cs, int interrupt_request) */ switch (interrupt_request) { case CPU_INTERRUPT_POLL: - cs->interrupt_request &= ~CPU_INTERRUPT_POLL; + cpu_reset_interrupt(cs, CPU_INTERRUPT_POLL); apic_poll_irq(cpu->apic_state); break; case CPU_INTERRUPT_SIPI: @@ -186,23 +186,22 @@ bool x86_cpu_exec_interrupt(CPUState *cs, int interrupt_request) break; case CPU_INTERRUPT_SMI: cpu_svm_check_intercept_param(env, SVM_EXIT_SMI, 0, 0); - cs->interrupt_request &= ~CPU_INTERRUPT_SMI; + cpu_reset_interrupt(cs, CPU_INTERRUPT_SMI); do_smm_enter(cpu); break; case CPU_INTERRUPT_NMI: cpu_svm_check_intercept_param(env, SVM_EXIT_NMI, 0, 0); - cs->interrupt_request &= ~CPU_INTERRUPT_NMI; + cpu_reset_interrupt(cs, CPU_INTERRUPT_NMI); env->hflags2 |= HF2_NMI_MASK; do_interrupt_x86_hardirq(env, EXCP02_NMI, 1); break; case CPU_INTERRUPT_MCE: - cs->interrupt_request &= ~CPU_INTERRUPT_MCE; + cpu_reset_interrupt(cs, CPU_INTERRUPT_MCE); do_interrupt_x86_hardirq(env, EXCP12_MCHK, 0); break; case CPU_INTERRUPT_HARD: cpu_svm_check_intercept_param(env, SVM_EXIT_INTR, 0, 0); - cs->interrupt_request &= ~(CPU_INTERRUPT_HARD | - CPU_INTERRUPT_VIRQ); + cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD | CPU_INTERRUPT_VIRQ); intno = cpu_get_pic_interrupt(env); qemu_log_mask(CPU_LOG_INT, "Servicing hardware INT=0x%02x\n", intno); @@ -215,7 +214,7 @@ bool x86_cpu_exec_interrupt(CPUState *cs, int interrupt_request) qemu_log_mask(CPU_LOG_INT, "Servicing virtual hardware INT=0x%02x\n", intno); do_interrupt_x86_hardirq(env, intno, 1); - cs->interrupt_request &= ~CPU_INTERRUPT_VIRQ; + cpu_reset_interrupt(cs, CPU_INTERRUPT_VIRQ); env->int_ctl &= ~V_IRQ_MASK; break; } diff --git a/target/i386/tcg/system/svm_helper.c b/target/i386/tcg/system/svm_helper.c index 3569196bdd..505788b0e2 100644 --- a/target/i386/tcg/system/svm_helper.c +++ b/target/i386/tcg/system/svm_helper.c @@ -824,7 +824,7 @@ void do_vmexit(CPUX86State *env) env->intercept_exceptions = 0; /* Clears the V_IRQ and V_INTR_MASKING bits inside the processor. */ - cs->interrupt_request &= ~CPU_INTERRUPT_VIRQ; + cpu_reset_interrupt(cs, CPU_INTERRUPT_VIRQ); env->int_ctl = 0; /* Clears the TSC_OFFSET inside the processor. */ diff --git a/target/i386/whpx/whpx-all.c b/target/i386/whpx/whpx-all.c index 878cdd1668..c09a0a64f2 100644 --- a/target/i386/whpx/whpx-all.c +++ b/target/i386/whpx/whpx-all.c @@ -1471,14 +1471,14 @@ static void whpx_vcpu_pre_run(CPUState *cpu) if (!vcpu->interruption_pending && cpu_test_interrupt(cpu, CPU_INTERRUPT_NMI | CPU_INTERRUPT_SMI)) { if (cpu_test_interrupt(cpu, CPU_INTERRUPT_NMI)) { - cpu->interrupt_request &= ~CPU_INTERRUPT_NMI; + cpu_reset_interrupt(cpu, CPU_INTERRUPT_NMI); vcpu->interruptable = false; new_int.InterruptionType = WHvX64PendingNmi; new_int.InterruptionPending = 1; new_int.InterruptionVector = 2; } if (cpu_test_interrupt(cpu, CPU_INTERRUPT_SMI)) { - cpu->interrupt_request &= ~CPU_INTERRUPT_SMI; + cpu_reset_interrupt(cpu, CPU_INTERRUPT_SMI); } } @@ -1502,7 +1502,7 @@ static void whpx_vcpu_pre_run(CPUState *cpu) vcpu->interruptable && (env->eflags & IF_MASK)) { assert(!new_int.InterruptionPending); if (cpu_test_interrupt(cpu, CPU_INTERRUPT_HARD)) { - cpu->interrupt_request &= ~CPU_INTERRUPT_HARD; + cpu_reset_interrupt(cpu, CPU_INTERRUPT_HARD); irq = cpu_get_pic_interrupt(env); if (irq >= 0) { new_int.InterruptionType = WHvX64PendingInterrupt; @@ -1520,7 +1520,7 @@ static void whpx_vcpu_pre_run(CPUState *cpu) } } else if (vcpu->ready_for_pic_interrupt && cpu_test_interrupt(cpu, CPU_INTERRUPT_HARD)) { - cpu->interrupt_request &= ~CPU_INTERRUPT_HARD; + cpu_reset_interrupt(cpu, CPU_INTERRUPT_HARD); irq = cpu_get_pic_interrupt(env); if (irq >= 0) { reg_names[reg_count] = WHvRegisterPendingEvent; @@ -1607,7 +1607,7 @@ static void whpx_vcpu_process_async_events(CPUState *cpu) } if (cpu_test_interrupt(cpu, CPU_INTERRUPT_POLL)) { - cpu->interrupt_request &= ~CPU_INTERRUPT_POLL; + cpu_reset_interrupt(cpu, CPU_INTERRUPT_POLL); apic_poll_irq(x86_cpu->apic_state); } @@ -1623,7 +1623,7 @@ static void whpx_vcpu_process_async_events(CPUState *cpu) } if (cpu_test_interrupt(cpu, CPU_INTERRUPT_TPR)) { - cpu->interrupt_request &= ~CPU_INTERRUPT_TPR; + cpu_reset_interrupt(cpu, CPU_INTERRUPT_TPR); whpx_cpu_synchronize_state(cpu); apic_handle_tpr_access_report(x86_cpu->apic_state, env->eip, env->tpr_access_type); diff --git a/target/openrisc/sys_helper.c b/target/openrisc/sys_helper.c index d96b41a01c..b091a9c668 100644 --- a/target/openrisc/sys_helper.c +++ b/target/openrisc/sys_helper.c @@ -196,7 +196,7 @@ void HELPER(mtspr)(CPUOpenRISCState *env, target_ulong spr, target_ulong rb) env->ttmr = (rb & ~TTMR_IP) | ip; } else { /* Clear IP bit. */ env->ttmr = rb & ~TTMR_IP; - cs->interrupt_request &= ~CPU_INTERRUPT_TIMER; + cpu_reset_interrupt(cs, CPU_INTERRUPT_TIMER); } cpu_openrisc_timer_update(cpu); bql_unlock(); diff --git a/target/rx/helper.c b/target/rx/helper.c index ce003af421..41c9606fd1 100644 --- a/target/rx/helper.c +++ b/target/rx/helper.c @@ -63,7 +63,7 @@ void rx_cpu_do_interrupt(CPUState *cs) env->bpsw = save_psw; env->pc = env->fintv; env->psw_ipl = 15; - cs->interrupt_request &= ~CPU_INTERRUPT_FIR; + cpu_reset_interrupt(cs, CPU_INTERRUPT_FIR); qemu_set_irq(env->ack, env->ack_irq); qemu_log_mask(CPU_LOG_INT, "fast interrupt raised\n"); } else if (do_irq & CPU_INTERRUPT_HARD) { @@ -73,7 +73,7 @@ void rx_cpu_do_interrupt(CPUState *cs) cpu_stl_data(env, env->isp, env->pc); env->pc = cpu_ldl_data(env, env->intb + env->ack_irq * 4); env->psw_ipl = env->ack_ipl; - cs->interrupt_request &= ~CPU_INTERRUPT_HARD; + cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); qemu_set_irq(env->ack, env->ack_irq); qemu_log_mask(CPU_LOG_INT, "interrupt 0x%02x raised\n", env->ack_irq); diff --git a/target/s390x/tcg/excp_helper.c b/target/s390x/tcg/excp_helper.c index e4c75d0ce0..4c7faeee82 100644 --- a/target/s390x/tcg/excp_helper.c +++ b/target/s390x/tcg/excp_helper.c @@ -559,7 +559,7 @@ try_deliver: /* we might still have pending interrupts, but not deliverable */ if (!env->pending_int && !qemu_s390_flic_has_any(flic)) { - cs->interrupt_request &= ~CPU_INTERRUPT_HARD; + cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); } /* WAIT PSW during interrupt injection or STOP interrupt */ From 27e76d010104646c997d20ca0996fb5a046587b0 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 29 Aug 2025 11:26:05 +0200 Subject: [PATCH 0454/2396] cpu-common: use atomic access for interrupt_request MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Writes to interrupt_request used non-atomic accesses, but there are a few cases where the access was not protected by the BQL. Now that there is a full set of helpers, it's easier to guarantee that interrupt_request accesses are fully atomic, so just drop the requirement instead of fixing them. Reviewed-by: Igor Mammedov Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- hw/core/cpu-common.c | 12 +----------- include/hw/core/cpu.h | 1 - system/cpus.c | 3 +-- 3 files changed, 2 insertions(+), 14 deletions(-) diff --git a/hw/core/cpu-common.c b/hw/core/cpu-common.c index 259cf2a3c3..152abc9024 100644 --- a/hw/core/cpu-common.c +++ b/hw/core/cpu-common.c @@ -67,19 +67,9 @@ CPUState *cpu_create(const char *typename) return cpu; } -/* Resetting the IRQ comes from across the code base so we take the - * BQL here if we need to. cpu_interrupt assumes it is held.*/ void cpu_reset_interrupt(CPUState *cpu, int mask) { - bool need_lock = !bql_locked(); - - if (need_lock) { - bql_lock(); - } - cpu->interrupt_request &= ~mask; - if (need_lock) { - bql_unlock(); - } + qatomic_and(&cpu->interrupt_request, ~mask); } void cpu_exit(CPUState *cpu) diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h index b01a0cffd6..23bd02277f 100644 --- a/include/hw/core/cpu.h +++ b/include/hw/core/cpu.h @@ -495,7 +495,6 @@ struct CPUState { bool exit_request; int exclusive_context_count; uint32_t cflags_next_tb; - /* updates protected by BQL */ uint32_t interrupt_request; int singlestep_enabled; int64_t icount_budget; diff --git a/system/cpus.c b/system/cpus.c index 437848b5eb..9bfbe2b060 100644 --- a/system/cpus.c +++ b/system/cpus.c @@ -257,8 +257,7 @@ int64_t cpus_get_elapsed_ticks(void) void cpu_set_interrupt(CPUState *cpu, int mask) { /* Pairs with cpu_test_interrupt(). */ - qatomic_store_release(&cpu->interrupt_request, - cpu->interrupt_request | mask); + qatomic_or(&cpu->interrupt_request, mask); } void generic_handle_interrupt(CPUState *cpu, int mask) From 9e1ecd4aaaf9aa2f5b7caf364a10241a2cba02a8 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 29 Aug 2025 10:31:34 +0200 Subject: [PATCH 0455/2396] cpus: document that qemu_cpu_kick() can be used for BQL-less operation Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- include/hw/core/cpu.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h index 23bd02277f..8b57bcd92c 100644 --- a/include/hw/core/cpu.h +++ b/include/hw/core/cpu.h @@ -829,7 +829,8 @@ bool qemu_cpu_is_self(CPUState *cpu); * qemu_cpu_kick: * @cpu: The vCPU to kick. * - * Kicks @cpu's thread. + * Kicks @cpu's thread to exit the accelerator. For accelerators that + * can do that, the target vCPU thread will try not to take the BQL. */ void qemu_cpu_kick(CPUState *cpu); From ac6c8a390b451913995ee784ef7261b8928e5ace Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 1 Aug 2025 14:57:51 +0200 Subject: [PATCH 0456/2396] accel: use store_release/load_acquire for cross-thread exit_request Reads and writes cpu->exit_request do not use a load-acquire/store-release pair right now, but this means that cpu_exit() may not write cpu->exit_request after any flags that are read by the vCPU thread. Probably everything is protected one way or the other by the BQL, because cpu->exit_request leads to the slow path, where the CPU thread often takes the BQL (for example, to go to sleep by waiting on the BQL-protected cpu->halt_cond); but it's not clear, so use load-acquire/store-release consistently. Reviewed-by: Richard Henderson Reviewed-by: Peter Xu Reviewed-by: Igor Mammedov Signed-off-by: Paolo Bonzini --- accel/kvm/kvm-all.c | 19 +++++++++---------- accel/tcg/cpu-exec.c | 7 +++++-- accel/tcg/tcg-accel-ops-rr.c | 11 +++++++++-- hw/core/cpu-common.c | 3 ++- target/i386/nvmm/nvmm-all.c | 5 ++--- target/i386/whpx/whpx-all.c | 3 ++- 6 files changed, 29 insertions(+), 19 deletions(-) diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index f36dfe3349..bd9e5e3886 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -3029,10 +3029,6 @@ static void kvm_eat_signals(CPUState *cpu) if (kvm_immediate_exit) { qatomic_set(&cpu->kvm_run->immediate_exit, 0); - /* Write kvm_run->immediate_exit before the cpu->exit_request - * write in kvm_cpu_exec. - */ - smp_wmb(); return; } @@ -3187,7 +3183,8 @@ int kvm_cpu_exec(CPUState *cpu) } kvm_arch_pre_run(cpu, run); - if (qatomic_read(&cpu->exit_request)) { + /* Corresponding store-release is in cpu_exit. */ + if (qatomic_load_acquire(&cpu->exit_request)) { trace_kvm_interrupt_exit_request(); /* * KVM requires us to reenter the kernel after IO exits to complete @@ -3197,13 +3194,15 @@ int kvm_cpu_exec(CPUState *cpu) kvm_cpu_kick_self(); } - /* Read cpu->exit_request before KVM_RUN reads run->immediate_exit. - * Matching barrier in kvm_eat_signals. - */ - smp_rmb(); - run_ret = kvm_vcpu_ioctl(cpu, KVM_RUN, 0); + /* + * After writing cpu->exit_request, cpu_exit() sends a signal that writes + * kvm->run->immediate_exit. The signal is already happening after the + * write to cpu->exit_request so, if KVM read kvm->run->immediate_exit + * as true, cpu->exit_request will always read as true. + */ + attrs = kvm_arch_post_run(cpu, run); #ifdef KVM_HAVE_MCE_INJECTION diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index 508d2d2d9e..f838535d11 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -851,8 +851,11 @@ static inline bool cpu_handle_interrupt(CPUState *cpu, } #endif /* !CONFIG_USER_ONLY */ - /* Finally, check if we need to exit to the main loop. */ - if (unlikely(qatomic_read(&cpu->exit_request)) || icount_exit_request(cpu)) { + /* + * Finally, check if we need to exit to the main loop. + * The corresponding store-release is in cpu_exit. + */ + if (unlikely(qatomic_load_acquire(&cpu->exit_request)) || icount_exit_request(cpu)) { qatomic_set(&cpu->exit_request, 0); if (cpu->exception_index == -1) { cpu->exception_index = EXCP_INTERRUPT; diff --git a/accel/tcg/tcg-accel-ops-rr.c b/accel/tcg/tcg-accel-ops-rr.c index 6eec5c9eee..e8b0e370a8 100644 --- a/accel/tcg/tcg-accel-ops-rr.c +++ b/accel/tcg/tcg-accel-ops-rr.c @@ -242,10 +242,17 @@ static void *rr_cpu_thread_fn(void *arg) cpu = first_cpu; } - while (cpu && cpu_work_list_empty(cpu) && !cpu->exit_request) { - /* Store rr_current_cpu before evaluating cpu_can_run(). */ + while (cpu && cpu_work_list_empty(cpu)) { + /* + * Store rr_current_cpu before evaluating cpu->exit_request. + * Pairs with rr_kick_next_cpu(). + */ qatomic_set_mb(&rr_current_cpu, cpu); + /* Pairs with store-release in cpu_exit. */ + if (qatomic_load_acquire(&cpu->exit_request)) { + break; + } current_cpu = cpu; qemu_clock_enable(QEMU_CLOCK_VIRTUAL, diff --git a/hw/core/cpu-common.c b/hw/core/cpu-common.c index 152abc9024..42463e6258 100644 --- a/hw/core/cpu-common.c +++ b/hw/core/cpu-common.c @@ -74,7 +74,8 @@ void cpu_reset_interrupt(CPUState *cpu, int mask) void cpu_exit(CPUState *cpu) { - qatomic_set(&cpu->exit_request, 1); + /* Ensure cpu_exec will see the reason why the exit request was set. */ + qatomic_store_release(&cpu->exit_request, true); /* Ensure cpu_exec will see the exit request after TCG has exited. */ smp_wmb(); qatomic_set(&cpu->neg.icount_decr.u16.high, -1); diff --git a/target/i386/nvmm/nvmm-all.c b/target/i386/nvmm/nvmm-all.c index e1151b04c6..10bd51d9b5 100644 --- a/target/i386/nvmm/nvmm-all.c +++ b/target/i386/nvmm/nvmm-all.c @@ -743,7 +743,8 @@ nvmm_vcpu_loop(CPUState *cpu) nvmm_vcpu_pre_run(cpu); - if (qatomic_read(&cpu->exit_request)) { + /* Corresponding store-release is in cpu_exit. */ + if (qatomic_load_acquire(&cpu->exit_request)) { #if NVMM_USER_VERSION >= 2 nvmm_vcpu_stop(vcpu); #else @@ -751,8 +752,6 @@ nvmm_vcpu_loop(CPUState *cpu) #endif } - /* Read exit_request before the kernel reads the immediate exit flag */ - smp_rmb(); ret = nvmm_vcpu_run(mach, vcpu); if (ret == -1) { error_report("NVMM: Failed to exec a virtual processor," diff --git a/target/i386/whpx/whpx-all.c b/target/i386/whpx/whpx-all.c index c09a0a64f2..2106c29c3a 100644 --- a/target/i386/whpx/whpx-all.c +++ b/target/i386/whpx/whpx-all.c @@ -1714,7 +1714,8 @@ static int whpx_vcpu_run(CPUState *cpu) if (exclusive_step_mode == WHPX_STEP_NONE) { whpx_vcpu_pre_run(cpu); - if (qatomic_read(&cpu->exit_request)) { + /* Corresponding store-release is in cpu_exit. */ + if (qatomic_load_acquire(&cpu->exit_request)) { whpx_vcpu_kick(cpu); } } From f084ff128b6136e1aebfd73e1cf7066a077a79bf Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 8 Aug 2025 18:55:48 +0200 Subject: [PATCH 0457/2396] accel: use atomic accesses for exit_request MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CPU threads write exit_request as a "note to self" that they need to go out to a slow path. This write happens out of the BQL and can be a data race with another threads' cpu_exit(); use atomic accesses consistently. While at it, change the source argument from int ("1") to bool ("true"). Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Peter Xu Reviewed-by: Igor Mammedov Signed-off-by: Paolo Bonzini --- accel/kvm/kvm-all.c | 2 +- accel/tcg/tcg-accel-ops-mttcg.c | 2 +- accel/tcg/tcg-accel-ops-rr.c | 4 ++-- hw/ppc/spapr_hcall.c | 6 +++--- include/hw/core/cpu.h | 9 +++++++++ target/i386/kvm/kvm.c | 6 +++--- target/i386/nvmm/nvmm-accel-ops.c | 2 +- target/i386/nvmm/nvmm-all.c | 2 +- target/i386/whpx/whpx-all.c | 6 +++--- 9 files changed, 24 insertions(+), 15 deletions(-) diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index bd9e5e3886..e4167d94b4 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -3730,7 +3730,7 @@ int kvm_on_sigbus_vcpu(CPUState *cpu, int code, void *addr) have_sigbus_pending = true; pending_sigbus_addr = addr; pending_sigbus_code = code; - qatomic_set(&cpu->exit_request, 1); + qatomic_set(&cpu->exit_request, true); return 0; #else return 1; diff --git a/accel/tcg/tcg-accel-ops-mttcg.c b/accel/tcg/tcg-accel-ops-mttcg.c index 337b993d3d..b12b7a36b5 100644 --- a/accel/tcg/tcg-accel-ops-mttcg.c +++ b/accel/tcg/tcg-accel-ops-mttcg.c @@ -85,7 +85,7 @@ static void *mttcg_cpu_thread_fn(void *arg) qemu_guest_random_seed_thread_part2(cpu->random_seed); /* process any pending work */ - cpu->exit_request = 1; + qatomic_set(&cpu->exit_request, true); do { if (cpu_can_run(cpu)) { diff --git a/accel/tcg/tcg-accel-ops-rr.c b/accel/tcg/tcg-accel-ops-rr.c index e8b0e370a8..d13e0d8b44 100644 --- a/accel/tcg/tcg-accel-ops-rr.c +++ b/accel/tcg/tcg-accel-ops-rr.c @@ -212,7 +212,7 @@ static void *rr_cpu_thread_fn(void *arg) cpu = first_cpu; /* process any pending work */ - cpu->exit_request = 1; + qatomic_set(&cpu->exit_request, true); while (1) { /* Only used for icount_enabled() */ @@ -293,7 +293,7 @@ static void *rr_cpu_thread_fn(void *arg) /* Does not need a memory barrier because a spurious wakeup is okay. */ qatomic_set(&rr_current_cpu, NULL); - if (cpu && cpu->exit_request) { + if (cpu && qatomic_read(&cpu->exit_request)) { qatomic_set_mb(&cpu->exit_request, 0); } diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c index 1e936f35e4..51875e32a0 100644 --- a/hw/ppc/spapr_hcall.c +++ b/hw/ppc/spapr_hcall.c @@ -509,7 +509,7 @@ static target_ulong h_cede(PowerPCCPU *cpu, SpaprMachineState *spapr, if (!cpu_has_work(cs)) { cs->halted = 1; cs->exception_index = EXCP_HLT; - cs->exit_request = 1; + qatomic_set(&cs->exit_request, true); ppc_maybe_interrupt(env); } @@ -531,7 +531,7 @@ static target_ulong h_confer_self(PowerPCCPU *cpu) } cs->halted = 1; cs->exception_index = EXCP_HALTED; - cs->exit_request = 1; + qatomic_set(&cs->exit_request, true); ppc_maybe_interrupt(&cpu->env); return H_SUCCESS; @@ -624,7 +624,7 @@ static target_ulong h_confer(PowerPCCPU *cpu, SpaprMachineState *spapr, } cs->exception_index = EXCP_YIELD; - cs->exit_request = 1; + qatomic_set(&cs->exit_request, true); cpu_loop_exit(cs); return H_SUCCESS; diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h index 8b57bcd92c..338757e525 100644 --- a/include/hw/core/cpu.h +++ b/include/hw/core/cpu.h @@ -422,6 +422,15 @@ struct qemu_work_item; * valid under cpu_list_lock. * @created: Indicates whether the CPU thread has been successfully created. * @halt_cond: condition variable sleeping threads can wait on. + * @exit_request: Another thread requests the CPU to call qemu_wait_io_event(). + * Should be read only by CPU thread with load-acquire, to synchronize with + * other threads' store-release operation. + * + * In some cases, accelerator-specific code will write exit_request from + * within the same thread, to "bump" the effect of qemu_cpu_kick() to + * the one provided by cpu_exit(), especially when processing interrupt + * flags. In this case, the write and read happen in the same thread + * and the write therefore can use qemu_atomic_set(). * @interrupt_request: Indicates a pending interrupt request. * Only used by system emulation. * @halted: Nonzero if the CPU is in suspended state. diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index 8420c4090e..34e74f2447 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -5486,10 +5486,10 @@ void kvm_arch_pre_run(CPUState *cpu, struct kvm_run *run) if (cpu_test_interrupt(cpu, CPU_INTERRUPT_INIT | CPU_INTERRUPT_TPR)) { if (cpu_test_interrupt(cpu, CPU_INTERRUPT_INIT) && !(env->hflags & HF_SMM_MASK)) { - qatomic_set(&cpu->exit_request, 1); + qatomic_set(&cpu->exit_request, true); } if (cpu_test_interrupt(cpu, CPU_INTERRUPT_TPR)) { - qatomic_set(&cpu->exit_request, 1); + qatomic_set(&cpu->exit_request, true); } } @@ -5604,7 +5604,7 @@ int kvm_arch_process_async_events(CPUState *cs) if (env->exception_nr == EXCP08_DBLE) { /* this means triple fault */ qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); - cs->exit_request = 1; + qatomic_set(&cs->exit_request, true); return 0; } kvm_queue_exception(env, EXCP12_MCHK, 0, 0); diff --git a/target/i386/nvmm/nvmm-accel-ops.c b/target/i386/nvmm/nvmm-accel-ops.c index 3799260bbd..86869f133e 100644 --- a/target/i386/nvmm/nvmm-accel-ops.c +++ b/target/i386/nvmm/nvmm-accel-ops.c @@ -77,7 +77,7 @@ static void nvmm_start_vcpu_thread(CPUState *cpu) */ static void nvmm_kick_vcpu_thread(CPUState *cpu) { - cpu->exit_request = 1; + qatomic_set(&cpu->exit_request, true); cpus_kick_thread(cpu); } diff --git a/target/i386/nvmm/nvmm-all.c b/target/i386/nvmm/nvmm-all.c index 10bd51d9b5..7e36c42fbb 100644 --- a/target/i386/nvmm/nvmm-all.c +++ b/target/i386/nvmm/nvmm-all.c @@ -414,7 +414,7 @@ nvmm_vcpu_pre_run(CPUState *cpu) * or commit pending TPR access. */ if (cpu_test_interrupt(cpu, CPU_INTERRUPT_INIT | CPU_INTERRUPT_TPR)) { - cpu->exit_request = 1; + qatomic_set(&cpu->exit_request, true); } if (!has_event && cpu_test_interrupt(cpu, CPU_INTERRUPT_NMI)) { diff --git a/target/i386/whpx/whpx-all.c b/target/i386/whpx/whpx-all.c index 2106c29c3a..00fb7e2310 100644 --- a/target/i386/whpx/whpx-all.c +++ b/target/i386/whpx/whpx-all.c @@ -1489,10 +1489,10 @@ static void whpx_vcpu_pre_run(CPUState *cpu) if (cpu_test_interrupt(cpu, CPU_INTERRUPT_INIT | CPU_INTERRUPT_TPR)) { if (cpu_test_interrupt(cpu, CPU_INTERRUPT_INIT) && !(env->hflags & HF_SMM_MASK)) { - cpu->exit_request = 1; + qatomic_set(&cpu->exit_request, true); } if (cpu_test_interrupt(cpu, CPU_INTERRUPT_TPR)) { - cpu->exit_request = 1; + qatomic_set(&cpu->exit_request, true); } } @@ -1539,7 +1539,7 @@ static void whpx_vcpu_pre_run(CPUState *cpu) if (tpr != vcpu->tpr) { vcpu->tpr = tpr; reg_values[reg_count].Reg64 = tpr; - cpu->exit_request = 1; + qatomic_set(&cpu->exit_request, true); reg_names[reg_count] = WHvX64RegisterCr8; reg_count += 1; } From 9cf342b491f6872d316b7b0c3cbc0f6157f28797 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 11 Aug 2025 08:28:31 +0200 Subject: [PATCH 0458/2396] accel/tcg: create a thread-kick function for TCG MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Round-robin TCG is calling into cpu_exit() directly. In preparation for making cpu_exit() usable from all accelerators, define a generic thread-kick function for TCG which is used directly in the multi-threaded case, and through CPU_FOREACH in the round-robin case. Use it also for user-mode emulation, and take the occasion to move the implementation to accel/tcg/user-exec.c. Reviewed-by: Igor Mammedov Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- accel/tcg/cpu-exec.c | 6 ++++++ accel/tcg/tcg-accel-ops-mttcg.c | 5 ----- accel/tcg/tcg-accel-ops-mttcg.h | 3 --- accel/tcg/tcg-accel-ops-rr.c | 2 +- accel/tcg/tcg-accel-ops.c | 2 +- accel/tcg/tcg-accel-ops.h | 1 + accel/tcg/user-exec.c | 6 ++++++ bsd-user/main.c | 5 ----- docs/devel/tcg-icount.rst | 2 +- linux-user/main.c | 5 ----- 10 files changed, 16 insertions(+), 21 deletions(-) diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index f838535d11..9241bcadb5 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -40,6 +40,7 @@ #include "exec/replay-core.h" #include "system/tcg.h" #include "exec/helper-proto-common.h" +#include "tcg-accel-ops.h" #include "tb-jmp-cache.h" #include "tb-hash.h" #include "tb-context.h" @@ -748,6 +749,11 @@ static inline bool cpu_handle_exception(CPUState *cpu, int *ret) return false; } +void tcg_kick_vcpu_thread(CPUState *cpu) +{ + cpu_exit(cpu); +} + static inline bool icount_exit_request(CPUState *cpu) { if (!icount_enabled()) { diff --git a/accel/tcg/tcg-accel-ops-mttcg.c b/accel/tcg/tcg-accel-ops-mttcg.c index b12b7a36b5..1148ebcaae 100644 --- a/accel/tcg/tcg-accel-ops-mttcg.c +++ b/accel/tcg/tcg-accel-ops-mttcg.c @@ -123,11 +123,6 @@ static void *mttcg_cpu_thread_fn(void *arg) return NULL; } -void mttcg_kick_vcpu_thread(CPUState *cpu) -{ - cpu_exit(cpu); -} - void mttcg_start_vcpu_thread(CPUState *cpu) { char thread_name[VCPU_THREAD_NAME_SIZE]; diff --git a/accel/tcg/tcg-accel-ops-mttcg.h b/accel/tcg/tcg-accel-ops-mttcg.h index 8ffa7a9a9f..5c145cc859 100644 --- a/accel/tcg/tcg-accel-ops-mttcg.h +++ b/accel/tcg/tcg-accel-ops-mttcg.h @@ -10,9 +10,6 @@ #ifndef TCG_ACCEL_OPS_MTTCG_H #define TCG_ACCEL_OPS_MTTCG_H -/* kick MTTCG vCPU thread */ -void mttcg_kick_vcpu_thread(CPUState *cpu); - /* start an mttcg vCPU thread */ void mttcg_start_vcpu_thread(CPUState *cpu); diff --git a/accel/tcg/tcg-accel-ops-rr.c b/accel/tcg/tcg-accel-ops-rr.c index d13e0d8b44..a1d75fd341 100644 --- a/accel/tcg/tcg-accel-ops-rr.c +++ b/accel/tcg/tcg-accel-ops-rr.c @@ -43,7 +43,7 @@ void rr_kick_vcpu_thread(CPUState *unused) CPUState *cpu; CPU_FOREACH(cpu) { - cpu_exit(cpu); + tcg_kick_vcpu_thread(cpu); }; } diff --git a/accel/tcg/tcg-accel-ops.c b/accel/tcg/tcg-accel-ops.c index 9c37266c1e..1f662a9c74 100644 --- a/accel/tcg/tcg-accel-ops.c +++ b/accel/tcg/tcg-accel-ops.c @@ -206,7 +206,7 @@ static void tcg_accel_ops_init(AccelClass *ac) if (qemu_tcg_mttcg_enabled()) { ops->create_vcpu_thread = mttcg_start_vcpu_thread; - ops->kick_vcpu_thread = mttcg_kick_vcpu_thread; + ops->kick_vcpu_thread = tcg_kick_vcpu_thread; ops->handle_interrupt = tcg_handle_interrupt; } else { ops->create_vcpu_thread = rr_start_vcpu_thread; diff --git a/accel/tcg/tcg-accel-ops.h b/accel/tcg/tcg-accel-ops.h index 6feeb3f3e9..aecce605d7 100644 --- a/accel/tcg/tcg-accel-ops.h +++ b/accel/tcg/tcg-accel-ops.h @@ -18,5 +18,6 @@ void tcg_cpu_destroy(CPUState *cpu); int tcg_cpu_exec(CPUState *cpu); void tcg_handle_interrupt(CPUState *cpu, int mask); void tcg_cpu_init_cflags(CPUState *cpu, bool parallel); +void tcg_kick_vcpu_thread(CPUState *cpu); #endif /* TCG_ACCEL_OPS_H */ diff --git a/accel/tcg/user-exec.c b/accel/tcg/user-exec.c index 66c25fba7d..3c072fd868 100644 --- a/accel/tcg/user-exec.c +++ b/accel/tcg/user-exec.c @@ -38,6 +38,7 @@ #include "qemu/int128.h" #include "trace.h" #include "tcg/tcg-ldst.h" +#include "tcg-accel-ops.h" #include "backend-ldst.h" #include "internal-common.h" #include "tb-internal.h" @@ -46,6 +47,11 @@ __thread uintptr_t helper_retaddr; //#define DEBUG_SIGNAL +void qemu_cpu_kick(CPUState *cpu) +{ + tcg_kick_vcpu_thread(cpu); +} + /* * Adjust the pc to pass to cpu_restore_state; return the memop type. */ diff --git a/bsd-user/main.c b/bsd-user/main.c index 9ba69642f5..73aae8c327 100644 --- a/bsd-user/main.c +++ b/bsd-user/main.c @@ -214,11 +214,6 @@ bool qemu_cpu_is_self(CPUState *cpu) return thread_cpu == cpu; } -void qemu_cpu_kick(CPUState *cpu) -{ - cpu_exit(cpu); -} - /* Assumes contents are already zeroed. */ static void init_task_state(TaskState *ts) { diff --git a/docs/devel/tcg-icount.rst b/docs/devel/tcg-icount.rst index 7df883446a..a1dcd79e0f 100644 --- a/docs/devel/tcg-icount.rst +++ b/docs/devel/tcg-icount.rst @@ -37,7 +37,7 @@ translator starts by allocating a budget of instructions to be executed. The budget of instructions is limited by how long it will be until the next timer will expire. We store this budget as part of a vCPU icount_decr field which shared with the machinery for handling -cpu_exit(). The whole field is checked at the start of every +qemu_cpu_kick(). The whole field is checked at the start of every translated block and will cause a return to the outer loop to deal with whatever caused the exit. diff --git a/linux-user/main.c b/linux-user/main.c index 7b0ccb6fd6..4ddfc9a619 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -189,11 +189,6 @@ bool qemu_cpu_is_self(CPUState *cpu) return thread_cpu == cpu; } -void qemu_cpu_kick(CPUState *cpu) -{ - cpu_exit(cpu); -} - void task_settid(TaskState *ts) { if (ts->ts_tid == 0) { From 61d996da508fe4082f1cbfd9b51c8c47f535a993 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 11 Aug 2025 08:33:40 +0200 Subject: [PATCH 0459/2396] accel/tcg: inline cpu_exit() Right now, cpu_exit() is not usable from all accelerators because it includes a TCG-specific thread kick. In fact, cpu_exit() doubles as the TCG thread-kick via tcg_kick_vcpu_thread(). In preparation for changing that, inline cpu_exit() into tcg_kick_vcpu_thread(). The direction of the calls can then be reversed, with an accelerator-independent cpu_exit() calling into qemu_vcpu_kick() rather than the opposite. Reviewed-by: Igor Mammedov Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- accel/tcg/cpu-exec.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index 9241bcadb5..3ae545e888 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -751,7 +751,16 @@ static inline bool cpu_handle_exception(CPUState *cpu, int *ret) void tcg_kick_vcpu_thread(CPUState *cpu) { - cpu_exit(cpu); + /* + * Ensure cpu_exec will see the reason why the exit request was set. + * FIXME: this is not always needed. Other accelerators instead + * read interrupt_request and set exit_request on demand from the + * CPU thread; see kvm_arch_pre_run() for example. + */ + qatomic_store_release(&cpu->exit_request, true); + + /* Ensure cpu_exec will see the exit request after TCG has exited. */ + qatomic_store_release(&cpu->neg.icount_decr.u16.high, -1); } static inline bool icount_exit_request(CPUState *cpu) @@ -780,7 +789,8 @@ static inline bool cpu_handle_interrupt(CPUState *cpu, /* Clear the interrupt flag now since we're processing * cpu->interrupt_request and cpu->exit_request. * Ensure zeroing happens before reading cpu->exit_request or - * cpu->interrupt_request (see also smp_wmb in cpu_exit()) + * cpu->interrupt_request (see also store-release in + * tcg_kick_vcpu_thread()) */ qatomic_set_mb(&cpu->neg.icount_decr.u16.high, 0); From dcb46ecb2e90d532fcdc04702c92e732a0ef77e8 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 1 Aug 2025 13:24:48 +0200 Subject: [PATCH 0460/2396] cpus: remove TCG-ism from cpu_exit() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that TCG has its own kick function, make cpu_exit() do the right kick for all accelerators. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Igor Mammedov Signed-off-by: Paolo Bonzini --- hw/core/cpu-common.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/hw/core/cpu-common.c b/hw/core/cpu-common.c index 42463e6258..41a339903c 100644 --- a/hw/core/cpu-common.c +++ b/hw/core/cpu-common.c @@ -76,9 +76,7 @@ void cpu_exit(CPUState *cpu) { /* Ensure cpu_exec will see the reason why the exit request was set. */ qatomic_store_release(&cpu->exit_request, true); - /* Ensure cpu_exec will see the exit request after TCG has exited. */ - smp_wmb(); - qatomic_set(&cpu->neg.icount_decr.u16.high, -1); + qemu_cpu_kick(cpu); } static int cpu_common_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg) From f8217ae54e4c44a7f0d20d56a5368ec1818f1cc2 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 1 Aug 2025 13:50:04 +0200 Subject: [PATCH 0461/2396] cpus: properly kick CPUs out of inner execution loop Now that cpu_exit() actually kicks all accelerators, use it whenever the message to another thread is processed in qemu_wait_io_event(). Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- cpu-common.c | 3 ++- hw/ppc/ppc.c | 2 ++ hw/ppc/spapr_hcall.c | 7 +++---- hw/ppc/spapr_rtas.c | 2 +- replay/replay-events.c | 3 ++- system/cpu-timers.c | 6 +++--- system/cpus.c | 5 +++-- target/arm/tcg/mte_helper.c | 2 +- target/i386/kvm/hyperv.c | 1 - 9 files changed, 17 insertions(+), 14 deletions(-) diff --git a/cpu-common.c b/cpu-common.c index ef5757d23b..152661df8e 100644 --- a/cpu-common.c +++ b/cpu-common.c @@ -137,7 +137,8 @@ static void queue_work_on_cpu(CPUState *cpu, struct qemu_work_item *wi) wi->done = false; qemu_mutex_unlock(&cpu->work_mutex); - qemu_cpu_kick(cpu); + /* exit the inner loop and reach qemu_wait_io_event_common(). */ + cpu_exit(cpu); } void do_run_on_cpu(CPUState *cpu, run_on_cpu_func func, run_on_cpu_data data, diff --git a/hw/ppc/ppc.c b/hw/ppc/ppc.c index 43d0d0e755..3e436c7041 100644 --- a/hw/ppc/ppc.c +++ b/hw/ppc/ppc.c @@ -190,6 +190,7 @@ static void ppc970_set_irq(void *opaque, int pin, int level) if (level) { trace_ppc_irq_cpu("stop"); cs->halted = 1; + cpu_exit(cs); } else { trace_ppc_irq_cpu("restart"); cs->halted = 0; @@ -386,6 +387,7 @@ static void ppc40x_set_irq(void *opaque, int pin, int level) if (level) { trace_ppc_irq_cpu("stop"); cs->halted = 1; + cpu_exit(cs); } else { trace_ppc_irq_cpu("restart"); cs->halted = 0; diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c index 51875e32a0..c594d4b916 100644 --- a/hw/ppc/spapr_hcall.c +++ b/hw/ppc/spapr_hcall.c @@ -509,8 +509,8 @@ static target_ulong h_cede(PowerPCCPU *cpu, SpaprMachineState *spapr, if (!cpu_has_work(cs)) { cs->halted = 1; cs->exception_index = EXCP_HLT; - qatomic_set(&cs->exit_request, true); ppc_maybe_interrupt(env); + cpu_exit(cs); } return H_SUCCESS; @@ -531,8 +531,8 @@ static target_ulong h_confer_self(PowerPCCPU *cpu) } cs->halted = 1; cs->exception_index = EXCP_HALTED; - qatomic_set(&cs->exit_request, true); ppc_maybe_interrupt(&cpu->env); + cpu_exit(cs); return H_SUCCESS; } @@ -624,8 +624,7 @@ static target_ulong h_confer(PowerPCCPU *cpu, SpaprMachineState *spapr, } cs->exception_index = EXCP_YIELD; - qatomic_set(&cs->exit_request, true); - cpu_loop_exit(cs); + cpu_exit(cs); return H_SUCCESS; } diff --git a/hw/ppc/spapr_rtas.c b/hw/ppc/spapr_rtas.c index 78309dbb09..143bc8c379 100644 --- a/hw/ppc/spapr_rtas.c +++ b/hw/ppc/spapr_rtas.c @@ -221,7 +221,7 @@ static void rtas_stop_self(PowerPCCPU *cpu, SpaprMachineState *spapr, cs->halted = 1; ppc_store_lpcr(cpu, env->spr[SPR_LPCR] & ~pcc->lpcr_pm); kvmppc_set_reg_ppc_online(cpu, 0); - qemu_cpu_kick(cs); + cpu_exit(cs); } static void rtas_ibm_suspend_me(PowerPCCPU *cpu, SpaprMachineState *spapr, diff --git a/replay/replay-events.c b/replay/replay-events.c index 8959da9f1f..a96e47e774 100644 --- a/replay/replay-events.c +++ b/replay/replay-events.c @@ -118,7 +118,8 @@ void replay_add_event(ReplayAsyncEventKind event_kind, g_assert(replay_mutex_locked()); QTAILQ_INSERT_TAIL(&events_list, event, events); - qemu_cpu_kick(first_cpu); + /* Kick the TCG thread out of tcg_cpu_exec(). */ + cpu_exit(first_cpu); } void replay_bh_schedule_event(QEMUBH *bh) diff --git a/system/cpu-timers.c b/system/cpu-timers.c index cb35fa62b8..9919b46230 100644 --- a/system/cpu-timers.c +++ b/system/cpu-timers.c @@ -246,14 +246,14 @@ void qemu_timer_notify_cb(void *opaque, QEMUClockType type) if (qemu_in_vcpu_thread()) { /* - * A CPU is currently running; kick it back out to the + * A CPU is currently running; send it out of the * tcg_cpu_exec() loop so it will recalculate its * icount deadline immediately. */ - qemu_cpu_kick(current_cpu); + cpu_exit(current_cpu); } else if (first_cpu) { /* - * qemu_cpu_kick is not enough to kick a halted CPU out of + * cpu_exit() is not enough to kick a halted CPU out of * qemu_tcg_wait_io_event. async_run_on_cpu, instead, * causes cpu_thread_is_idle to return false. This way, * handle_icount_deadline can run. diff --git a/system/cpus.c b/system/cpus.c index 9bfbe2b060..bb13942cbb 100644 --- a/system/cpus.c +++ b/system/cpus.c @@ -604,7 +604,7 @@ void cpu_pause(CPUState *cpu) qemu_cpu_stop(cpu, true); } else { cpu->stop = true; - qemu_cpu_kick(cpu); + cpu_exit(cpu); } } @@ -644,6 +644,7 @@ void pause_all_vcpus(void) while (!all_vcpus_paused()) { qemu_cond_wait(&qemu_pause_cond, &bql); + /* FIXME: is this needed? */ CPU_FOREACH(cpu) { qemu_cpu_kick(cpu); } @@ -672,7 +673,7 @@ void cpu_remove_sync(CPUState *cpu) { cpu->stop = true; cpu->unplug = true; - qemu_cpu_kick(cpu); + cpu_exit(cpu); bql_unlock(); qemu_thread_join(cpu->thread); bql_lock(); diff --git a/target/arm/tcg/mte_helper.c b/target/arm/tcg/mte_helper.c index 0efc18a181..302e899287 100644 --- a/target/arm/tcg/mte_helper.c +++ b/target/arm/tcg/mte_helper.c @@ -591,7 +591,7 @@ static void mte_async_check_fail(CPUARMState *env, uint64_t dirty_ptr, * which is rather sooner than "normal". But the alternative * is waiting until the next syscall. */ - qemu_cpu_kick(env_cpu(env)); + cpu_exit(env_cpu(env)); #endif } diff --git a/target/i386/kvm/hyperv.c b/target/i386/kvm/hyperv.c index 9865120cc4..f7a81bd270 100644 --- a/target/i386/kvm/hyperv.c +++ b/target/i386/kvm/hyperv.c @@ -81,7 +81,6 @@ int kvm_hv_handle_exit(X86CPU *cpu, struct kvm_hyperv_exit *exit) * necessary because memory hierarchy is being changed */ async_safe_run_on_cpu(CPU(cpu), async_synic_update, RUN_ON_CPU_NULL); - cpu_exit(CPU(cpu)); return EXCP_INTERRUPT; case KVM_EXIT_HYPERV_HCALL: { From 871de7078fcaf597605576b97b32fab14722ea43 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 2 Sep 2025 07:17:09 +0200 Subject: [PATCH 0462/2396] treewide: rename qemu_wait_io_event/qemu_wait_io_event_common Do so before extending it to the user-mode emulators, where there is no such thing as an "I/O thread". Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- accel/dummy-cpus.c | 2 +- accel/hvf/hvf-accel-ops.c | 2 +- accel/kvm/kvm-accel-ops.c | 2 +- accel/tcg/tcg-accel-ops-mttcg.c | 2 +- accel/tcg/tcg-accel-ops-rr.c | 4 ++-- cpu-common.c | 2 +- include/hw/core/cpu.h | 2 +- include/system/cpus.h | 4 ++-- system/cpus.c | 6 +++--- target/i386/nvmm/nvmm-accel-ops.c | 2 +- target/i386/whpx/whpx-accel-ops.c | 2 +- 11 files changed, 15 insertions(+), 15 deletions(-) diff --git a/accel/dummy-cpus.c b/accel/dummy-cpus.c index 03cfc0fa01..225a47c31f 100644 --- a/accel/dummy-cpus.c +++ b/accel/dummy-cpus.c @@ -57,7 +57,7 @@ static void *dummy_cpu_thread_fn(void *arg) qemu_sem_wait(&cpu->sem); #endif bql_lock(); - qemu_wait_io_event(cpu); + qemu_process_cpu_events(cpu); } while (!cpu->unplug); bql_unlock(); diff --git a/accel/hvf/hvf-accel-ops.c b/accel/hvf/hvf-accel-ops.c index d488d6afba..7a27bdadb4 100644 --- a/accel/hvf/hvf-accel-ops.c +++ b/accel/hvf/hvf-accel-ops.c @@ -198,7 +198,7 @@ static void *hvf_cpu_thread_fn(void *arg) cpu_handle_guest_debug(cpu); } } - qemu_wait_io_event(cpu); + qemu_process_cpu_events(cpu); } while (!cpu->unplug || cpu_can_run(cpu)); hvf_vcpu_destroy(cpu); diff --git a/accel/kvm/kvm-accel-ops.c b/accel/kvm/kvm-accel-ops.c index b709187c7d..65a7f76a69 100644 --- a/accel/kvm/kvm-accel-ops.c +++ b/accel/kvm/kvm-accel-ops.c @@ -53,7 +53,7 @@ static void *kvm_vcpu_thread_fn(void *arg) cpu_handle_guest_debug(cpu); } } - qemu_wait_io_event(cpu); + qemu_process_cpu_events(cpu); } while (!cpu->unplug || cpu_can_run(cpu)); kvm_destroy_vcpu(cpu); diff --git a/accel/tcg/tcg-accel-ops-mttcg.c b/accel/tcg/tcg-accel-ops-mttcg.c index 1148ebcaae..342917c5f6 100644 --- a/accel/tcg/tcg-accel-ops-mttcg.c +++ b/accel/tcg/tcg-accel-ops-mttcg.c @@ -113,7 +113,7 @@ static void *mttcg_cpu_thread_fn(void *arg) } } - qemu_wait_io_event(cpu); + qemu_process_cpu_events(cpu); } while (!cpu->unplug || cpu_can_run(cpu)); tcg_cpu_destroy(cpu); diff --git a/accel/tcg/tcg-accel-ops-rr.c b/accel/tcg/tcg-accel-ops-rr.c index a1d75fd341..813b313859 100644 --- a/accel/tcg/tcg-accel-ops-rr.c +++ b/accel/tcg/tcg-accel-ops-rr.c @@ -117,7 +117,7 @@ static void rr_wait_io_event(void) rr_start_kick_timer(); CPU_FOREACH(cpu) { - qemu_wait_io_event_common(cpu); + qemu_process_cpu_events_common(cpu); } } @@ -203,7 +203,7 @@ static void *rr_cpu_thread_fn(void *arg) /* process any pending work */ CPU_FOREACH(cpu) { current_cpu = cpu; - qemu_wait_io_event_common(cpu); + qemu_process_cpu_events_common(cpu); } } diff --git a/cpu-common.c b/cpu-common.c index 152661df8e..0eb5c7b8f2 100644 --- a/cpu-common.c +++ b/cpu-common.c @@ -137,7 +137,7 @@ static void queue_work_on_cpu(CPUState *cpu, struct qemu_work_item *wi) wi->done = false; qemu_mutex_unlock(&cpu->work_mutex); - /* exit the inner loop and reach qemu_wait_io_event_common(). */ + /* exit the inner loop and reach qemu_process_cpu_events_common(). */ cpu_exit(cpu); } diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h index 338757e525..6075be0b59 100644 --- a/include/hw/core/cpu.h +++ b/include/hw/core/cpu.h @@ -422,7 +422,7 @@ struct qemu_work_item; * valid under cpu_list_lock. * @created: Indicates whether the CPU thread has been successfully created. * @halt_cond: condition variable sleeping threads can wait on. - * @exit_request: Another thread requests the CPU to call qemu_wait_io_event(). + * @exit_request: Another thread requests the CPU to call qemu_process_cpu_events(). * Should be read only by CPU thread with load-acquire, to synchronize with * other threads' store-release operation. * diff --git a/include/system/cpus.h b/include/system/cpus.h index 69be6a77a7..4aebec4870 100644 --- a/include/system/cpus.h +++ b/include/system/cpus.h @@ -17,8 +17,8 @@ bool cpu_work_list_empty(CPUState *cpu); bool cpu_thread_is_idle(CPUState *cpu); bool all_cpu_threads_idle(void); bool cpu_can_run(CPUState *cpu); -void qemu_wait_io_event_common(CPUState *cpu); -void qemu_wait_io_event(CPUState *cpu); +void qemu_process_cpu_events_common(CPUState *cpu); +void qemu_process_cpu_events(CPUState *cpu); void cpu_thread_signal_created(CPUState *cpu); void cpu_thread_signal_destroyed(CPUState *cpu); void cpu_handle_guest_debug(CPUState *cpu); diff --git a/system/cpus.c b/system/cpus.c index bb13942cbb..fd804e0732 100644 --- a/system/cpus.c +++ b/system/cpus.c @@ -450,7 +450,7 @@ static void qemu_cpu_stop(CPUState *cpu, bool exit) qemu_cond_broadcast(&qemu_pause_cond); } -void qemu_wait_io_event_common(CPUState *cpu) +void qemu_process_cpu_events_common(CPUState *cpu) { qatomic_set_mb(&cpu->thread_kicked, false); if (cpu->stop) { @@ -459,7 +459,7 @@ void qemu_wait_io_event_common(CPUState *cpu) process_queued_cpu_work(cpu); } -void qemu_wait_io_event(CPUState *cpu) +void qemu_process_cpu_events(CPUState *cpu) { bool slept = false; @@ -474,7 +474,7 @@ void qemu_wait_io_event(CPUState *cpu) qemu_plugin_vcpu_resume_cb(cpu); } - qemu_wait_io_event_common(cpu); + qemu_process_cpu_events_common(cpu); } void cpus_kick_thread(CPUState *cpu) diff --git a/target/i386/nvmm/nvmm-accel-ops.c b/target/i386/nvmm/nvmm-accel-ops.c index 86869f133e..d066364b98 100644 --- a/target/i386/nvmm/nvmm-accel-ops.c +++ b/target/i386/nvmm/nvmm-accel-ops.c @@ -51,7 +51,7 @@ static void *qemu_nvmm_cpu_thread_fn(void *arg) while (cpu_thread_is_idle(cpu)) { qemu_cond_wait_bql(cpu->halt_cond); } - qemu_wait_io_event_common(cpu); + qemu_process_cpu_events_common(cpu); } while (!cpu->unplug || cpu_can_run(cpu)); nvmm_destroy_vcpu(cpu); diff --git a/target/i386/whpx/whpx-accel-ops.c b/target/i386/whpx/whpx-accel-ops.c index da58805b1a..2ca4ee0263 100644 --- a/target/i386/whpx/whpx-accel-ops.c +++ b/target/i386/whpx/whpx-accel-ops.c @@ -51,7 +51,7 @@ static void *whpx_cpu_thread_fn(void *arg) while (cpu_thread_is_idle(cpu)) { qemu_cond_wait_bql(cpu->halt_cond); } - qemu_wait_io_event_common(cpu); + qemu_process_cpu_events_common(cpu); } while (!cpu->unplug || cpu_can_run(cpu)); whpx_destroy_vcpu(cpu); From 758e5de501f78d4ef53a43e0c5a4783cd807b5be Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 29 Aug 2025 16:53:05 +0200 Subject: [PATCH 0463/2396] bsd-user, linux-user: introduce qemu_process_cpu_events MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a user-mode emulation version of the function. More will be added later, for now it is just process_queued_cpu_work. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- accel/tcg/user-exec.c | 5 +++++ bsd-user/aarch64/target_arch_cpu.h | 2 +- bsd-user/arm/target_arch_cpu.h | 2 +- bsd-user/i386/target_arch_cpu.h | 2 +- bsd-user/riscv/target_arch_cpu.h | 2 +- bsd-user/x86_64/target_arch_cpu.h | 2 +- include/hw/core/cpu.h | 9 +++++++++ include/system/cpus.h | 1 - linux-user/aarch64/cpu_loop.c | 2 +- linux-user/alpha/cpu_loop.c | 2 +- linux-user/arm/cpu_loop.c | 2 +- linux-user/hexagon/cpu_loop.c | 2 +- linux-user/hppa/cpu_loop.c | 2 +- linux-user/i386/cpu_loop.c | 2 +- linux-user/loongarch64/cpu_loop.c | 2 +- linux-user/m68k/cpu_loop.c | 2 +- linux-user/microblaze/cpu_loop.c | 2 +- linux-user/mips/cpu_loop.c | 2 +- linux-user/openrisc/cpu_loop.c | 2 +- linux-user/ppc/cpu_loop.c | 2 +- linux-user/riscv/cpu_loop.c | 2 +- linux-user/s390x/cpu_loop.c | 2 +- linux-user/sh4/cpu_loop.c | 2 +- linux-user/sparc/cpu_loop.c | 2 +- linux-user/xtensa/cpu_loop.c | 2 +- 25 files changed, 36 insertions(+), 23 deletions(-) diff --git a/accel/tcg/user-exec.c b/accel/tcg/user-exec.c index 3c072fd868..65f5da6c50 100644 --- a/accel/tcg/user-exec.c +++ b/accel/tcg/user-exec.c @@ -52,6 +52,11 @@ void qemu_cpu_kick(CPUState *cpu) tcg_kick_vcpu_thread(cpu); } +void qemu_process_cpu_events(CPUState *cpu) +{ + process_queued_cpu_work(cpu); +} + /* * Adjust the pc to pass to cpu_restore_state; return the memop type. */ diff --git a/bsd-user/aarch64/target_arch_cpu.h b/bsd-user/aarch64/target_arch_cpu.h index 87fbf6d677..15df84fda2 100644 --- a/bsd-user/aarch64/target_arch_cpu.h +++ b/bsd-user/aarch64/target_arch_cpu.h @@ -54,7 +54,7 @@ static inline G_NORETURN void target_cpu_loop(CPUARMState *env) cpu_exec_start(cs); trapnr = cpu_exec(cs); cpu_exec_end(cs); - process_queued_cpu_work(cs); + qemu_process_cpu_events(cs); switch (trapnr) { case EXCP_SWI: diff --git a/bsd-user/arm/target_arch_cpu.h b/bsd-user/arm/target_arch_cpu.h index bc2eaa0bf4..9a952ef0ff 100644 --- a/bsd-user/arm/target_arch_cpu.h +++ b/bsd-user/arm/target_arch_cpu.h @@ -46,7 +46,7 @@ static inline G_NORETURN void target_cpu_loop(CPUARMState *env) cpu_exec_start(cs); trapnr = cpu_exec(cs); cpu_exec_end(cs); - process_queued_cpu_work(cs); + qemu_process_cpu_events(cs); switch (trapnr) { case EXCP_UDEF: case EXCP_NOCP: diff --git a/bsd-user/i386/target_arch_cpu.h b/bsd-user/i386/target_arch_cpu.h index 5d4c931dec..f147d5b6f8 100644 --- a/bsd-user/i386/target_arch_cpu.h +++ b/bsd-user/i386/target_arch_cpu.h @@ -113,7 +113,7 @@ static inline G_NORETURN void target_cpu_loop(CPUX86State *env) cpu_exec_start(cs); trapnr = cpu_exec(cs); cpu_exec_end(cs); - process_queued_cpu_work(cs); + qemu_process_cpu_events(cs); switch (trapnr) { case 0x80: { diff --git a/bsd-user/riscv/target_arch_cpu.h b/bsd-user/riscv/target_arch_cpu.h index ef92f00480..ad428d0263 100644 --- a/bsd-user/riscv/target_arch_cpu.h +++ b/bsd-user/riscv/target_arch_cpu.h @@ -49,7 +49,7 @@ static inline G_NORETURN void target_cpu_loop(CPURISCVState *env) cpu_exec_start(cs); trapnr = cpu_exec(cs); cpu_exec_end(cs); - process_queued_cpu_work(cs); + qemu_process_cpu_events(cs); signo = 0; diff --git a/bsd-user/x86_64/target_arch_cpu.h b/bsd-user/x86_64/target_arch_cpu.h index f82042e30a..1fa71d87f1 100644 --- a/bsd-user/x86_64/target_arch_cpu.h +++ b/bsd-user/x86_64/target_arch_cpu.h @@ -121,7 +121,7 @@ static inline G_NORETURN void target_cpu_loop(CPUX86State *env) cpu_exec_start(cs); trapnr = cpu_exec(cs); cpu_exec_end(cs); - process_queued_cpu_work(cs); + qemu_process_cpu_events(cs); switch (trapnr) { case EXCP_SYSCALL: diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h index 6075be0b59..fb788ca110 100644 --- a/include/hw/core/cpu.h +++ b/include/hw/core/cpu.h @@ -1145,6 +1145,15 @@ AddressSpace *cpu_get_address_space(CPUState *cpu, int asidx); G_NORETURN void cpu_abort(CPUState *cpu, const char *fmt, ...) G_GNUC_PRINTF(2, 3); +/** + * qemu_process_cpu_events: + * @cpu: CPU that left the execution loop + * + * Perform accelerator-independent work after the CPU has left + * the inner execution loop. + */ +void qemu_process_cpu_events(CPUState *cpu); + /* $(top_srcdir)/cpu.c */ void cpu_class_init_props(DeviceClass *dc); void cpu_exec_class_post_init(CPUClass *cc); diff --git a/include/system/cpus.h b/include/system/cpus.h index 4aebec4870..508444ccf1 100644 --- a/include/system/cpus.h +++ b/include/system/cpus.h @@ -18,7 +18,6 @@ bool cpu_thread_is_idle(CPUState *cpu); bool all_cpu_threads_idle(void); bool cpu_can_run(CPUState *cpu); void qemu_process_cpu_events_common(CPUState *cpu); -void qemu_process_cpu_events(CPUState *cpu); void cpu_thread_signal_created(CPUState *cpu); void cpu_thread_signal_destroyed(CPUState *cpu); void cpu_handle_guest_debug(CPUState *cpu); diff --git a/linux-user/aarch64/cpu_loop.c b/linux-user/aarch64/cpu_loop.c index 4c4921152e..f6b498c6c4 100644 --- a/linux-user/aarch64/cpu_loop.c +++ b/linux-user/aarch64/cpu_loop.c @@ -38,7 +38,7 @@ void cpu_loop(CPUARMState *env) cpu_exec_start(cs); trapnr = cpu_exec(cs); cpu_exec_end(cs); - process_queued_cpu_work(cs); + qemu_process_cpu_events(cs); switch (trapnr) { case EXCP_SWI: diff --git a/linux-user/alpha/cpu_loop.c b/linux-user/alpha/cpu_loop.c index 728b64906d..bb8346b509 100644 --- a/linux-user/alpha/cpu_loop.c +++ b/linux-user/alpha/cpu_loop.c @@ -35,7 +35,7 @@ void cpu_loop(CPUAlphaState *env) cpu_exec_start(cs); trapnr = cpu_exec(cs); cpu_exec_end(cs); - process_queued_cpu_work(cs); + qemu_process_cpu_events(cs); switch (trapnr) { case EXCP_RESET: diff --git a/linux-user/arm/cpu_loop.c b/linux-user/arm/cpu_loop.c index 9aeb9b0087..cd89b7d6f5 100644 --- a/linux-user/arm/cpu_loop.c +++ b/linux-user/arm/cpu_loop.c @@ -295,7 +295,7 @@ void cpu_loop(CPUARMState *env) cpu_exec_start(cs); trapnr = cpu_exec(cs); cpu_exec_end(cs); - process_queued_cpu_work(cs); + qemu_process_cpu_events(cs); switch(trapnr) { case EXCP_UDEF: diff --git a/linux-user/hexagon/cpu_loop.c b/linux-user/hexagon/cpu_loop.c index 25c97edcae..1941f4c9c1 100644 --- a/linux-user/hexagon/cpu_loop.c +++ b/linux-user/hexagon/cpu_loop.c @@ -36,7 +36,7 @@ void cpu_loop(CPUHexagonState *env) cpu_exec_start(cs); trapnr = cpu_exec(cs); cpu_exec_end(cs); - process_queued_cpu_work(cs); + qemu_process_cpu_events(cs); switch (trapnr) { case EXCP_INTERRUPT: diff --git a/linux-user/hppa/cpu_loop.c b/linux-user/hppa/cpu_loop.c index 3af50653bb..356cb48acc 100644 --- a/linux-user/hppa/cpu_loop.c +++ b/linux-user/hppa/cpu_loop.c @@ -119,7 +119,7 @@ void cpu_loop(CPUHPPAState *env) cpu_exec_start(cs); trapnr = cpu_exec(cs); cpu_exec_end(cs); - process_queued_cpu_work(cs); + qemu_process_cpu_events(cs); switch (trapnr) { case EXCP_SYSCALL: diff --git a/linux-user/i386/cpu_loop.c b/linux-user/i386/cpu_loop.c index 7b2d8b03d8..f3f58576af 100644 --- a/linux-user/i386/cpu_loop.c +++ b/linux-user/i386/cpu_loop.c @@ -214,7 +214,7 @@ void cpu_loop(CPUX86State *env) cpu_exec_start(cs); trapnr = cpu_exec(cs); cpu_exec_end(cs); - process_queued_cpu_work(cs); + qemu_process_cpu_events(cs); switch(trapnr) { case 0x80: diff --git a/linux-user/loongarch64/cpu_loop.c b/linux-user/loongarch64/cpu_loop.c index a0a4cbb7cc..26a5ce3a93 100644 --- a/linux-user/loongarch64/cpu_loop.c +++ b/linux-user/loongarch64/cpu_loop.c @@ -27,7 +27,7 @@ void cpu_loop(CPULoongArchState *env) cpu_exec_start(cs); trapnr = cpu_exec(cs); cpu_exec_end(cs); - process_queued_cpu_work(cs); + qemu_process_cpu_events(cs); switch (trapnr) { case EXCP_INTERRUPT: diff --git a/linux-user/m68k/cpu_loop.c b/linux-user/m68k/cpu_loop.c index aca0bf23dc..2c9f628241 100644 --- a/linux-user/m68k/cpu_loop.c +++ b/linux-user/m68k/cpu_loop.c @@ -33,7 +33,7 @@ void cpu_loop(CPUM68KState *env) cpu_exec_start(cs); trapnr = cpu_exec(cs); cpu_exec_end(cs); - process_queued_cpu_work(cs); + qemu_process_cpu_events(cs); switch(trapnr) { case EXCP_ILLEGAL: diff --git a/linux-user/microblaze/cpu_loop.c b/linux-user/microblaze/cpu_loop.c index d8277961c7..78506ab23d 100644 --- a/linux-user/microblaze/cpu_loop.c +++ b/linux-user/microblaze/cpu_loop.c @@ -32,7 +32,7 @@ void cpu_loop(CPUMBState *env) cpu_exec_start(cs); trapnr = cpu_exec(cs); cpu_exec_end(cs); - process_queued_cpu_work(cs); + qemu_process_cpu_events(cs); switch (trapnr) { case EXCP_INTERRUPT: diff --git a/linux-user/mips/cpu_loop.c b/linux-user/mips/cpu_loop.c index e67b8a2e46..2365de1de1 100644 --- a/linux-user/mips/cpu_loop.c +++ b/linux-user/mips/cpu_loop.c @@ -74,7 +74,7 @@ void cpu_loop(CPUMIPSState *env) cpu_exec_start(cs); trapnr = cpu_exec(cs); cpu_exec_end(cs); - process_queued_cpu_work(cs); + qemu_process_cpu_events(cs); switch(trapnr) { case EXCP_SYSCALL: diff --git a/linux-user/openrisc/cpu_loop.c b/linux-user/openrisc/cpu_loop.c index 8c72347a99..2167d880d5 100644 --- a/linux-user/openrisc/cpu_loop.c +++ b/linux-user/openrisc/cpu_loop.c @@ -33,7 +33,7 @@ void cpu_loop(CPUOpenRISCState *env) cpu_exec_start(cs); trapnr = cpu_exec(cs); cpu_exec_end(cs); - process_queued_cpu_work(cs); + qemu_process_cpu_events(cs); switch (trapnr) { case EXCP_SYSCALL: diff --git a/linux-user/ppc/cpu_loop.c b/linux-user/ppc/cpu_loop.c index 22885ffd90..b0b0cb14b4 100644 --- a/linux-user/ppc/cpu_loop.c +++ b/linux-user/ppc/cpu_loop.c @@ -77,7 +77,7 @@ void cpu_loop(CPUPPCState *env) cpu_exec_start(cs); trapnr = cpu_exec(cs); cpu_exec_end(cs); - process_queued_cpu_work(cs); + qemu_process_cpu_events(cs); arch_interrupt = true; switch (trapnr) { diff --git a/linux-user/riscv/cpu_loop.c b/linux-user/riscv/cpu_loop.c index b316281532..ce542540c2 100644 --- a/linux-user/riscv/cpu_loop.c +++ b/linux-user/riscv/cpu_loop.c @@ -36,7 +36,7 @@ void cpu_loop(CPURISCVState *env) cpu_exec_start(cs); trapnr = cpu_exec(cs); cpu_exec_end(cs); - process_queued_cpu_work(cs); + qemu_process_cpu_events(cs); switch (trapnr) { case EXCP_INTERRUPT: diff --git a/linux-user/s390x/cpu_loop.c b/linux-user/s390x/cpu_loop.c index 49e44548f8..4929b32e1f 100644 --- a/linux-user/s390x/cpu_loop.c +++ b/linux-user/s390x/cpu_loop.c @@ -64,7 +64,7 @@ void cpu_loop(CPUS390XState *env) cpu_exec_start(cs); trapnr = cpu_exec(cs); cpu_exec_end(cs); - process_queued_cpu_work(cs); + qemu_process_cpu_events(cs); switch (trapnr) { case EXCP_INTERRUPT: diff --git a/linux-user/sh4/cpu_loop.c b/linux-user/sh4/cpu_loop.c index 259ea1cc8b..0c9d7e9c46 100644 --- a/linux-user/sh4/cpu_loop.c +++ b/linux-user/sh4/cpu_loop.c @@ -34,7 +34,7 @@ void cpu_loop(CPUSH4State *env) cpu_exec_start(cs); trapnr = cpu_exec(cs); cpu_exec_end(cs); - process_queued_cpu_work(cs); + qemu_process_cpu_events(cs); switch (trapnr) { case 0x160: diff --git a/linux-user/sparc/cpu_loop.c b/linux-user/sparc/cpu_loop.c index 7d30cd1ff2..7391e2add8 100644 --- a/linux-user/sparc/cpu_loop.c +++ b/linux-user/sparc/cpu_loop.c @@ -220,7 +220,7 @@ void cpu_loop (CPUSPARCState *env) cpu_exec_start(cs); trapnr = cpu_exec(cs); cpu_exec_end(cs); - process_queued_cpu_work(cs); + qemu_process_cpu_events(cs); switch (trapnr) { case TARGET_TT_SYSCALL: diff --git a/linux-user/xtensa/cpu_loop.c b/linux-user/xtensa/cpu_loop.c index 43a194fc4a..a0ff10eff8 100644 --- a/linux-user/xtensa/cpu_loop.c +++ b/linux-user/xtensa/cpu_loop.c @@ -133,7 +133,7 @@ void cpu_loop(CPUXtensaState *env) cpu_exec_start(cs); trapnr = cpu_exec(cs); cpu_exec_end(cs); - process_queued_cpu_work(cs); + qemu_process_cpu_events(cs); env->sregs[PS] &= ~PS_EXCM; switch (trapnr) { From 9a191d3782d707062c42b9fc9f22d9f55d4ed375 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 21 Aug 2025 18:56:55 +0200 Subject: [PATCH 0464/2396] cpus: clear exit_request in qemu_process_cpu_events Make the code common to all accelerators: after seeing cpu->exit_request set to true, accelerator code needs to reach qemu_process_cpu_events_common(). So for the common cases where they use qemu_process_cpu_events(), go ahead and clear it in there. Note that the cheap qatomic_set() is enough because at this point the thread has taken the BQL; qatomic_set_mb() is not needed. In particular, this is the ordering of the communication between I/O and vCPU threads is always the same. In the I/O thread: (a) store other memory locations that will be checked if cpu->exit_request or cpu->interrupt_request is 1 (for example cpu->stop or cpu->work_list for cpu->exit_request) (b) cpu_exit(): store-release cpu->exit_request, or (b) cpu_interrupt(): store-release cpu->interrupt_request >>> at this point, cpu->halt_cond is broadcast and the BQL released (c) do the accelerator-specific kick (e.g. write icount_decr for TCG, pthread_kill for KVM, etc.) In the vCPU thread instead the opposite order is respected: (c) the accelerator's execution loop exits thanks to the kick (b) then the inner execution loop checks cpu->interrupt_request and cpu->exit_request. If needed cpu->interrupt_request is converted into cpu->exit_request when work is needed outside the execution loop. (a) then the other memory locations are checked. Some may need to be read under the BQL, but the vCPU thread may also take other locks (e.g. for queued work items) or none at all. qatomic_set_mb() would only be needed if the halt sleep was done outside the BQL (though in that case, cpu->exit_request probably would be replaced by a QemuEvent or something like that). Reviewed-by: Igor Mammedov Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- accel/kvm/kvm-all.c | 2 -- accel/tcg/cpu-exec.c | 1 - accel/tcg/tcg-accel-ops-rr.c | 9 +++++++-- accel/tcg/tcg-accel-ops.c | 2 -- accel/tcg/user-exec.c | 1 + system/cpus.c | 1 + target/i386/nvmm/nvmm-all.c | 2 -- target/i386/whpx/whpx-all.c | 2 -- 8 files changed, 9 insertions(+), 11 deletions(-) diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index e4167d94b4..d13156bee8 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -3155,7 +3155,6 @@ int kvm_cpu_exec(CPUState *cpu) trace_kvm_cpu_exec(); if (kvm_arch_process_async_events(cpu)) { - qatomic_set(&cpu->exit_request, 0); return EXCP_HLT; } @@ -3345,7 +3344,6 @@ int kvm_cpu_exec(CPUState *cpu) vm_stop(RUN_STATE_INTERNAL_ERROR); } - qatomic_set(&cpu->exit_request, 0); return ret; } diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index 3ae545e888..ad94f96b25 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -872,7 +872,6 @@ static inline bool cpu_handle_interrupt(CPUState *cpu, * The corresponding store-release is in cpu_exit. */ if (unlikely(qatomic_load_acquire(&cpu->exit_request)) || icount_exit_request(cpu)) { - qatomic_set(&cpu->exit_request, 0); if (cpu->exception_index == -1) { cpu->exception_index = EXCP_INTERRUPT; } diff --git a/accel/tcg/tcg-accel-ops-rr.c b/accel/tcg/tcg-accel-ops-rr.c index 813b313859..7dbdba7b51 100644 --- a/accel/tcg/tcg-accel-ops-rr.c +++ b/accel/tcg/tcg-accel-ops-rr.c @@ -293,8 +293,13 @@ static void *rr_cpu_thread_fn(void *arg) /* Does not need a memory barrier because a spurious wakeup is okay. */ qatomic_set(&rr_current_cpu, NULL); - if (cpu && qatomic_read(&cpu->exit_request)) { - qatomic_set_mb(&cpu->exit_request, 0); + if (cpu) { + /* + * This could even reset exit_request for all CPUs, but in practice + * races between CPU exits and changes to "cpu" are so rare that + * there's no advantage in doing so. + */ + qatomic_set(&cpu->exit_request, false); } if (icount_enabled() && all_cpu_threads_idle()) { diff --git a/accel/tcg/tcg-accel-ops.c b/accel/tcg/tcg-accel-ops.c index 1f662a9c74..3bd9800504 100644 --- a/accel/tcg/tcg-accel-ops.c +++ b/accel/tcg/tcg-accel-ops.c @@ -82,8 +82,6 @@ int tcg_cpu_exec(CPUState *cpu) ret = cpu_exec(cpu); cpu_exec_end(cpu); - qatomic_set_mb(&cpu->exit_request, 0); - return ret; } diff --git a/accel/tcg/user-exec.c b/accel/tcg/user-exec.c index 65f5da6c50..916f18754f 100644 --- a/accel/tcg/user-exec.c +++ b/accel/tcg/user-exec.c @@ -54,6 +54,7 @@ void qemu_cpu_kick(CPUState *cpu) void qemu_process_cpu_events(CPUState *cpu) { + qatomic_set(&cpu->exit_request, false); process_queued_cpu_work(cpu); } diff --git a/system/cpus.c b/system/cpus.c index fd804e0732..aa7bfcf56e 100644 --- a/system/cpus.c +++ b/system/cpus.c @@ -463,6 +463,7 @@ void qemu_process_cpu_events(CPUState *cpu) { bool slept = false; + qatomic_set(&cpu->exit_request, false); while (cpu_thread_is_idle(cpu)) { if (!slept) { slept = true; diff --git a/target/i386/nvmm/nvmm-all.c b/target/i386/nvmm/nvmm-all.c index 7e36c42fbb..ed42425167 100644 --- a/target/i386/nvmm/nvmm-all.c +++ b/target/i386/nvmm/nvmm-all.c @@ -817,8 +817,6 @@ nvmm_vcpu_loop(CPUState *cpu) cpu_exec_end(cpu); bql_lock(); - qatomic_set(&cpu->exit_request, false); - return ret < 0; } diff --git a/target/i386/whpx/whpx-all.c b/target/i386/whpx/whpx-all.c index 00fb7e2310..2a85168ed5 100644 --- a/target/i386/whpx/whpx-all.c +++ b/target/i386/whpx/whpx-all.c @@ -2050,8 +2050,6 @@ static int whpx_vcpu_run(CPUState *cpu) whpx_last_vcpu_stopping(cpu); } - qatomic_set(&cpu->exit_request, false); - return ret < 0; } From d5e33b5f8f5a787bc4bb38bd0b9a8c3e3a0aa9f0 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 11 Aug 2025 09:52:46 +0200 Subject: [PATCH 0465/2396] accel: make all calls to qemu_process_cpu_events look the same MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is no reason for some accelerators to use qemu_process_cpu_events_common (which is separated from qemu_process_cpu_events() specifically for round robin TCG). They can also check for events directly on the first pass through the loop, instead of setting cpu->exit_request to true. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- accel/dummy-cpus.c | 2 +- accel/hvf/hvf-accel-ops.c | 2 +- accel/kvm/kvm-accel-ops.c | 3 ++- accel/tcg/tcg-accel-ops-mttcg.c | 7 ++--- accel/tcg/tcg-accel-ops-rr.c | 43 ++++++++++++++----------------- target/i386/nvmm/nvmm-accel-ops.c | 6 ++--- target/i386/whpx/whpx-accel-ops.c | 6 ++--- 7 files changed, 30 insertions(+), 39 deletions(-) diff --git a/accel/dummy-cpus.c b/accel/dummy-cpus.c index 225a47c31f..5752f6302c 100644 --- a/accel/dummy-cpus.c +++ b/accel/dummy-cpus.c @@ -43,6 +43,7 @@ static void *dummy_cpu_thread_fn(void *arg) qemu_guest_random_seed_thread_part2(cpu->random_seed); do { + qemu_process_cpu_events(cpu); bql_unlock(); #ifndef _WIN32 do { @@ -57,7 +58,6 @@ static void *dummy_cpu_thread_fn(void *arg) qemu_sem_wait(&cpu->sem); #endif bql_lock(); - qemu_process_cpu_events(cpu); } while (!cpu->unplug); bql_unlock(); diff --git a/accel/hvf/hvf-accel-ops.c b/accel/hvf/hvf-accel-ops.c index 7a27bdadb4..8b794c2d41 100644 --- a/accel/hvf/hvf-accel-ops.c +++ b/accel/hvf/hvf-accel-ops.c @@ -192,13 +192,13 @@ static void *hvf_cpu_thread_fn(void *arg) qemu_guest_random_seed_thread_part2(cpu->random_seed); do { + qemu_process_cpu_events(cpu); if (cpu_can_run(cpu)) { r = hvf_vcpu_exec(cpu); if (r == EXCP_DEBUG) { cpu_handle_guest_debug(cpu); } } - qemu_process_cpu_events(cpu); } while (!cpu->unplug || cpu_can_run(cpu)); hvf_vcpu_destroy(cpu); diff --git a/accel/kvm/kvm-accel-ops.c b/accel/kvm/kvm-accel-ops.c index 65a7f76a69..8ed6945c2f 100644 --- a/accel/kvm/kvm-accel-ops.c +++ b/accel/kvm/kvm-accel-ops.c @@ -47,13 +47,14 @@ static void *kvm_vcpu_thread_fn(void *arg) qemu_guest_random_seed_thread_part2(cpu->random_seed); do { + qemu_process_cpu_events(cpu); + if (cpu_can_run(cpu)) { r = kvm_cpu_exec(cpu); if (r == EXCP_DEBUG) { cpu_handle_guest_debug(cpu); } } - qemu_process_cpu_events(cpu); } while (!cpu->unplug || cpu_can_run(cpu)); kvm_destroy_vcpu(cpu); diff --git a/accel/tcg/tcg-accel-ops-mttcg.c b/accel/tcg/tcg-accel-ops-mttcg.c index 342917c5f6..cf1ee7ac25 100644 --- a/accel/tcg/tcg-accel-ops-mttcg.c +++ b/accel/tcg/tcg-accel-ops-mttcg.c @@ -84,10 +84,9 @@ static void *mttcg_cpu_thread_fn(void *arg) cpu_thread_signal_created(cpu); qemu_guest_random_seed_thread_part2(cpu->random_seed); - /* process any pending work */ - qatomic_set(&cpu->exit_request, true); - do { + qemu_process_cpu_events(cpu); + if (cpu_can_run(cpu)) { int r; bql_unlock(); @@ -112,8 +111,6 @@ static void *mttcg_cpu_thread_fn(void *arg) break; } } - - qemu_process_cpu_events(cpu); } while (!cpu->unplug || cpu_can_run(cpu)); tcg_cpu_destroy(cpu); diff --git a/accel/tcg/tcg-accel-ops-rr.c b/accel/tcg/tcg-accel-ops-rr.c index 7dbdba7b51..2fb4643997 100644 --- a/accel/tcg/tcg-accel-ops-rr.c +++ b/accel/tcg/tcg-accel-ops-rr.c @@ -211,13 +211,30 @@ static void *rr_cpu_thread_fn(void *arg) cpu = first_cpu; - /* process any pending work */ - qatomic_set(&cpu->exit_request, true); - while (1) { /* Only used for icount_enabled() */ int64_t cpu_budget = 0; + if (cpu) { + /* + * This could even reset exit_request for all CPUs, but in practice + * races between CPU exits and changes to "cpu" are so rare that + * there's no advantage in doing so. + */ + qatomic_set(&cpu->exit_request, false); + } + + if (icount_enabled() && all_cpu_threads_idle()) { + /* + * When all cpus are sleeping (e.g in WFI), to avoid a deadlock + * in the main_loop, wake it up in order to start the warp timer. + */ + qemu_notify_event(); + } + + rr_wait_io_event(); + rr_deal_with_unplugged_cpus(); + bql_unlock(); replay_mutex_lock(); bql_lock(); @@ -292,26 +309,6 @@ static void *rr_cpu_thread_fn(void *arg) /* Does not need a memory barrier because a spurious wakeup is okay. */ qatomic_set(&rr_current_cpu, NULL); - - if (cpu) { - /* - * This could even reset exit_request for all CPUs, but in practice - * races between CPU exits and changes to "cpu" are so rare that - * there's no advantage in doing so. - */ - qatomic_set(&cpu->exit_request, false); - } - - if (icount_enabled() && all_cpu_threads_idle()) { - /* - * When all cpus are sleeping (e.g in WFI), to avoid a deadlock - * in the main_loop, wake it up in order to start the warp timer. - */ - qemu_notify_event(); - } - - rr_wait_io_event(); - rr_deal_with_unplugged_cpus(); } g_assert_not_reached(); diff --git a/target/i386/nvmm/nvmm-accel-ops.c b/target/i386/nvmm/nvmm-accel-ops.c index d066364b98..dd5d5428b1 100644 --- a/target/i386/nvmm/nvmm-accel-ops.c +++ b/target/i386/nvmm/nvmm-accel-ops.c @@ -42,16 +42,14 @@ static void *qemu_nvmm_cpu_thread_fn(void *arg) qemu_guest_random_seed_thread_part2(cpu->random_seed); do { + qemu_process_cpu_events(cpu); + if (cpu_can_run(cpu)) { r = nvmm_vcpu_exec(cpu); if (r == EXCP_DEBUG) { cpu_handle_guest_debug(cpu); } } - while (cpu_thread_is_idle(cpu)) { - qemu_cond_wait_bql(cpu->halt_cond); - } - qemu_process_cpu_events_common(cpu); } while (!cpu->unplug || cpu_can_run(cpu)); nvmm_destroy_vcpu(cpu); diff --git a/target/i386/whpx/whpx-accel-ops.c b/target/i386/whpx/whpx-accel-ops.c index 2ca4ee0263..f75886128d 100644 --- a/target/i386/whpx/whpx-accel-ops.c +++ b/target/i386/whpx/whpx-accel-ops.c @@ -42,16 +42,14 @@ static void *whpx_cpu_thread_fn(void *arg) qemu_guest_random_seed_thread_part2(cpu->random_seed); do { + qemu_process_cpu_events(cpu); + if (cpu_can_run(cpu)) { r = whpx_vcpu_exec(cpu); if (r == EXCP_DEBUG) { cpu_handle_guest_debug(cpu); } } - while (cpu_thread_is_idle(cpu)) { - qemu_cond_wait_bql(cpu->halt_cond); - } - qemu_process_cpu_events_common(cpu); } while (!cpu->unplug || cpu_can_run(cpu)); whpx_destroy_vcpu(cpu); From b422a7bff64eaf55b8250225533ca1df42c3777e Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 1 Aug 2025 14:46:27 +0200 Subject: [PATCH 0466/2396] tcg/user: do not set exit_request gratuitously Whenever user-mode emulation needs to go all the way out of the cpu exec loop, it uses cpu_exit(), which already sets cpu->exit_request. Therefore, there is no need for tcg_kick_vcpu_thread() to set cpu->exit_request again outside system emulation. Reviewed-by: Igor Mammedov Signed-off-by: Paolo Bonzini --- accel/tcg/cpu-exec.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index ad94f96b25..7c20d9db12 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -751,6 +751,7 @@ static inline bool cpu_handle_exception(CPUState *cpu, int *ret) void tcg_kick_vcpu_thread(CPUState *cpu) { +#ifndef CONFIG_USER_ONLY /* * Ensure cpu_exec will see the reason why the exit request was set. * FIXME: this is not always needed. Other accelerators instead @@ -758,6 +759,7 @@ void tcg_kick_vcpu_thread(CPUState *cpu) * CPU thread; see kvm_arch_pre_run() for example. */ qatomic_store_release(&cpu->exit_request, true); +#endif /* Ensure cpu_exec will see the exit request after TCG has exited. */ qatomic_store_release(&cpu->neg.icount_decr.u16.high, -1); From 614fff7c0a2e28ed8675bbc3385ad88a76d4ee3a Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 8 Sep 2025 12:49:33 +0200 Subject: [PATCH 0467/2396] ci: temporarily remove rust from Ubuntu MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is for the purpose of getting an easy-to-use base for future development. The plan is: - that Debian will require trixie to enable Rust usage - that Ubuntu will backport 1.83 to its 22.04 and 24.04 versions (https://bugs.launchpad.net/ubuntu/+source/rustc-1.83/+bug/2120318) Marc-André is working on adding Rust to other CI jobs. Reviewed-by: Zhao Liu Link: https://lore.kernel.org/r/20250908105005.2119297-2-pbonzini@redhat.com Signed-off-by: Paolo Bonzini --- .gitlab-ci.d/buildtest.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitlab-ci.d/buildtest.yml b/.gitlab-ci.d/buildtest.yml index 83c2867295..f01978fb40 100644 --- a/.gitlab-ci.d/buildtest.yml +++ b/.gitlab-ci.d/buildtest.yml @@ -39,9 +39,9 @@ build-system-ubuntu: job: amd64-ubuntu2204-container variables: IMAGE: ubuntu2204 - CONFIGURE_ARGS: --enable-docs --enable-rust + CONFIGURE_ARGS: --enable-docs TARGETS: alpha-softmmu microblazeel-softmmu mips64el-softmmu - MAKE_CHECK_ARGS: check-build check-doc + MAKE_CHECK_ARGS: check-build check-system-ubuntu: extends: .native_test_job_template From 091f115ea5d40880e74123f2a7cd12f3dd32d624 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 8 Sep 2025 12:49:34 +0200 Subject: [PATCH 0468/2396] configure: bump Meson to 1.9.0 for use with Rust Meson 1.9.0 provides mixed linking of Rust and C objects. As a side effect, this also allows adding dependencies with "sources: ..." files to Rust crates that use structured_sources(). It can also clean up up the meson.build files for Rust noticeably, but due to an issue with doctests (see https://github.com/mesonbuild/meson/pull/14973) that will have to wait for 1.9.1. Reviewed-by: Zhao Liu Link: https://lore.kernel.org/r/20250908105005.2119297-3-pbonzini@redhat.com Signed-off-by: Paolo Bonzini --- configure | 4 ++-- python/scripts/vendor.py | 4 ++-- python/wheels/meson-1.8.1-py3-none-any.whl | Bin 1013001 -> 0 bytes python/wheels/meson-1.9.0-py3-none-any.whl | Bin 0 -> 1029634 bytes pythondeps.toml | 4 ++-- 5 files changed, 6 insertions(+), 6 deletions(-) delete mode 100644 python/wheels/meson-1.8.1-py3-none-any.whl create mode 100644 python/wheels/meson-1.9.0-py3-none-any.whl diff --git a/configure b/configure index 274a778764..3053c23fbe 100755 --- a/configure +++ b/configure @@ -1184,12 +1184,12 @@ fi # detect rust triple meson_version=$($meson --version) -if test "$rust" != disabled && ! version_ge "$meson_version" 1.8.1; then +if test "$rust" != disabled && ! version_ge "$meson_version" 1.9.0; then if test "$rust" = enabled; then $mkvenv ensuregroup --dir "${source_path}/python/wheels" \ ${source_path}/pythondeps.toml meson-rust || exit 1 else - echo "Rust needs Meson 1.8.1, disabling" 2>&1 + echo "Rust needs Meson 1.9.0, disabling" 2>&1 rust=disabled fi fi diff --git a/python/scripts/vendor.py b/python/scripts/vendor.py index b47db00743..33ac7a45de 100755 --- a/python/scripts/vendor.py +++ b/python/scripts/vendor.py @@ -41,8 +41,8 @@ def main() -> int: parser.parse_args() packages = { - "meson==1.8.1": - "374bbf71247e629475fc10b0bd2ef66fc418c2d8f4890572f74de0f97d0d42da", + "meson==1.9.0": + "45e51ddc41e37d961582d06e78c48e0f9039011587f3495c4d6b0781dad92357", } vendor_dir = Path(__file__, "..", "..", "wheels").resolve() diff --git a/python/wheels/meson-1.8.1-py3-none-any.whl b/python/wheels/meson-1.8.1-py3-none-any.whl deleted file mode 100644 index a885f0e18cea1d1c32987540dc12d91cb74bcb9d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1013001 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⁡*1p+RgYa0uHUnD4)f_RRu+t>uHK4GzBbiZ`HbDOCnwkL z3;3C_MLS@Je8V07rrcT6RZ>h%S4t<99dcu{y0vPtZ}rmXP}T`wl*@jm9KU-}PcC_Z z&z~=8wpX_QczUC8*Rnr1yYel*w%=^~_~@F=XD)ZiXVZELPU~Jfp{29t=aF?`#wS;$ z6nKPxzuA0#g87fT(+n5!{hs!1`K)4}P03TQnLS^v!#??I#li0et(G3kk3Bh+C%S0Q z@$j-OH#KBZ=d*5>XSq?n(DV}@L)Nv2tq){=%WnMpL2?u0KACLa1 z$L>0{X_*F#RvlJs<7oTxaraY}Nms>-qtYLXKB;i=>#MiAmujsWRQYC3q_fOYpEujX z!_O}C-+e>gMX$9*DCUi;^rpS>E7=w$-`hCj-J=^vPdt`No`3LvwB?nxwvt6Z zeLf*3@#_8kl%Jcj0{2`^Fz4&3)?xatzw>;Q!sTx}{x3@HdFcP>koDbZt5dhF5|>VF zQ@-B6=l3o?^{MaQtkl(McHq9})z|Xuo6^3ZC*QyOt#;*;C_lQQ)kN-@aFytkBR%t( zOU|^d;Oy5n4)=Sw^-cNuy)!)DZ`rcXRzE5zNo#%Uf>WnER&*VgkbA!W+LqgrzjSu+ z-RDoYig zPLS`W1B>K>1a7oWVEShLMvvjggzXL651(tg8q2eE_Vuzr1qP9Z{oiso{(F~S<~_D;4ciqUe_+}a1yT8{ya(M%Wn;JKsTw~y-@d`> z{<&#Wos)kT#TI`uwP)IE?>#-V;&Qu%%G?=SdT#Yy2!FH6Oy1I6uC8&Fs9D0zGur9b zlB6BWa*b!%UU+^m{7~Pk84N3b19_096#m-)Xfm4E56_?A#b_FC0^kEN>X zl`rYdSKfNWKi|SY>chhWbyj)%Vlp1v-)T2~c41TgaT%vs3oKjie^Z`Un9?77vTljz zk@6Rv-DR_v@GVblE}vB)<&(QKTe;<;hh*}pBTvo;fggkpu@ztQ{ANr2f(OYRk6XRu>J7dGY0kPGt8NxsIjCv#fLMSo6aw5%bI2OI_`*Dz=Mje=}80 zwiK-2#df|VA!Ge;Wo3Wi(lRTym+qKwcjM4kBmGwr^t93tT-YYQKy>Vk2TkFwHmjou)ec^oexnaNkCi^$fx&KX9-F3=!nbr?O zrhb3j*Q%dO4hrAUGf4k5L04b$?1P%x!?(nKx|MNe_uW5rb?^Esdn0?V+pqFIrFZpl z@wXqXyHfh&R~&D+ujd}Vr6GFJ4coq&`p06yI{%%HpWbxJE>K_=SMi1i507wd?ryw& zdfUemt$m>#{1+l;&DXzNY}K%9|D{h--?`J1C+U57pnP7k)-mx(Pxd_b50fT!i!3;Q z`Hxh%-LaCU%vpxY#&R($Jwg+DC#_KTW-~GH>!D3$SvFcCGjvB86vGp$d4(5h__}i0_cJS%;3kzbJTTZ|C@H_w0>5lRz zTkdlX!L>%|h2doita3K$+?K}o%tp z=bhNd)}Eoa{q3elKekH;Z#umxMf$^yCeFNB$;UaYwzN*u2Wu*4@_AHeR{W0g-)vtSA->R<+`h4=neS6Ecy6aPZ>J?Q>{PFpb zW#RvI#k3=S2Yl;w%J=O%v;N_$jrX{?&2OBVsI|6oLNV`Zg>p%e*x1M2%1<|W{or)p z>3m+HQA^`so7fzc27}8CJ-NKye`9v0DsXtMl1*sjEnIygeERi9Q;k5blaA|u95hm_ zJ!V$%=R=KY+T2E~vh$PYNS-`?=JKh=%isL|vmdJqs`a-`V7I`|kwic}?`X ze`U_v&ebxTYZgd9Uz>4_vqWF`@dm@sXLxunj~3R#Sy)3zgl-mNlUoN!*hL#(;mKb*wC?Z!@SiTxmKH|OWaV< zs1ELUy+=E5$0q$h`Zo=?YQ~qJv-_DJ^7O>J2=|#~vtD)1ZQFA%xS6jnW2U5ptfuj_ z9*a=k=%Ziut+{#Q8uPczHL~vBKbA}Xm}Xqqwjgy<+qvkg%>SgM4Kg#KhZTZR5 z@fpXC9Qu^?vUHxsRlNi6?uv(M@4D9Dap31>38{%D%2nD&eI{JzsQ=cxZpOuP^Q@~k z{C!sSsNz=DqaVj!OnNBbS$OP=H2=EZH#xueyx+JuZ%zL}!@?Pd6Ic$5PWInu+JAm^ zg3qd%wa=`-)cP(wvAZk${+4G~H>brVR{gTK-u&Uh1NoD>g>Fj6)}^n$`E`xy?tO1> zC5pQ=9G{uGStqP{-gMcSPpbuXO}y#6A=-(1!vn@0d0*}`C(h?9tP)=QX1iMIT~Fcr zOCNlBe5vyUW4f zI;AA8W>K1#u0hu;Z?9sz^5-vVt9u)Gbe_m8o8GV5SG9S=o`u&;Cfzt!_A}AYLB!hp z-g8q~o`05ZjYnCHf4W`Yf7;2C%USu`W960FuUpKybc_3UR;)SLCj4uwtjManyZJeG zW?W3uXqkFu`&!v!`Ik?!Ob(low|LKWhQ$#J76q=h`E+>mtM`-m916Y|pI^-1&#>v^ z3$sPPpPjy`^81NzM8?~l!Bf_xxa`R4$t<;;G~HSrV~ zvF^YrHzx^{DL>pYsjR|WNs8$~>A%bQkAHYSE8=dFS}nt^BO>!HHl?}q)F##3H~DV7_0!XUYwFLh9lCXC=l$qg-+o_X zdsv(tBk}RVjZf3OH7EYPR~ssR-|2#f`eH-%tV7#heom`!WBqKnJKuHJ*X-g&2B*b1 zOn*!B3-(M}?s6k(2cL_s*yf)730>=3)7MG$)a`Q#(OOo2Mp-}TvBLeIKZW*5x;b0v zPx>Nb{hTFyht@RzTqmj0KLTy$DYdGXujH*LuUYQYbS6>dW6FVPH9vnfS8qGjx1&{x zH~ZdmU*+HD4I4rmy!B20d~}WoxO_kHSn9N0Ot0MolX=YfjOz?rm*0Obx;)%_%R7Vk zl}}86eOW2>_0g`06W+AS=!v@C>)$<5;EmBdZhyg``>kG{?(d`;I?a{0v8?#K)QlnZpS$^odt$F^rG4K&D7e2+%Gq6AV(W#8 zD>Xxx92c2=)%)|Od4(AcUMmc3rY@PK9@HBf?z`Dj>RZp5$!pcr+@^F$%jM15T_JHZ zb$i64kI(;wr1);x9=-LF z_3qgXD{JPQUl-_6_VY#jl9>?^PZWRi#JAY3D_n2w@oSRgqH?}_;Zc*EzZETJ&|0N= za$E4tzmrdvxY)`0uvi#y9u@7kPwQdb6l+#d*7Ne=_Ucq0p2D-DEaAJ$PpzJuACRfL z;zwj~gk@=u%IyEkOI&?Q7Fn#dEc4|TSnWEwV8h0>#$6dcm04Z8Z*=a-Eu0m1yM(Ww zNAs$v(xa^PlcH>2N^{<>ymo~nVR1zCRonEH-s-Gt&s@Jf@#Pwi?7{~Nc5Pl|X1&n8 zsnpy+>rhA0zl}ADSzFc#H-4Ouoh~Ce>t6Sfj7#i~4P$g#t}EG=%U<8TyZz3CrfuJL z@6~-Owry$mS_^TZhBe2h9*&y!Yni{#&mQ+VQ7j!k**9c<{n@@;<43OX$2DDd)`W>^ zwNDXj-#fqk-0bkKwVs!6&Iu^wxqI+;qR`h_Df4aZUTtW+%x&DXfJ0YE{KMX@r?z~W zRF!c`YNph?OY+?@FI3EWBDh@&E=WZxwgn!)bj(FQ*f4mryQ{TDxcvnendL`gUnX8s z>57}7_s&{5>G#_wKd)HxYER5owwe+4?&r2od3PN1kF`x!|6rBcd2YYb=d~TNRZ8=_ zI&x=b2A|=%6`YnUxV_++)x!e>`5y*5#K z@n+&)ha*$+9y1BPpUfZ;|L{TZ`fjm1+dgc1bJy&I=Z@8eQ+!QtO!^}mlq+s@rrhtS zn%s8YZHHInGQTnnUA^FQb3^1j9m7ZlFAMIR2wm@;<K9gQmbq~$N1&AXEyt%@!nU(@zNCC`SZ;q+*T>h$ z-?--Dgso?1@6F?=u4P-}GyO*s`-^`wq9R>R>`AyUtlauAN+zy(^I4g#$Ikv|LY>FI zcsAHjRDyxwbpQhc`YENwsU@WadIgn7WBwN1cANBnzQ191tf)hKOU~1GS^nX>w2zkw zy+78c6*OsgPzVPHpGSnkfkW*Y^St>>)))Gh{e3-?ku6^2?WuR0guI#3s;a7f?XIp+ zTBmpa&-Fj;7E>}6pTD1Gr2AN7+RvR8TX_#{oio2uUrqf{>9)JiKW4>tZ5G%6U+VY& zPh`ycr4{$f?^otPQHwp8aa@84}+n~wU_sFGK#( z|FV2$@Vo7WyAJha_wGq=(_Li|dCq0V(@6q6X-><&7tUL%aK`7zmhUyQy?>RfnJLJu zb=ZILv(4i9p4%>cIOEMcedV8Tic_N1W=c)@DBmjiS^59Z@SW;eu8(f*{`da$_J6P6 z-{1G|^=|(AbNyLrE9N=fpU$s3cL#5SM_^jwG!tjr&4R{HvScqbUw?mp#nBfD24+4@ ziv-RFZ@10~J3MdRYRw-RRr9-Dmx(`Z&D?q9K5L~{gxK8aJ9##nbcG+$m}|bf;&!cw zn$Zd84+YOz=U*~X6VpGnr!FcbRPNNh$GIDt47MBD-};I}WO3Oz=MQRqebwOPx{V`hWAf#eQ$$kvkXpPVM~s`B^F+PwZ1v zCsu8rwvwY`--L-Zp^e`oby5Q+%u#i1+rB8q`_WMzd(SwtFS8q<``)8Cp|a zB-p+e?Oc>RWhehLjmKNq3#|h0G~BnX_PjpxeDI~)GPa*IldkyGeEt0Nsek_Zb$oi~R~4VzmYgT@BGKg5sc$D2 zSIXaUY!c4>de&y?X8z*IOLw{lR3*LDj^s-I*i@1^o9UXO%f)^p9ovt^ii@=Zvoxk! zKkQiV=J@%zVZwDr&eKj>UtP0;vekKKh9A8@>-o{z2_c_2J}jv_Iax7w)_uXP^Cno` z*r>03VZBN4OfyHf9)WY8YWssi87C^qKe0QfHEk7dn@XV;Z|Pp;Z7G*`?0(Yq%`0ef zc(jqY@eL8VY2I=jPsUYWJt$2{y@uU%(Q zm$)}chIv!tp{e&yDYHfkKAk4y<-06Vr+cOPx<@8c9xeU8_4^}*S;trvbAqa(Vl4h= zDzg19Nho@|Qf8KIV4S4)tJQDAW?IkizONn}_-h}xuBq|zy1bwHveKRW$FA``Z=D&k zKP_V3q0{O|(z2M|3)TB)^4TlS?0mTEapFV2Y=$QrrM@NmuPfD1chZzgTK!fo=YZW| zx6Qh{EmT%X_h=q?VqGOYK|p-@aB{M@}u~ z<2u!t_X_v-8wx%16Rg%bU){q!x!*E#!^DF5d0CIQDwR*=c-EZoMrGIB&pSoqH#$Fm zC~vN9bHx7MaUDNv@fQ?dBvUx@*tYq&lwh~VdI5mAYlvBN@f{yc`Vowaiqg*3K1-h-^7pYuXOyquXr-E#)ITX^+9bfjc$wD{Zxp2@?2Wrl5In?O82+c z1?>mi3cb`#4;lGLMJ)ThVFUApMc(f&CPz)+c)VoGk$z>rC(TvqGfkE(u~+z==hEe1 zcfs-Z&AUfUBYzaW%JAK^P~+s$TTu=PqMm)Vt%bUQ#tWxEXkn0cO<|PMQ4ZT8+jg<| zR@J7)Gp|maN}m!s?;1mebgT4AN7KBmeocG1Ut zQ_g)zX!E^mHoG%8?$~`1?Icdgqx@SopZKYe?6LM-Uqs$Lb)A39|4h7L{r+b|MWvg* z?Ck@|OjqJ>Z_bFD$GiUuvx4iH@VS$VPMl9Nl&3qf;r<)bLNEq(|I@-7n^(Wk{vXwDkI5o%4N%FKtR<-Kf(& zQSIS{opU+57EN*$xL3GCNB&gT9@&)ara86QMY}jWyygjO9*@?%62uz9Z2GS8rZmCUd7p&AwSwIWmTPK^!?frIeUX_P)^oz zTU{%&E!A1<5l?6LEaI;gw%YCSL(9Z68*ggwKaR4%M8WbqK6@= zCjU1dzBqr?oGn{9+q-w$=qS{VT`iOH&J{ocg=$wb0cD`H6EcL^U$I zg!pQ%d$l{F>Y;o3iF>ndr0J}YEI;pVQ>>d%n-wj(`^UV+_xjFfZ|?W9f1T3MYM~Ot zxmaj-_qy;4O&MWI4W$pf|65!(bC8Yw?y%~|X@<PrW>lTI1`*`r;xl>u%Q9pmBZ+MfE;~e~Ux&3TczWhhADO+*` z)LB*wM5h#Y7afa!t;6MI7yRI>&JQ)#qrXyj*lp-Kl_kx6dkQDlxup{-pYv?FIYIKY zcS-NmXHC}_ub&i~tS!rz5j~eddQF)^%Z^*(OYclD4&P;SMY-sQiFxKr{phD(KfZCS zO04YZeENLL(ItG-j!9OyEH^!~#@6#(is6A&wX2Jd?YS1prkb&T=`^mAD#H&Aw;n30 zKl1XroAiNq=S&Snu|tM$o^O!m*x8V_Y)1E#*V|Ge&xS7xYGGBGVi?Y>z_ZoTs9+24 z^~s@o=Zkb{33mKe>P$>I)gBhZ+pyiF%lO@?uC4b?Lsb7avGqOio9{J$``YmH5&|Lm zN|)Kof7)?ds@}Ep_i(gZcRebZaqYF8FNztGe}9dLf2~k1p=z>G;VXAt_kZ_Y4Q+3X zZIAig$viH!U3)_9{_8!)XO(nADh(HOOq2Wc!#sNazq8)r_0_X`IwmhIeBpQeVU(}Z z(&dZes%xsZY*;2s7$)abUvSbPUMqk z3qAE`oiUM0vM64E|L0w!iD!x{bk{DMS@!p_-r2I1y@HDln)ThAd*}92%h)Lvxy*CP)Z!?vKUea%A z%u3qqwa#%i(=68CD%?&hBIQr*aosG` zughV6^7A#%xSZSUpY`kU=56g+=PEDFTpQpR-1h0ymmMz+w>?jG|*6@h`>4^<=iO`%5$lf1De#ROFH5m*|%)Erve|rPM^drxhme zcy#59+6zgs14}<;>U7_}!I)w;rFu!s+Ngf>uj&sTX0Lem;pL2Vdkr*BH0QoLv|{<} zwkiBiGDL4HOnLYD`FBV4H=Ba><_B+=I&{P;Htf515pVc9zV#8eg}-VTWkfmc{Ux3x zQSNonp-m__;8xjzwy$#kPARn|7rZOjt#MSvXpPsDg7)Pro|?uQth~1@>ePF$$<{lz zCq9_|yNGxF3|VUibKSM`s;=B-|NH)vez#8KF2BpuSIgL$o|c^T^v|MM+ELLNTdlUX z$6qznN;AnWlbLaF>8p;4gjW-0bGtf)S?JZ|F$UjTZ7o~CsPp0JyqcS0HSVSpKIY73 z`>XO_a6^fEEdPxr7Pq6qHlo+=B*mEgxWusf|MlwYfBJ&12$aY0bUQ154;N((ULtvD z_bsiNx*Hzf{xBs-{ zsucFMOX(IWQeHKsK)Jj>e!=tOOZW?rV3)#|LS6>j3y!V*(UNgkHybGBu=bHZ=^ z$#W;pAKCE4(m!}3|8I>2Vq4-ApRY(@oqw>a@H*Ek5h+EM?(8Vn=i9z5C}*zZwiQ~* z^2sNLJ&V15N3eVBY~W2r=Ut`db~xf7Z;N%GhK2Xh zn}MC}k=xnVuCe~ZX~y3Ez-89$l3&*=^-R~Ta9YB=Sj+R}jCyP_+zxq8}=@T+tRd z*H?yV+n;p4E?p|XARQr+cW%|@HcLyDbpl*&FJ5k5VUoOPg_^T$O`4+Q^`vWewq|^9 z65PBxWFosvO|f0x^10nt+00)|c=(mGS7_hf{pU9AW-?-(IQv^VkDB0{oa2ewH)|q< zd3I{-t$CU5xTX1ye(_XH*ji1x0MzIr5ZZM^-*Pux?JzU=s9ZeH(r|DWKg8i%uw zE{EPx*>CqVEyHNPfbN!83raPDO%^k-d+9S`x9O9TzHKmA|Z#h%j=F{vFhZxpxTP62L@`AP0-`%@vrgfF) z-!t-0eeo_R(kW;6HqDs)AoE8FN*_*K`FW#2`QxWopYGZpiN4~IetcK=El=a1$J@Wf z?#|lQ^0%?>skqL+N;x&x##PH#72lekV)6XJ?O%@+cbo3`>|vNLFmd!{K@IvzqX=Bx63Ve+MT$4S75TjX zdWHVNT+v4bUnXom9C`Soa>d<}Z*ezPa+ugYc&A`G0m@hiTG_YP!hvUBMyo+i! zAu44{^qA5lw;Z$0NtOHjwX(?|?D;kMbm0d|Gk=7d7G&o+beeMo7%G~1|NL|^k2f}S zX--M!%4u&}4?o;nQ~KBT$MTNpL7&gRIhlG;_`oiuI?XSpkFLL;a(@21!s~6{)SGuR ziFLC~C~TN}TItzUmZ#xY?pZE-cFtB?IaK$;-p96`N0;t$&zW%jE_zqe z^8Njyo0iXGbDz)7iBwEEXfDr_QC<~WFg@g@(!yM~)sJo-C|I-fdCj8vF}#QUtyJrL zs*7JvUw-wk$F4uf4u7Mk*viA?9E9J)IlJTSIjgKD=;8CC=jcm(|Da zTbeWMH13F)IB&w*!mpQKE?yX2J}quf+*afLl^<{1cfM>YzVhC`kK1-+?672MKk{(S z>V4u$Qafe`t6z$WHT<<}cZ}WaPr7q=+)!)mYTEhe?t!1tPZ9%GFDVF_?%zK9vvE>{ z=;lZF*S9=g$ai(R^6Jeuw$!YgsVw_TvPvXlvxx4CM&mUXo=^W+R?8H)wC3?L^No`e zFS-6axMPu~RA1}uU5sDutYg&3e_EsS+CCsCzQU`1CR4WhjEDEqt8%;7t1h*<)nR#i z^P+H_lhfk3i_0BrI(iS9D&F@#?tZ57-DPuU{}XHzQ_ej98say1!s5pKi3iUr8cF={ zn$e>@seDq+lqEfd3oku1m2oWI!l<#on ztfcPr;^j{#z3r8gSBb2xmyAzYSe)GSjO|2)qPy7g!_G4It>>pVI24JKx@mdTXlaH_R7azx7nzf9#jwroz>Z-tp3a+m2n zTe>1lO*Up~;uYJ9%42rzMakb-3D^2;wstnwPK3vrQf-)iaz#ZoyxnJCoc7O%uCAKr*pkZ^!OwWc7Y|k zrmo3Txo)&D&!|XTefRbct4#mj|9R}bs``}$CKsc&R7x&?%n~&1-mV9ty?uLED&Fg| zzu43xBza8F^W?)R{M&0yZ|xTS_$|@y=bzR5_e{b&c>7wr zr~MBf?>JLyDqA{Z!OC8j=Tn$xiD_x>41242d`-%q8;5E-9i^+6UYSyr7rivuBR=?_ zz{ALh58FOmv`+pWf6&FiY~MdEhW&D9l1rtwcs3n8Sru|k*J%3>i7l@Y&*4u_JaH-Pjg_XEqHCItAp3(!)whni`L3!y zV#M1s`()|al_@jkt#aPxus(U}v4=9I(~o3qbeeJIywGCP11i&Ww>^6AY&`d(aIuY; zd+oG9s|V$LnHQI<>^?es)`44_>kWU_2cVqumUwf{O-BU=2K^=m1}U`dfW@Ur1x5K; zsmUe9dIgnNL3;wtChh;d-eJ<~#Zx>=Ht)Ut_LpDv*{8{~w`a~0U%tEi`7a5t78Ns* zm7fmxy?*}x?=xlwMxiMw>UZD1+S=zL)O_$vfV@Cl zv1ZB7*x0AtTQ1nYF4cUjayIni^p`RA-`MJAom;={g7E^Cdo#TYclsDNztw*^tI#v;g9A! zavUo^y$b#EuKu2l{al&OMt(zGrboKh0)x|pFXl9UH9EwnwB)4A8iv0?X+~@xCFEt7 zdVkb&S#j}l{(Sqtr%zw6w^Mx4B0Je?l6>~eaE-(^`P4x1ZYAri`EUE5`0n{3u>Ohh zP9Mi-3oPwxZ=JcXFe_Cx&F#XW6?^}*L=>jw*=mNLWpnAspKmNODQ#hXsB-VcA4de1 zYt1TBoV>+GFf?}7>B`NWQ+Aww<+7ym?zRP;AB0aZ-Kh}Y=e7D5&#ZZ~*&7sY9Aiu0 z+PHJ3{)UH_ElxkGR6Bp}oO%-9p}S5E)<;vPJQMY(*7Sd|&UTS}N8;NZXUww`md;`R z9&-CzlIm;43JuTm|FX@mq%vN4#hB`N(9zag+@+VZrhZbqWlhDdY2Ey;!SCNC-fOhe ze=Ngd+HXE7Gt03iupud8O*wOn$ZX#^>Uz!&c~*BeAJ~_9>Z;tUrgKrto-S+7o~6G@ zZQ@0y37V)xl~RtHx9ICQ{dvyD~lcd=#4K9drZnm#O7I_v&S?aUn) zJyxyr8hYZdzc^j`v(9aWrN2jF(~F~Pc(1f>obiv7qgd;;@t?|b9*t}V+z+ja)VtiD z#P+v!fn&4S)6-kxvX@Hz%imSY_&>soE=ibPVz;0eQjS+%c<_fzD}y-2y@}Ibwv}@6+gQ+p1$@o zH=yL=W(8xv4G|`(@0cvDl2%rPt_j++{E z_Q^Ggj|UC3#pD_EH@|9I|KL!@8@m+7!|Au<-z6KQZj)S--&y&^`+MW2lf@ywxOBzD z=eT{)$e5A0{pzlaO)8UAFX?y}cYczRnW?D1d0Euu3fslZ7e6x>re;Jl6}tUXS3hti zMeGkp(TgpX9F|Ny-3Ph0q?@)Z&Dz0pZw0&mk*)pH%oX&m$Hv`ZVLPYxPoi;)vg}D0 zu0l;V@8rqPK9o%Tr{td8Fw;~zB4as|oNVItw>w0#WqL0>zj;GVpDD2X_@jwGy#9DD zemH3d>)L&D=Vp{FfAszN1rv_L1}`qP{HtVHBDgsF>W8#Dc3Wo`{@Fh1cBH_M8y05{ z$=ln_oB#J{QVQ$kO<|k6a%49ztN7trb=YUk^M>% zxOuC_*E8OSIRDApK5^rhFEZvgjkz_oC^+WC+$XQKmbe>#{2`zuUzeQ(Q|_lI10HkldR=rlela_mRtfjq@oieII!h#qIl zSeqy@t$3?WKygV%3)`gyIZPo}@@|wkY+!0+IkfJ@$<&KaILpqd@a($edg;TuoF%qR z>63U(E-aKeeu+h9o-&W!rIu}5#Cz2X-j`oUm@W~it4GMKwP(ko7fq@8qmpkz8%DCUFhlnq@L z`*-!MZ#w6)?ZjKZr_&fR+P8@un0ZBZrLc5y`zMBjX~hvo1x0%lADXPV9u$z^^XJ1i z(R&_k5z=J~_9{PQ4ZHeb`yYp3_w~wKo^_f2cyEw)A^)_v0!)7;8A_X4*KVoPYDk z=zN#O#a|P*UcRJpLM2x3VfO2f7m}}SaXVWqw&H$hLEX1h>97+|QA0DYjR0tGL!Z;AJNoZO6kG(ziM*Uu0Nm6y)603R@ob3Vi6U> zA9$*}dFtMF6q|e5Ub4L@ru_HYsivPi6Knl9FWh>)ZrSEld!|iayF|lt(#g(ge=S!$ zoOn9ZyYH^SH1=PUd3-->Gp=&=T+XyX2?@(0ud%i-yn7_{#=bMX zT{|`E3--hxtd@~}dz?4YoV{c2pNQ!nnPk$>-%9v=f8V>yf8=iOxxabsm8pew4;I}M zFN+CWI@O%#rJOfYBdL4t!fRQs zd?{M)oBt{4&RWHME}Ny~MnRSIo&KAzPAv7&n&QpA|Ii_QZLulGd21K?T(zvZm(Y@I zwKQ_sGnv`OCI{J`PrZLHHBM4=qu1*d8Lx5|H)nSHK6(=5k$zzMwKQsBbljN;idIt9x+w&~`^fYmtVx4z+ri$&X zRSb0oA%#1q9Z|U;B~h$-!CIwxlK_kDmIr@*lP>V{9+lvc`Rl^o5|s8fSN5r5XnCho zPUDgj(^qWs?L4IQZoi&KfRSpUO3)1ZUGlef?MOWy+2EJkJj;59-}a1@)WTVh&rhyC zKIi_tZIcVTPG#&mR(MtZ^@BZ!y;9;iYCN|uF7!Wo`NZ~@Vpp@7t_YhX*;-w=;3GPr z{70bA#{(C*hl8j)1!TaOalulfidu;me*Y4umCXch@=azpJT>h@qA|m>oTHwQ| zWZUFL{@Y(Nc^(Zoykli|@RK)d*nDr_JCLtGZ-4c#+%IoGtDn!=q*;AnU5X^v)w^q> zC#<%tI9N8nwy67>w~6k|DGD!Sv$+$*Dq}7DGpzd9-_#s%5|FZuUMb%)MUlT~&wI!9 zqEEIq>^Ww^?X3EG_o+k7jwa>e&7DHuxSIbfojV}1?!toyH^f_Af_N(R5=B;U?B@Eg z$V0ef&&mWIhvuSL@twRn$3C)KE(opY{5s+ELoWTLuRvzN!>E*pPAbx{w*bd*UILdbE44~#|-wi z=r>Cq>*`F5pFg4FjD6Q6myX*LgQn^=`O3W9v0o`QI4wd^>$R+v(63oRYvN{m6z^er z)m9%KE3fEgp0lfFy!~>x`sC(K(#t=e^Y$03V6GD? zS@!#?*;hlYDL<6|&a#!eEm*kpOx&~1KQBB3Dy{QQw*FP*4cjy?$t-12!HWxx`l~`t zB~CGjOt^dL2mb~J1^w!e!7V?sTl_U|E;{terrYhWMzwShXFx@B;c@Q!vTaAtKU{bt z?(%Im=e>s(+s_veF+CgI=Q8Ej{h3;?Se-u>cQ>qf`L)b3kh{5V(<*tHx8Iur`2x1R zc&I#cwMgVu-sd~d+WB`B2o$f?THJl(Y-C)*ubG#>HP&fJe7_ahX~{HEZ|?mA*VgJJ zvfZ2A*>Hz>(d1uw3rdBTl|9cpCekdk_s^=o7Ip@uw^sp!@;d+Ne#2?I-uCG1*PEkf7}{GMd?{6{-DyW(%=cx|%m2K&Sbk^I zzAWAyycVfZ;&rn;e<}HG{B>q_(erf~bM~7Smu%OP_4~qeVf(9#({oxs|C@8TsYZ39 z^_C0&@|Fe5-ZhrZ+A%kdKl!!aHLciU^|E_iOSdPyR>)3eGdZToD79>^0<+VeCkr3V zC^S#6mKWS zbq=A;r!5+HPSCR}X;^5n+%mNz%P8n}!onY%#cC4Q*|+B|GSgbOl<$MUEoMatPAsQw+cVbi+y(WJO2@Gsho+)wTRSMhr%I5^1 zvp6a->*FrQqCHN+jvqCTp5o+5O?8>>wfuIC#r7pnN=&lyre=GkuwF|{D*kWx)P3q| z-G9!Hzb^kRcY$N+g%x27<{8)oNQ6I@*!)PU{=nQ`)hyBVeYI_& zCW`+Rs`j7L^*FGS@$?~8)&JuHbZE32gkfT{+@%Hg^r??JC-{KYQoHDyd`z%Nfz1omSrWc${>a z@7ayJw~de8oZVdbsPo>+2DT^m?_&a%gjGG?b~(}GPRxoEhdxbCeb*3MHznRl>dn2= z*VhT{&1jul=B4ehHaYG+&xM36rfj|43p3wt68!yn+l)_>cxSsM_r6=THGRoxt6r0) zji1xL*$H2l%3ayu{&m8ZMV!JVGhgV;`gW}2V_e0IGHVu@W4Yg)nN3SM3?i<5JM1KB z!y>V2)7=XVrSFv9J^UNo_j1MEs4z==SJguKHZz5K;X@nKPxl(nO1PxR!FG-J%7OI2 ziM7?ID_tI#HYXb>X0o3Oa4S8*Q@m{hi{AbR%da&=(+JQ`P*9kYk~pS z*b;uPO8t6!y5_!`37YPW?yr}u+^19L^y{3tnzwDXS9dmdv-Fn@Wv{buzcu)?)#1ge zJu?-1{rBk@SIVFJ_g2BmxaNRU%C_KlvNz_6OYhZQcQ?O$ddmyh*L+qx7939gT9+Vs zQ1>)D-}*_>pE5JsS2w)-m~~j{z#~KXiyf;X^QRj&t+A}QJimH=O3ao$V&R+Y8t$)3 z^El7lsv(zfkwGu^i0x^1kGbFS-T5A`;V%7m#gPKjdi>_tm2<7r zEfG_5mNZb}lRdko{Qr%AWpNkpKI*J#=U!LcaDUxl^`6IGZk?OVGt{};+%-&2lET>Mk#=;hlBZS(s7NOHBkWah43xAC1? z&L^F-_9Y54oaZ`JCMOD13hv@O_gL9K{+(e5-yU80*)HW(%-K_Q-*0NwG&V4(Pi#$8 z`7WdR%Cc6j{7vKUJ^wFF__S~Pl|$T8!rR{P{aBpW9#F6Tu5Q{4wQIYwA|AJ1)-|(u zlhK~LhWCW?%5&`gU2N{SXZKHA@b0SzlRNGm`WfSLJz8yH@Ye6b-c|=*2fpH% zyZG$3eOcPyoC>*&12$b^nR|_Kd#|_6OKtD^hq)74y~Hkbp3Zq1bktOwNo|I8Xz6Ws znczhxNps?Ra~L@;M@`$YsoXp8>+yI~u5W$3sc)8Q$eD139pJt*^`WNJyWp>l6@UL) z+d1k!ztFOw&*!kuhyBGut70W?Wu5S?IqK(7y4BHg<05r8jwxP^Zhc*^Z!_|LnVB22 z?!fZ%2^@Kg^hC1K*Pp9O$mNhdvTJ+v`>zugMlZINtC(=^?}iU4k}+aCt_JekK5@=} zu%DGfNsQ&p`jEV5$-cr8{qt^LD0uOu_3KpDz^PR#uEyurF?0u6RZsq#aqvm_-16uD z6+hqOzifT~``ORi{yM0Jn>%=w9l9_hBTXj3{L8XCj@*APT=}%fIOXx(F21a^x_YnA z7au>K5b0(UNko9X^XR!j6N9yYjHvTB!u)J3o5KOj#AaFWI}3?lqTgU%Qd{RrZX_1;KM2(!@?W$nLtXz2trWtsoZ0Ya0S5 z${60BmSwl-?Ayl&wBmY`j-UDeMt*T=ka6jdjGv!&34cED&;pK zNvRcFdlcihc&_d`U*zU$_^Rn~#LE4=8l3m;zb?CQ%VT%1+#ic6uYYbYsZ5v>YiJ?A z`&m)7&hImaI{0>e>znU%WbMl{Y?G#&N=2S8{AKoi4f9v_7s(2Vb3Jc=)zW^p_`$=& zkJ}IKFxK#PoO?`spO0MVwA&$HSE!aQ*s+P>P5a&FFMN864u%|Y+QRoJc)CinUqjo* z^Qp~8dEaoI-?us_ZPrvav6$vonP2O=rVJCg(!ur^Z!~+*6PFdjT_NJ;{^51Rw!}`Y` zDm;84a&V=c>jkGa6YU*OLzWtFYbn^Du#%l=)N@0)=(LGRfVk?s(|5zHLsIm!BsZUr zH?g=Ic}^$8pht%J`Q`@?^>X6B-iZ4unYm#pYrs=`PTR7WAG!_+>$b0FNeZq_5aFtP zDp|}d)!B4YEY)Dr9`)_M{;OHzWGCd@`15;x!`bGTWxp=_r><-5wDL65Oj*iwzQ}pe z_5+Jg=&*>o)%9Mw_u0Eij#tNj;-NiKH~SxLd|_;7XTQbosn4@}(<+R%-Ca~3WVSu% zS3z~jlKwY~Rt4HT=eqny>Tl%=W$AzB*YsPi$^P9^V|XwIMH+%T5POp0U3F)s}NlbOLtD zTyqe8;WM=^$Y%4A%~KvYB*vtsRc_iY_Rz1DrQ1{cdh?-dzsO6MPJ2Ii+wxsETrmD% zQfpuuMTexmOt~^bMs}D_q&@FkBj;n23>uvvaoRNce$^Ee|Gy``xxiAiA%|PvB~qw zH_PAune6F(mHR?!^lRG-(?h>pSm*eEC-=8(9e&MeDz?8`CM{ZPpkH!v&sGD;V@l>% zIX{}T{_Eqp!tpMBVV|sonB95pO}sk34If2r#WWjpowv$4f7(4c?R$yLtfhaMdgA8a zIJ(TEV*jtXRtpbozV*3nY3r*U$29XrUuD_co7b`2Fms94L=)BP8c$A_v>trEUtZW$ z==aPOT4qslLAk9V()yvftacC2hVn$tui88Ffp+4f?j0uH6C9QoUhfI%znEw1mMYG~ z{P*z}lgn4utjnKY|NY6+*Xif)^L@{sXZ!cz+1cCA`}=?2oHIjp^XAXdg}xt?>@RD~ z%Zd0Fmip2`<@P3~*-?d&%TG(JeSTiC;jXEle6CmH;sfg&cSqfFoKqb1#PRdZ>k`NI zvV6`ghz#BMD!TMr$nou&)jB0I>9WT+crRn#>2~JB?4=UF%jHZ@HZWJ@92MbNy}!G7 z$s|`p^@=~MKTJQgYrE1{;jUHXi&y9EE1s}j;@h6k{N?{FUOJSwZ@j`8Bm45Z#Lu`b zu4Yz0zMOK|{&}&LMLg3iiQ1g{Ph~aqQuPgK3MC^B4BD{X1a{a0GkFGBbwCb!n(amwcoh5sL>@?;m z#lp6@@;7}vi?wz}Kk9hv9CQ0cXq;g0-e{xzr{89XUh$Y)CbzTPJ=x^w9Es-noIld;H7m3f1_6_s`2^&w3@=VBp&Dta6d9-t%7JWwWmDP6)P2F4xg`bvpF$pGlKrKFq$VwdU@Pkcw+Jg9t~{=(vkzoU#!1w4+swnut}$n~##Kcy|Y|7f#*)XJ8_dyGT(8oWE1I_KIK zKdUPFxmDJ4>L$&R`W|}pEpuex1Er;Ld*mM7_IYvQ6G#3E&E@%v^0wwZlCN`ERlX{D z%EIr9N*A-u6;G1>`6^(CeqhwJAAYCo_TREwByq&~VxUsp)5w_X%d+phT%LF6&XJDp zmvQm=h8M!!)SAzh95Ua}m{GU?F-LrbmxQL`o5Wj8Iq{JvJXhHkU$06{lnDP(cKplY zz24KN8HBKXNj=ix+mdB_@vd)6^L{b5IbH|MtCq*ja-UVK{5N%x)dBW9)27;Kh}ZAf z9&zZ)U%#js@ipN$Wz8$+USc%;;&yK0wDqAqr74Ts|5QcJes}J)Ywyjc>09>Qe!OL0 z;>M`>qRO>e+#WndHz zxJN$}d?cQtoIO43)i@2mC4?AJK1s`>BJT!nrn!>mOWUp zqcGv#yVZr+H@&!Tq{ck*4s(}Q-`BP1zS$47g$GlYf7f@9W?-0A#K<6ww(y`NwYWsD zpz`Y8+WOmOn>7CHw`a}1|2HrzMa^&f?Q7d#$z3muny-LF-)7fnbVcRJ_qiCJKfZHC z+2XyCW_!eL>rWP!j=orDxqJ1e)vqhRd^&p0e%r(O`HMbh3+@#yjdW4|80d5Arihk% z@kC$q1;^q{^iOmC5f1HUns@E9)v4}`S)%0^HRStUTbJf<+HG}HBK)a|*X55%`&PKj za{E~l7WP>~yQ^s4+0UC+nD02FGdte@Q=?V^ll{-58|L^Nnb5Ury7I^8PAj$_jk$BK zch=V=v(+xKtG{pFy=>Nuhxv8JddqIsRIH6VHD8+R;H#`@p<6HPf4w_t#`IUuHD-5L z+Ff1mx^c#;_w@{SuYBJWc0cXshr=$@=lgE@xnoVqTrF+4)X(12SASluH2Xz-_rWz< z?KdpHKB-d5``T~)_s)^rI`h-r+AkbS*2(F1)kO5Md{GMS&s#p(<&3uCq~^{85AE9D zM|hrj%Awj7((Gy{X?fn|qv85fOxjlY%O-#FvXh!7x^-pTMUmgrS7b@t*=u*x2yB>vE;p z4@B-P?!11x}B_MZ#ufi$O>f@oXAEVC>0Efio@vVL znmS(TfJg>LmNy)Wd$(8bU;6v`^89a%$HF-tO_ezOg7L91Yl)t;4@=RWjEcFTHw8EX zjx)UTy5rVhRKxtw#%0dJ_+X~~`=7WN&T1)0Tx}29aGv4tW6@9VUNCID_xs~n$6vQ^ zo4?$>`0(HP{^!dQ9rv7k{gZXgyrVfvdJC%fJL@Vrcf?-Ed9`Op?Z-vp2lwAEtN!xZ z{nu9gdf5l-=U)HwdiM1nuW#=!uU_?T`~L&#_fLLbAHP>AVtu^q3ib8z!Yn_2@2cjk zwa|JVIPsv??umP3GPq~E8=cL2dQRZ)h2v`R2im7sdcPOCQQg>EqW5Lh3x#Ey4$5Xt zET~vHZ|{@zw4dv*>|Q_XQ+@dPfA6OMkJ|ftm*#)|uW#h*e&%1c-{Z;Are?8qF z{C;2dsrM_>tLuKsU)?P~`_u1NH}}Q=n_BgKm1g~dd9pvA#?NNfIQ@Rl1(&*mk8gcV zQ{V}x+ExDL@1+w$yfJmTam4?*JBkI}&o8p@D=>evMfuv%py_va?A}=T>)jh0TlpDFI$|e^Z)#d{kz^<3#V|*{k}N; z?)R1h$2@u(Eg!boY30A-6Zxko#nAsmv_UC)qAx2$*rjNu?liq+bMu&v2!}5BW>w|S zJ@tKY#G#&2<-@zRE1qprT>U62BsZW`k9TXLM4fF->A&J%V%sEdG&9cp{iAe`^xikF zAJ2a%XaDz4t6o0!)4_>A+G9qkLNcnX8A z?5>KscOcri%f}_Gc}ei|$caXG62qrYeK7B92?p*Dhf4qmeV0)*ARm>9U+Kc^hX0j_c?GcN*a~brO@0_5Sq=;T1Gtx_|gYozf4E zZ|`gPcCWwKb3fH$L%`&+>G3svdct<6t5)Bx%gN6@v9NEK?wsmF%0WdmxhentNNX|0|*Un?#xyiFst65L9ncnYMO? zT-eVZ|M&kNzdd;K+9!Rv>2mSNxurXzAASAbREXaRklnk zeT)Lcx_t%aR#Z#3@QFx0mXoPJCb{OnU59=_nrLmzqqUdJ9+lgPfk`~=ejwZx__BI z2BZ`(j<)`>b>8>nzt2AIJyKk|)>J>}#k$E)XNagOI5*8}Jexg3)?m4j)q`)3tN6Zn zJ-?cCle2weW$2|-6BI;OGA?wwT+iwhzPPcU^Kx6fE#vCom0eA-oP8e!4@CAJF`IXT z*C(lU#u1gL-?gLMY#XCu12m9R|{Qx7(7H!5&! zTO5#i?^2NQ7ej4>iK4l(>Vj*OC#`1rzDC=Eqi@>hhFaU1d2YO}*RJS#`nXxj6gRe% zcbqP0VGgs(&a-TNLH!xg_i-rRlobMw^4 zTW{h3oI# zbYhsbUplPvf8djtYyN@%nprb2MTqiFS>c{Nww5#c&D)Xtffa2lTF;s z5B(bT7hm%8`v)jX9X2eftgio{7NE22cF<}4Gl`tq?GG3DHc8K&bH3ir_C|Fn8~dxT zsonEWOYhssxMb5w7ayg&7JZKw#Th=dH@YV8YO3-2z_zcNOl+U+Om{xDY1iB>vd~^~ zp}qJ{j+{V$hv!SQ+_@)wQg@irQSN?Jtiol}$s(b%lFC!=F8K9LQ{Y*<}Z_0B@1l)=#X~mn$TU*PbRSow!HcL=`Po-zg2Vj*DpOL zqrJv-|Mrc4%uex~i~Hiy=_K2m5qfshuCU@2^Ewspo$lkH}x!G+tkug zHfjB2aqSbH?h*MCoV(qgCck^IE-y8d{ai+V$YhBNW?RIA{x{BX@pG>ex%hjT^!;@& z+UD@F{(E~oew|p)i**;`yi(xo!NEKB)P^T&0EeLI~3wzwu?#N z@uE7B<4e+Z-nh3@#q`B*r9PGog}@kK$kb)$L2WW^$UGTP-#9liU;b2n338sSTM)>Q zt$FA{W6gSp0!!Ae-KUop)?BKbdF^w~z7^IV1m?3?tq#fD-<2^#^rK99%Zu-u{OW9L zYRi6HzIbnTbGU4nk2>#*ckhq5OsEe}nsoN42a~iuSLSM_rVR`ai)Sc0Bz_=tkpAtO$SWN?G-*nOU!`gKl zU7B;5yA!pZvhu{)b=?g$?VG)E^4wj|ugU%3Zm8%m-y`>JqTD?x+2d*#-=$`T#s@n& z-gHx7Jo@)mN`zCB>fP%n%9?I0;<)0~mDj`d;q&A0<0|+6oeIyE{#yF$dDFB{m7DmZ z1-qt+i*GpR77KK8Ty1&$!a@XeZ0PLyNn( z``H@I7bF&A;HO|SZJOSO9O z7GEamY8gi9T!G%=8}io=&5fASyMNC5FzupsQg0hPCVbkqNv+fBbbwf<3f}x~A*2YDL5;h6EV|eML;hueI@l#DHJMT-Y zC*D}sZ#xZkF=9qq>6s!=t-LW}kb+azyS+Y%-?}Q@Lr7CSTgDZ9M&kE{}iAbL%K{ zuI#Ow_+ggA(wb{8XZ=mq?r;}6y5UId>Q!*ejehm|Kq zzPNrbJE3bymAYZeyua`FZqL8*YD(Qaxh3{Y`x6TfZt^%>+4$>;{=Mt*iv(w1QBLR$ zZ(q*tWwYs3$-kZ4OYi3=PVL_O>(is@{L{tTe?C2W^3A8eH>dwEEwtOe$3jnEKd$#h z{`bEQPVfI+Zm9p1_WlIx-sWp1bGyZF-;cF>{b=SrCzYA0#aq<#6Hdh4e!*C`BenE@ zWZjFT!~gGu3Oo$|K7Vfa{~Nph6v@8$Z)IU?ZS!qHi|abyzozSab(hpumF+8kmHcgA z-L<>-?)mL2={{WcN~~>DP7H^KcJ`dYoeXiUXBua)g)Ee2tuj|w{9Hs|^UU!bN7h_z zy~A-{amDjn8OxTMZMiJtX+Lv866t=QdLcJt)8+!>#%j1Bj&mOq0`=u{QOV-haMa3 zUHYee$C>KxqjS{lJDo1*Ra|@(|6sR(gY%lWWA*yH-%c(M`m@+FxWU*-@bPr5&QINu z9U_xv9s1G8wCnw;zBPflSpSa<3#O?{VD_ z-B>1&8m?D-;qlxo8E@XUi+j&j|NWa7T-f#1ntl1Hf_5fZ#S2eb({$W^>4q!6oL*@7 z_M!77j|W8;wNKS1TECN6-7LMZI(ttNi|1^|Xq82?kM^H%FVbW-a*LRvvzqzxq)w-; zX4jgUyN@*{AKG9!S;aXntF`Cy->bIfzj+FncrV*}ZrClIEwbd_4xXv^Oq?as+FvKk z?^pelE7N{qV)Qv<)x#_JL*HJWzhu)9)2}zXJhn*soO;M!c0lvuJ$30t*F1X`p3wZ!)8(CuJ;Sc}R!gZYW61@o3@T52U-a}|K4QWXc-iZ1 z506};zJ7kr;Z{$s!raL z$h9r;Q^&azH*?;V>urpZ%4d{FcyYF7ndstostJ1KD^`oNn(|Y5004N_EGxsO#<(u`4PYtc9WvHj z9`DDE%G&FvxGZt}v#3FqzbrVDOHq=m@pv24mun{9mlki?!QJn#oLcP@Fx~KJ-t&W- zGuks$roTL`XB3mNZBO@O)`HLLQoAgi{+R53TXDYH@37s94>L?={7c{oTKp!W*x-g! zW3R)M{zDT63scQC3mP%-t!eldazDpSDfz+kU3+q1%`5|9|(#ztYUGcG*vvPX-`G#cPs{n3JkR8;iEy#>9DUjO`N>wYerx#U*H%&%?LFN@mXX5Hlc-DWqhj3wuamEYZpzMo+86y#MomlFCGTFKhBN2e(K1-%}J><_UutSJU4c2s+7Oj*V5wJ z^wq65Ts%aVmTH#+!{uxL;hu-I({J9-nK9SeHoaOM&k#4wIItw7Gfw3Gf~%&tt{;+m zdA$2)#qZn4_-6kvxvtXiNS(uW0jp}h4%-FZ{h`;>q_>@nk>>q=?i)Y<<;{~1FIG0{ z-rX^oW&M(}m-kc5pE-!THJ<(cK5kFNM&o~3>FJxrP4~w|?ccYl?r-g7;d8U6$49&V zPyGAq*srf|m+g7NzU2J5KL3joaz6ik^weBG&SmzON6*dM-Pu3AE z`akSba%S7!dcS1jcD^%0v0bJLf9~oj#ZH^dF zPns*PkkY)veo02B#%4ZkN1-sgxm|&L@#^KasmXZ=|sdX)`TyuKDXTF^(ZM(cbEhyf8ch9`1u8LZhUfqjY zU;8=dpqWEKo$tiPE0<1n-2HG#O1HU;N!zNuanAjn>E@ZcYih(c-3`-qT$&%R(|Dpe z<9o+mmf%<0zhpV^^VBSzHfPF%%!OZju5ANX7-gLk7d)!?`)fn?s$z|G4;0>pGM=0t z#?x@q_NiI3VfPI;^FSRL(`)m(&q%$q&^{qi|J&9wwg!&AB*a3x0-zJSE0_v zs`#VtTzDRS`B#=+@Ugowvh`qW<8*`gti|&V?wT~~$38ZNWjn8L-kPdYJI}0^nJbgQq8l41riqO z=ds()7yiS}`<7X_-?Njoh9P5?_VQ9Dd(Wq{Ha$qxnBVl-a-!VzK3VnBKayYm{M@Ux zVbS#;6J3A5J^N#FdhQqLIj_spEGmX!h zt*ds{t!a~%&f)vEG3nq9p+(y^n$26=)S&wNhkt+d#pU(3Kd&c!UQ_yS%jde{uGZv) zNXZM#t(+GpwZy8%KVr~iYhEw&EV3i|=n!oPjhh^&*1z!4g)mYNX zI;xtZ+P+Qn>ZTI~N=DaB`0b-#SX-TacVWfcy>_o_tY+VdJU73~!)E?EnnrrMASLvH* z%Dmn5CMe6R>8xkb*<*7KfGr+to>NC`Y+!v4^`ucw-tNOAB>oGC*juBO&2u`iW(J{ zIc@u)elyDBq_XPv2_Bgnw5|AdXR*HCuzFkDvxTt{a!Ok#vdhPZ>bi%1(E0Vs^}3Mk z*~`l|Z3>*q&6qT;WM;|x+HY+=3il-P<5F&ZOZZS@v8Si6f3feT+Xg#@HW$tJn5Xnu zTOi+~B8bnjEulU8d$fGpQZGXd?JlWer-H1GzU?!2{J!!$Q0uu&werM|T{pVr<$0DX z|Nn4R>(S@T6M|e3JPPi5s@y9M7P20A@ceRWlZTf{?a|dz>TATd?7ACpe9f!A`C@&W zJPqHA&5ql2)ajHI<9un!<({7>+<&ZW>=|GdV-&6PB$wmtZ!6bqr3XT}7kmRwe+ir> z8o~X0Bku;L)@2*=7YAFgIX|xyNGeJExOC?88LmzuFN5{vbgt`o>PzlAH0Qk<`$SLG zB@0#@KFDgjXUabYdx=YH);B!Xahx;X?CZ^?)~<`&_OWF2u2(&J`!46A&Yx~NANQ(p zI=8hxGtd$9d6NER$Lc&8Y1Xo6=2sKzUzpj5-CVwTPLEY`SMJG=$%Zm)RyP*EJ-GMo zo#rFgc{a5e+&j!ro|+)uC*$x`Z<}n6{_b^c&p+QuWPLtqadtx91s|@T2cGhYI9VUb z{&<)>+&wz;sr@FFqUl>>Y_7Ggj+|wDe(Eo;^?sf{uC~=yQ^VJ4*-XC4DxvT0!o;As z>`jKr?)ks-u6z`YP5yjj3!}CKr=g5z#nTUW|GqZ6d46;IIW5C;`MtdGJXl5f1(yH>jO?j>H4W>zta?Y}pzxi0STzK!qo>IBmddAC&GUhTD;>#&;T zvw`vNklm7>Pu<=1KX#q(m(b@KTnS%KUQmu&sp$Vhg(lf()`+TFHgR5=S=rSpN>cV3t9LV zDO~9PoHS`t_oK$izN$7m!z~qs|1Qu~d57YHN}Ix|Npe9>2)1 zIz8Prj5+x5^FVf0|DL_el~R69`PR~BW^HwIijDu-3wJ^oz-!7>t&rE z{&(G{ze_#Md0`jp->I`Fbi14`J7TqKs<-AA$(5U4$$YFboH^l?!@UQIKMqy%b|fpB zR^Qlv*lp80xo?#cU*&(zn7`*#dof>d<-9kd2fo_*`mmZ;ggpJ|nK zeVDzBqpMve_6)0Pkx+QLN?bkD!JhPL7Po2KFx*|H5r6MZ$ zNuusDh0gkgI}W5S);_GhR+#^@r?dR>&AXHxO(uu>tM=^A_EWNL?rSYod89{WX^^Wz-9juV%4z zTkW>&LiWsL$73neZ;H1pO1^Ti;kZ|QcV2Z#v4cjK#EJYX0v&nt4R;uKPt-cC6L;!w zBYPE>zsn-w+U|1~#VihQXpxW({odCgn5%Mr4ePQ`4!0j1ah*Qp{0lRk?y?DWQ=Ukx zH0H`mM&7D5wGjR>iD_}cj5O)wTWfT;oXifCRGpr@KEBNUmD*0rX%i&O|0+}&rDfHf zSkyV|-r8+1rl}vEEGxFccfy6|TGqRA*0u?Ca-Mj&(RH!4JuID>8;pZ~{9E|L$zlV~Cy{wVMsf_{44cH1 zC0_W=v8i9DDN^*}Uei{d->V)xcxXN&SJ-*uV^e{1{5Sru4|rPIo;h#*iDPP>LiPLA zw;k|#*|dGV_qIBp7ymb|boP6~^XjCu>m)8`0j4sx=!^c(ee};*dj^M9W|`|4u2}BK z6}&__>%>z2eS3eH$IYIe+xK4C@L^+6#wpuxpHA`y1s$w3o4fY@vUQ(0&pz^s-tVgO zm$UaFW6IxSCl=q#_u1UkV%%H0;^@<3`=c)=F1%yGa8}yn-!F|RoxNv7CGL0&F&|qW zV63h2e!@LBgZ`?M+Q(Auf+*TM#pioPQ03^YNx+WZB?$|;^rR;yOmRJY;&DwKBwuN+rin793n4m z)i?c<<7z+c08@mC(@7E5h<~k>a*+?@R4-aj^=_HQDE^^bO44h7OTx~Lnr@9Jwd+qk z&)?+e@=@%S$48GT54HDrGVL_9_}N*1k;yum=ZciFOVyrDZF6+`#f5tBIC$;fWqp>3 z+r9FEl;`17a|F3R=S!=$awVuf54@0D9a>yux8=?!hJ=2xY|}Ma>J@LhFL8Pb8LgF3 zD{`FXSvbk9bADr{>8&G-a-M2QVg5=&dOzIF)FdxXp0)X==Z?gd{W%%8ZijDuyD)Rn zB;R)@*8l2cUM%_T@`XI<#RiuwCTeWW-?~7;u-W?5zK<$LR2Tny?@&EubE(v8^{b9G z5n_-3WHnyoe0aambv!aPg~tn7y894lVpWE zr_PNjlv&1ip;F@Z`Kt!I-=F+{Xkv=o-k;{`udY5)u9NzBK=GH=6Qe%EY)&5k8=G^z zRu;Y8az$0e^zFr5_Z^{6Dt7JoSQ7Yn|LV)SOs5x@i%3hyE%Ck($|~2H8}XpBX+vW2 z;)-KJEwh#ST`H$u_)^mNa^;~*_3`(obHw^jjj?mR@$3D}#cP>vKY#s6og-_0KU@Fw zJy9-u|IcpGmp{7S;oqa|t^OwLH}2fIb6q^6=I-(B92KG`w&l**#qZ7}D%3D}*9Q}Q z$u+l`o%cNKeq@!QvfGJsx9HrH3)U>*UD|W}jjOe0e#(s`hm2s(+ONw>A7&)AeHJ*a z&FQHt{Oj6>rDCs{k4;`~^l#&{$_)(Xit|N3NIUDy4tI(OdTVKup{+@l^ zHWx{-^!LXaApU zJ#`~7h0kfXi0;3AbN@BQEtw*h;TfeAd&%R>;{(tBc{SVqc&6x46`H~Fca7egD-JiV z9bFpjEZMZCedac|ja~ifsn>!QZhREI-X>3}aYNLJ9nAfkFKVRE$Pts#Fq+f1W}9lJ zhGwn+vzb$<@g>J*=kFejP1~nDn;07=d-wM>PrI+|Q}sG^WpeAp+%C^}@ZsK%RPo?t zFKbQz%4(dQ)Bo!k*M{!Q=wBxk=2ow*#hb zd+>6KQkUkO-khU$@64daH9~VYL32Yne*&*(Sel%+o(-8mYJQL30q#^-araFL7#r zv{5+!e&I&d1v2NeO%s+qEUUPXax`sr@_eS)!#5b_Zds#Map}~_2Xm~J`doNs`HuO5 zbioR?sULH$G@m{&=ioAqwx2TUi)>rw+?-n1A=!RTR`0&uzoX6Rx?9xLcAmUFp+vVQ zbFSOdGuKp?-gM>TWm?*`(28@x%+%ns-*b%0OMBIO*UXK*dGdtpiTN_eF8klRZdBe^ zA35vtd3Tvs)$ZLFC1w1Yjfy#>jK z%X>3_(aimRX%^@0<4@Nv6ixLEQhB=2bVZ5nA%QM0jw}1zT3*zw6ra@cwo)(h!$*(o z>YYa?@+1euFR$10idek;ogc&94Mx46qx=F-?=ew(5@i&8xMsl>fA{x6cWf?QZvI@p z-2M0Q^?Y|Oo!fKl(m6qQK7Lt`fa{t*Q#OR_*Q0Go!aJk{O>m{Qr^p1_Bh~CLFW5=Y)75$X6!m7 zAZfjO`iusK*X{Yussig96}m2b{2Dk#pkP^XLtmd-mcGxW)bPbV3FR}i7AEWHyv$zF zALX(l+Mn;4-!>;D(?bf@kzV09wAP=Bmg=(e=(LJ9nq0N;{X>@`2B#x-^E992KNa72 zPRq1>t$tWH=am}cj(t0tH@P)DU9)+9R86{M&aU8Um4a29Q+~B(Km69Ca>()_=d^pv z@;tn~PNr?zU*|b_EcKL*dVOz64_#SWA(Yg2Zw)H39 zmOSbf{PJ(v8oL9P=L{$3HyoU2b^6)$BH@zNrg7ZUPF?3bdUr(`@7lbl%K{9HG$bme zYxb^}OWO8T{jQPz{f**#q;0SCo%)&0!za;kckj`4S{btSzE)MD^^;6mlDqD9wViok z9{W=9L3KlsmQ2Ld6ImfsCnfX#tXFuNvhLnN(Sv8QOT-iYvCfwA(DhuhyoI-q>2W;s z@i_)2D-XT*oZhT`&bW{5mVzgb{etdZk7;WPmT{PPaJesHv%IoHCGbTJ8%u_}kkQgJ zyOk!#MVUlh-22VsPVc*;;$NF3mpl;`yX(K=ieZ?{TAlKpbKlRE&=HINv&FY{k*Q5% zaCRLB&+k^<%Zu3``Gzf*PmpL4Uy>wyZN+KNyK7W6jxJ32_^H95?d^1xZwruENQN^<^9|`o07*%lRM7;KNUM&Gik1Z&Do5_#paWyTCV!8_Vo6-(`jL5)m44f zyVzLfnp~b!++lV;=8VPHXM2}z@;WD0@Y6u({K}l%zISPy5i^YpW|Wmb-Ey$MsP02^ z+0&CEaVg0Qm_ zqAdj`@>5MW+&?0@N4R3=-Ol^6e~U`~ybgR5p|pZwomPc*+v2mKZXSETg=FVN1jr_@ zu-!4CG=9+w+py!iUi@=2+g9zr!sfw0mE9j5)~imuqmu2wJeSpO#&^RT1v<}Je?IV< zY~Hul<6XE!jrFPZU6b}``56c)SiCHF*`|qoKIQTy?S-Qcd$bH|9@KfiQ#DD#dx%514f=AgBfs&Wek0hM_ z&ZzeKw;*Ab_~DaDDi*m~)lUm=lr2^>Q(U(B=Ui*?zsU*RjLAQ~h*|Bj59Ib`|8Ba} ze8E=5-3RrzZ!o-aACv+M8Frp~r5bB4K1pL)OgICpxn?Fl${|J$;coWezQCwrc( z6}K>(;&*X!URd8bnL^$i3peFSthGVjH6OR`(!H~WKdv??_|5(2=3DJpqI6!*XnlNg z*+Z{Msev)AT*U&LU3+=VQ|A_X?BaRZUDOpX5F{DsmA0bn*Sj}!ChcFooZb7g$z$#L zeRm>V3-yjh_P%zw)e&epMa=lho7InNcRvkNw~RE@S+sJ8=kB0`ec{V4y;&c)!J9+M zyroL|#O^&R^)<;W7C&TvxsZF~tA)bv-vv1xX>e7ZbSUDqZTCFgm6kFu%>vfMN~M1I zyx@fAi_IIIUkH4uzk6&;+bRR?ceg$)aos7lNPBDBX=$b0hm&tDoP6xR|7`Vj{>k5u ztSw4T*1ol0>@fet#1iR+2XooHyOVD&*tz;i^qK|BMK7GNkbbgkcDRgf7lVVO1J4on zGULgfWz!_SPYbO(I+Oo-=;o6zSI=MjA}PJR`uQZM@(HPI)y>AzTSH~ z^^1zZE$4f(vI|aCy+3Zw{xU!R{*vGJ^XDlBw`{%r@wPMP?u86*m{L5HEtC~qb)5nq z#WG&mb71Q2Znp9ROnSLS7j;EjEk5-u$YEylX1~o+)jq2;C*;;iBlC^LvipP@W7-oH zDr58Nf4{wcKli=Ft>|f@1*?sN4N@0}Ts!oBQI=5UJi#-;RkyZ1KE#=xINRjds$c&P zg^Qn?`Z-m`$Y*JP=KT%XQRX+c9Q(Y$@3&T{vsmP87QZ_SRc0?=B(vM9bL|V3b3BW> zGnsSk5_kk5bcx84V!{jh#;xthQ|3bLHaR%@vJO2WzjGFJ!Yi!+&c*&%~>~J(kkP zZXBJWT6WK0%(ZJb|AkE&*2&f;Va1bzr)8<^F;z3@+_Chv^2)&XPfQuK-JbG#Ohi<8qBctU!a=tl>v=XlHry7qT;@;Wik{f!AKsbIE`16s&Nlj=eWi>;#J_Kv*p^d8 z0t+;v)`zg}KbtMImOn$|fus7rxU2~qJ{2Oe0hzG&YvyFXR)deF|I0E!YbVE@uw_)&6DBqdb{Xcp}A5r z9pbl^JvZ8Ea9ed{yG2d%lc%c^!yhOvzt$3-;_xJKMwk%etlPq|r7ND5uGnYwZr9J{ z$JXs<^lv_(k$2+ojgxo(NIjE3)^PgbZo%UjAze}LI7AZPC9~ZQFkJ4b<#=&X3Tw5Z zVk)!1Qn43@W(J#RN-XVp<5#(9-SoYoS0{EKpTX1ByHR(-+!vSU=%2B9x}jj6ggDbH z-Y1LY7m2e~x^F29i9MQo)~@`^u8@~q(Z$00KR@J6z8BNIXnp$2hc9ndR(-hr@8HRw z4?hMPZ}{dSA88fr*VJczNL{%z8_*Y)P8Nr=WeC8e@E?uxm%v->qV|!Gjn~miuOil-i3eL zCQd4wxGq{j`I!F0?)b^URWmwXo3iVBBwu)>(=ISS?)C!avXwtm#Sf=!XKPnkdE;Rp z<8`FS|NADpamx6W5Gq zG}(EksdZYm>bc3qTcY-FaA~rY;hr_kWuAK>$MelR>m03vZ!Ns=?Fq}Z$kolqo@;U~ zFy(y8y+~_~LuRH}PHf2e8KSnUTi0d(eOwavXaB{@jaxqJT-sVvtv!d`{84;VXOb;1Ie!D!=}P{Z9XZ#&KFq4ZQY&~x+`VH+Kuye_G`)< zFL<)|=dI0>+gP3l9b|c-aAlhOjSTaE`wynvk~-8RT~PPv?7RZgv zop!1yn76C$@ynKzZfh zuI!CDej{DmO87=%g7sCC=K?JX-{M6j3YUg#{Mu+e&^+%G-ZzLzmmFwUuF_! ze{;VvCSMP8n#Edq_hj2=R@ZFD3tMk)WiWY_qp!6vq_mk`ZN_1(8D(OOa{FcWRX$ce zcWs-&Wvw&Da#kr@XW0Kb-X(H^y_%N3EVD?vJyia3{(HkK$K$N8 z)CWz!{%)Q8w9LPK+scZxZylZ@e^o_X^ekJ?k!JDxOI|uOywFk9H_1;pbogDkXL{q2 z#uC2GOv_9i59Y|Vp4sQ49%mVQHzf)(V zZLm(?qHVp7*(BrKtUvY11x)8HMP?|S7O3syyy4)I6Pxeyf6^`c%3U?V)8xfAFL-?- z(sJpO%F-(f6Rx_twe_31d?#&OM{Pyn?{r8Q08+Y8BV;1W0@MXr77c>6l-`VwWN3Nlf!}r#p zhPi=$(w45=JyMsKG0)!4`i$%N6C)k(zq-+Bn;VofN?prxP6|$Go166F<>qI(3pc6o zd=Yq(|1G=k>*2#Lns1gXPj=}#7U45LwIlCQSzM~7)j3P8{1?w%Yc`etGj2_olGnL) z#?E=2Yz;3WO9YD@+!k-!yi~NQaPNlr3tu~5K9(=I`CwOb1VLR{qSPrJF`hb*-O5f&ao3Hu<+Q@7`NN&q0+zqWfxy3yUm^Z^4zYJ zJt@B~PEBa$lM#BPw|v{1Bma%_d=qkgRGykj=}7kNIk&yzm?5XjobA>5_hjBLS7%@T z{Jmej&(su#;~YiF3!*)Dwv63Xwp%Jor>^Uh2(*=d zR{ZS-%ZFH-{ByhkQ>u8wEKy=b5!}1J4nI z!(R$|?D`sJBn25(n8jV-&%Hv|VXI|RX_5)hqR(Zc)z|}D2GRyx3rf2;0 ztA2jH9_uYyI*l>-%zxorhQ>oT6h55dd23#{gnfgC#-ZSY+cJzqWA!%wdpI>Eli@MP z0ZC({J=%<3$zR`wH*cB78+bfm!n}eu{T`X!3i9$RuQM6VK3-9=tA56V11l!moZl^_ zzy00!bKm-^-q*F%%U?>~)n++)=A>sofAqC26Xz9sc4pD+fS1#H4px0#$z(NeaY}#M ztaBDg#$0u;6{1@8b~hGo@?Nm49z{V-^CnS=riDwOVZdep~OU%T!(|?Rz0R0>f6CY%`ye`C?tz zzIQV&xqg4tI$xKe@Q+5d%;e?s87oe@y-Mg|-niHI$W6CJ3chDF9lk~!v(<5XWTtTa zQTtj4o;yB&);{Rfvgp;kJ=^@)ztiu3U+mr%9sWA!n#+v<-qTGhuS6}%Qp#9*nc>T< zdYLco4_;;QO@AOzyK_}y^S7`azD@0p!f%+7KuE4q;N zHM)1vD((5@hbR1xd2)!Cf49bk7XRoE-g9l1{S1v$xFmJp%{sn0x3gXM{+48loYhtC zc7;ncT_)DzMZnpldXJZRZ56pv2ecGQLd&m(D4h12EVyIyMvco}0Zy^cbr{z!f1%E| z{#kE>66>cu$9@RQ-c7skhQEEG;b-aN&%HuI3I*Sl{?%XA{_GNi1lJb%g9~SzIV|3K zMnwDl9*)SF%@wn5yjv`_AoI*G_pr*cNj6gAALVZz?-AOXtT^SW?4_HpBhEW*b!ZEX zoNcn{Y+m%q#ZkwDR!4t5=5mKCeFM`9iNKr_^M$om<|@^sOjj3=)jIR}!{*jx;hD|N zPRfc7M#XlDrwt8bF1S`K*e7)D`I8?m=>n0CD`Zbzf3f|gW^|{>p_r`7)Mfdle-by9 z7Mau?S@7**kN&1tiBr!%YkVwe`BlW1FM89}%q<<;lcLL%w=mznd_tkp$St)fI;JhN zw$*3ywTYH~Uel%;nf5cUJO4uN`cyB*%~wuZFPpZt-=_SyipZbuGP%O;8690o=JS%B z|J9lVHGbv4vHHlKhx;b0FX2C*GAB;r754cT}!=Ir@i8a3D4<+x;f$p z=K9aru9{h^%NAMKx==_|d3$7$K`V=6^s2Sjm(KfHQ6(937iXAwT*jTY)a@p*0NOfsR5&p z%0js;O(}JWeHpp)l#d3dT=suCapfBKolMs!wm1sDIGi|1a#H%iMh2Gh^c%9MNp4bmlyxahYIj3s-VLqW)A~}37rOaQZv66Lxw5mON1&-z zayJz$_e+%V%KWMLG5ky5CBeJPUQ6HFDSx@td)D!VOAbC{&3y7{r@rvI+n>YN-(Qp7 z{CV}H7aD@QH?4EL$;Ixl(d3U`l4+TSip-|7+^qUXW!{Ruo`03)iqi4YO5wBZ7f!ud z#dS37MiAGEH_08C!Mqn7_|V|8>62kFDPMV(yKFM;!b&yjEqqvAlkMn6&=FrLIpVzdAhQ`_i`$ z9=~RO{ATf~#~)1_w|{%MU;RqX4v+mmZ|(4tI=A?VT$1}VLzX>ljpb>!?UVYIdklB} zUMBgEYmU?HWs{n3H%qRa$mixA;P1uX5PUI|^^`cH>cX1Cc7obh+PO*&FnqguaDU&u zy_Ldw22X;DDvAV-ExfwxTBo=^=hK(JUd+4F`CjvAx;AGse{b71-s^2TC9$vj7S*oJ zohu)zXCV6h2;cmy;2BQWy|(UMIQirpjvcEXbL~D5#wxx|y0}^5gq*qHpKaSYD^5KW z{(@cz5}Rj!}5=Kijs8O4VQ6G!w1~ z&bhVrr)z=hnxifj&nB6~noB<_$o-XK=&iF~!Cm=qz%;4V=DfPrm5~iOy8|xk-8JZ) zue$E=gg1`om-}4al)55)?pB{e<~Lg2oSU`%T0-mDZ%&KJ+T=X+|kcHqq~ zwa(|ARKh;b%XVZu$+yGJJ$F%yr*P7d+%unKHac2~PB2`$$wi{xV!^+OJF8c2K9+iU z#WKH(drn(l-My*1TCDL@ME=vgi}zkD2w{(`ugPL!d8)B&@^7;%j)n#; z^O6_7FH@>eeEi_0asim5EEl-*k_(gKFmTK_ELxndQ zU%tq?cx;A|$c0?4_i_tSwJ!>w1=y$QX#qBJ@CpwvX`)I;kJ zOyXJfZ;sNg+}TCDZhl}qx?h}c>kD6`BG2ZnTzf?uJZ}_!J$l#a_U&TaC-Dd$?R>wyC4Det+ zc7HuXuVdWeO2Hh){fjG&s`owZRgBxx8@|Z+ft5w5Y=XgshHp))dVcavl~LL8y)Wy- zI=>B%WnXMIlz+>5*+|{Po7YLpSaHT7g+pc2B3?Q#d}unQTyp7Qt!<0sf2zq(T+oqc zvh4g%HA%r&uEB>}bsq0|6mn`g%hE;Z?~QUD8=^%f6*Zho(z<%ggRlR}?WuekGY`8+ zNuKDO*2m)J_crtB{1V$K;fqBrg&$`1pW4XWpDR{S{^&zL1LwDJMyAaxZi*FgJ=b|J z@FF)gmwTcc!|eyr;>Rs7Moh~%zSsBTO%5*m-oWYe&ITletJ`%=oo2x(#&d>kX6PRCiSUtCj<+Nmiea$344 z9(B;)DZgWx)v>Ivc{6L@dPPWmIIXdBLFUw_@l`Xuicf3_-MsP8j>zpNxtHhXy!o=^ z@S(6(tBZGYKG;~N^H+4k>~|Ik&HMCtcTcXl^mIMXZ2lybUpEqegs~h@4sb}>^yG!R z`iu1ntbR9&?U>xVcONh@O(^dUE&aH{@mf^2WaeHa_LG}-ar+)>JN$@E!!qaC$Ga-x z@+*HW%jKy2o>`+YGc<;`iF1{~G3j5|ZhvICD9E36>51I$PEAXLIg4s%af$qYDmlYd zCpb}hkNNgh_odV`3ax#fmm6Cxcq3Z=r(w;dufj&!#{_skc}5GJc(`PZr;W2wL}bof zbKTz?&$Xw|71I6WyF*aFdPlX}7UhE((Y&*MZ@WAt@yA2o5VLs-`wr{t`ZaAkwtM5T za?bsnasgM;E}koPdg(c3hw;6TodzDO5;{Kay?@5}4cDY)KC*e=53ygCIaT}3IL=4y zR8jQH{{IROCFa|9Kk#@OB`M%D-HmsSzslL4T9amM(s%2g#^IdF|6l#0|Mw@GX0`+e z#&YhQ=}{7T_G&to*KMj zYj?aEKGP>CW_y)d+l#qVCN{R4<@N5hJ)^*z?xJru%W`J>(>Lobzh$c^b3M0ywM}es z*K^i*%}TDdeD~|a7N*37Ct4dHD6E@4sqS#3Oz*V{l_NbHwpuJQMOPFo)i&KTU{LM8sWEx4-`eRfo?S_+k=?etFGjZ6 zNv)8pl7DJq!3Eb>58nLHu>J8?;qTXy_fMWYm~y9BL)uWvp=6r$*{Hdp2Q*wyD#ocS zd_1+x@Jca9*=pH*rrnoMmhZn%d?>(o<6buZ6uwy}g(FkHO#E<2N_*zHbLUO;|Gs5* z>)Ph@f96b=V<(mfGjy&rb8_oHd-srXgY3VOlA5XyS`6p+^;fW*cpUF^BT`)X^g_{# zdl~zR86^2Fmiim09B^Kc`Z(yh;9QFzZ=WRm|MNg}n;e7G4#O&z59RJ$e@@=J8Oc!d zVbjUX1I7z31cl~s&M`9NO8czvJksz|WsiXAYnyz{**`9$0HX>pcn2APHzZrtX-=~37ho%i`q*yoEG zm#Tg~7hV5p_3N+)|1?goIGrdM=vKMyX?-%+NmWjN9ra(fJd^gWm3f}Kit&tPyYXc9 z++F87maPa}_q_XK#LDTut1s&AwB9sR|7OsJ^4V9agXb?#?d3XjGKp)`G%j1y#4+$-r8#)M+>X6l|ZPnSAn+oE}IawuQkWGU(J-`V@qg}PV4^@c3LU1O z#ozz6h5tY7yYSSYb>fz<2?w5aR>z&&+&O8_jFi($#ry^0)Y914mSk3iJj=~pS!^mT z(i-w-`Ha(9r!-Gm$(;3%`n~YcoWqJ@GFqt)i9v23k~Mr}TXQ$1WX@ZoExNV;T9`>e z7*i7SO0Bc?v2z)IMqk`JFSl~F4V$c<>d8NcbZ-^aukvkObNcQxvkMy^I5Nx2moJ~N zulLiZX@9ju>a6wm_uV+Z^Fc}fFEi^yCl%e|6uj?z)jXp5EBpz6PZf`ccv}NzY)u&U=2JR~fxx%hIEivrEwC?X4&Kxmz zmW{mlq$B#;!r%pW-|u_9E#{B@?+e#FHgp)=xoI$C?Te{>T51*=Y@bpV$Vf31s+g6i zUOBjF)9!D2R$((|%ndEeoO<-f?8o!#tSo9n+@q}|FKnCnZKKCK1;-T{RRSNbJ)9c( z{aftbO?PfH&)yc~c|~Ghj)mxfU2eTIy3VPckq+`}x|OKy@c7=m%bqOjruJz2-!GbQ zH{#bHFV_EuZBF`qh|B!uzFC;Nw6JPF&)b?WzaFtht-Y{)ZtC_4tN6Cv>kiqp%s?>X z&Znl60p(HoqCfV&|C3^UW%5s%X;)eb&5A58{hb&TWuc-`|Jo{X-K}FwUvE@lxK_|( zChV6ZBptn2vPqn$#LsWtd%pAh8P{86rv?`v{THlP0m0+qi?HcDu1ft zYWptubZN(;{0g-N3FXd)e3nV6vWh`Q@|$(}8&9w-eNyYu5o8v7`&hkc?^B)wW@{q@ zc7N-NxxLAI0^{lF6N4`Iyx`b>fhmtwap}h_fqB+C@n60!c4snfKV5l@kK1hZ;k-wF zd;W--9=OGjIk}qU$FkxCf$dZN#7^U2Z!x|Nz$r=BNmefUelI2Ho<=rv z(&7_u3Ljp(sPbQ7(b;Db?3WkcIanW;x9FB+|FR!DgdZ1AaG#`Ncr1GJ*H}eN)4C?U_Yf3W)vS?cmM)t&7OEd+9FLqX&p6`^sn3bJN3;z$5|`3f4Qr>VfQAMUiIk<*83^<>hS*M%>0)jP<1MT zm20Mv>74g3rq7mic6#PjQ7?KgLgm5_Z~H8%Pg;4=OcQ?Vb-Gx{NEGlEaLm6N;>%ys zeRb1i?-j0v)oYrve#Mlmd34wN?Ywl~na@n#_I=N;(B7mNZSwWT|78<@X2>sVIelkd zu;h2aD=mSu6fddu3eDU#qv_6yTUR!%z4(y1>1ObmvIf79RoRUDde|niI=;Oj*15N> zQ^C(yvCeY|Xa6rFrz)|^SIWC3YM#3!9IvxlP;C(#6tv4}z9`dLuaZ+8%GxbX8(s*; zh_D1RvGlzYJ+|^z|FMsIU0x)9pZC^s@1)aezL!n5C{7h~$@h7$^<1#k>kMb%m(xl| zra1eC1tcvOn;}%oS$L)6$0v>E=_~8b?|8TJ%Hj(-XBK=s#Xlz{;9K!|)jvM!PeYZY z-!Uw0e8PYBn9svGM=}C?46X(`@8=O#Aodfr`mV&+vY)k|}7p56VXaA4no1+xcUePmzpB>vdNcU-4l zN{+Dj5`JGga;nWd_0#_nUYuY~s;X!c0iAax@ygff?C>l-*& z!x^o`w}0N+a5n$>#wS*#_UptyJk^@b9mv0VX2)*t(#y*(|KD^%j_vTLFPGQ$ad1e? z>qzVK7O^LuLY-MJyDZTn`jM||L&V*M~FmOVez*V=pezo&P)ZtoQR*C)bU zZm6(!@@}0I5nG>q$rfyvykvIA|L(R!Zyk-VGRyGcJ!&#L zcJbUy;l;0gY@?IbCiG}N4^gR|@}pQ$Ph`gRFOMeOseQ0B^V#9X%;2?eRa~dV_LoL( z6YBnRZ`DyA-4}Nbfv(2Qz*l{$1Yhra7#PnV;_?vanf-d-L`sJRfq^OeU9~IUaRxW>D5A`Ri6L@`Wy2 zXZ%f8-75U)$V!(3MT{?cEp|@YT_f{q^D4m?!C##3SM`Q%erqRC6&0|{=M2-T-m@i^ zwM&`PgD**cP3ZJ-6QN z{S#aJvf!3;rB<8Le~!CTZ$7F{InJ?uj`G)4thvXV@?Hzvj7gh3`^~zrnX@knF5ba3 z;r66;S(h&F?ek)@IN-9Y>Z!E>|M432CHdzxzw3oQoGJBKX6c?wU-!(hE%LIdu+b7|kq^fvvdb3AiGzH>33d%pM__bk3oj&gTC zXXGtB$#6XLdiDkWt@*Cm@xIT#GR{78rfrWDTk+g5g?rCrw&d`56&d{%jf#-EnRtlF zTcLL6ozTBO7nVOQD2dgbUv!(1`>N@;>_05!u3sb{+0A^J6E#cW)Z!UanYw2u{F&W1 zZO@_U>-^Osv-M-vel8NUx-+HN^Z9l;xeeF;Jb%5$`Ok$nsS68VDt)Nl8|FLb*S^9h zT;DF0)J<|=|NJYeP|8a7FlWf8b;xqwA-*LSG;G9;&g|c z)wPx%m!5Oaz9H#<`rzSyx7*zY-8?VF!)`uHcw#TQZq3A{v-?izOps@t^ENQD>*c-% z`{Zr;e{M?fMz6B1Np&e(d!%L4#Le5Tnw&{5i|v;5UGn^#%A^Dp9=`dfj#fw9Fzz_k zm{leot$~$V)bl+RYrj`4ZSwGX)#ZYR4ne**jZ_)H~(-&*wbxm z6h&W43Cx|ZX*Gj$le^!Sj9u6I#Xo=iw%sUiMzZ{}RSCut|Ct<4S0oyH&vIFIGKVTV~GN4AGrTj3qD^wgCXumkWWPsYv0F?gF7<+C@p)@ zno&`lS^aKn?9<2A=agn>7`6V;70uLDDCCPyJr$+3?9_?(EwdyywfL@`*prjPEg~_? zNG2$N@o>c6BB584ZXfW|`ncTiH`C72zrQ@TEn5{OaHQ@@%|(G17Kh3oFD>~#i^+ZN zp+bhALQA#6cb~3DBxX0r%_kP{1Yt!7A@{6C{x>=C8svvr%+4X5F+wZPBuBH0Qp&&p*%k`^C&(6gC zOzo>31T;Ds-0fv-0N zbJuc9nyL7DHK$wBjXNLCeDJ$`OzG%i=heA;OV@FnE195vlKtL|b&<|}6`9rYE+@RN zomjKh@UWSaQD*kGyWd-#L#OQtWv~?4nUb^R`tjwz*G3M#jT=l0)n6Zm&-y@cx2oREUJeWIM9qAy(otI7lQ<+WTVZcgXl zaP(W$?re^(Pxq}i-!nB!FY2A8B(SFZ+05_a^A6qJ9bSL-{{+b}w&(dpYu3bgU*Z0- zR_vzObN$KBCokU+^fmDG*=vVRKi98o{2twsZL+FK=6l47T`G1PG)|pNSsTikxhpSc zv7xWyw5^xKTtYRq*Mv-+S+PjvPO4Y3-WrBPg~#&$_N556?%^-FzGUK>f~}!T)-Mm- zdYDIL`@0`v{gXTE7x@Hn@8&(e>)NJG4^uvFw#^9iKXFVtwUDu0H%#GEOqjGl)#VMT zX>6s9hZH&vY3yU&eN<@8qx_)#OR}eVYp7k`v~7t$t9|VMy7KDc@6TUJ-u`kXXQRe5 z^`y@^lOE5yc*!pyhC4UFUtD-Wc(unbkwTOC-HSTX6pYv+-HuKA9<*9|NxP-g?w%+n zCUM`y>ETOE&dEHu(YQ086#1ka*^7+tt-ro zIn3X@=+l}g<}F;Ev|Q8Ox{pEW$IZe^;ae06`8j@kRg1Sc#4zK;AEu{XQlAD5I`N%nJlBIHff;4C8|XbQ{1KjU@~6m) zLP6OoeS_I<&(zLUA6((mX?&x5kz=3BKM$4}cU%w5m5CNuFk9=}(KkCSWi_1>I48bl zDQ>#O-F@+h^y`z8CsZo*df%50edyUJ+>_6myXM@?^u_|I2a;MFJe{_>3X3D)nWvhchrze`de7fH4)n?H> z8Qhb#?7tq(x{;8tZ2PVC_Nw1?-mfl3@7T~3Wt~~lx=V=FWa`F8`xcx#rrfZ8?tfv9 z8kwIm?`NNF+qzh;mPf0SalOc$JNxo~AL%*J`CLJB_TJoerN3E{Cm-@ZnSahfN<`^r|;=vConQu&qKHnR5g)U$DA6-M`Sj=4N57tRwOm$z$|f(^lC}H6 ztRSfvu7zd0AKd(N$KO44UAeU1OOe%o<6dNxEc>h2!`i%M&)(|aRi)L%U!R!@?7gz8 z=(MJx*Y(RWTkEw?))-5PUyd@LG2^ku%&=oiaz1$Yz0t4AKblkEn)CmhY3|DU{d1&? z{>$z6o^XHTxg`b3OZ=m@?Y2B#wP9KOwkhe}eDV9765hJ#ENW5kPJDcE?N;@3U*9{) zt}sly<6F6Z0b9*N9lz?Q|D5@sJYI3o^4jxlpJqMU;{VcUng6cC%IkN0eOi?9UC#ga z!=K0b_3bYGJjpcACZuS?yvo#{{vDalOEzB9xhnZ~edT|%#bW#ixFX-`DSo`0D)Gr( z!DQ7%Nxo^jyP0g?cI@DLYB-nO)vV!zIp3p=DNAOqdF<%cx8glp`6+2@UCz2a{J~3R zzfI<|+E{&r802-9P`*IY(8Oy8rB`#dc@@l5Pzi1_mQN z1_l|_VmrT}Br~rhwWy#dwIsC&RAfiJF221(@c+C0Ow-Tso@{$`qkQM=FymKOHqN~= z`N+wbDSqpvrZRU=INPNZI$_J&&oAoV>&dY=1o1E5Xn#UQuKTY1w^r8pN&558|F`@x z;ao)Arh}=6K55Ne-otQG9M;Vv7iS)Y?UA63MKo2D1~Tzb0df#|}@xR5Pg-Fs}Tf^z4)T%NVp z>$9|zw^5jglI_Nblb(66g~HEY%D%dW?d3iHM_+BDx2Lbw(3i~Lo_4f1%j z`0d}PS9d>j9Jue-dE|%nDd(Rmm3Qsmde{D)DgHlk^QoSx)#97mxkOIhV@z~uk|-pmL&J2kea%WKeu;pzWniMWzDbKyY*98g6hsDUU;%gW7C8wqDJzH ze>*GM=4)=N@$kNAG}p^3|8F7QaQJ9{qyg_1qUCQzka?qx6}kMB@kpZHEo&dl_F!p8bB-X~LV#^rN44 zS)^#*U6LSs%Q{bZ4-4Thv=V>P*&x4p`OBD2bl9^qPc zAY{XjIeL+25~Jk~@K|=I9W9!gcILa<|7Fege(e9PA2`RrY=_0rOhZj(|DJrhX2 zWV80s4gE+n{?a)P{6;GCp87lN*t9Rsb31G7vGz(v$v>e#!kWc|E}zR>(dj1tnyay? zS{huGX_**i`2tmi&D z#Os;(c;j69nZ+Liv`$hEsJ`T#vuUb;|R^I!fS5!~e zwto+cS3az2s(RaV_e-AADu#sAy|SiSEY{3caaK|y=N*+>SHHa2*R{T#FaLXHDYtOs zyXei%hdxD4YzR?azwTMHee)v6EaUR!T-w?~DS2{*Zo>8%zh)XJGVNn@pJV)I?}I%L zJlc1bSokVB^%n>*^m!8!i7ZVZtf}rm&dh(^m7JRK8tt zu#6|8vuj<^>KB`TJb&!Nen~-yd6!e(LmA76Z7#8!JPhnNaei;QXyR1H(y8uwTEuMr zGvjB>yAuz;mE7=CO*^eyyMp7+)q=U(mIwE_Ce2`Km%4Mp$-}45=)})YOc@87ULDQv zxAc&@%;r5Y@~mQpsf+jY=jWb1m~V1-R`H2Tk7HKJ?D^K8ETOLu*ul82D>3Q);TvL) ztIPXJcWw}1Y-1^LN-RA6hsrMHMZ(rl{Kf$yAsz#R6^q9g1o)%UMjz~R)*O4c$d!L;0 z%By0MTbktRJI7X6Ri53EkhVH!;)&+6^=n>s>g4UtnJo7(p}x^&fv?EC)~SbfRctym zUn+Oy9JynR%b0C>RPuH;*6(NGBimfDgS zBC>q2#YbY8WX)f##+45AgHD+?_9eJPZstbF$e|=N5g}7~#IFk!FTbgm<-qtwv`BWtt2GNkqyLsIFx3;|Da+xTFhRUoT*7C9h3d|LXJQ`9 zm;^oL9!n*yoW5B)YH7KTuT~&u%l)p>YZ0~~Gw#Z}@88qsXCPW;d?hZ1UEx~dg*egL zs9$pnj&AuP{gI_eY|)e#E#Em8IF)>0o%`T-L)RLap4Xb~dx}o3D!Im-zfJZ|&)M6n z^W}9`RqeG_+@-kssZjexj-YQUA34qn&rXeO<-B!vlh|jL;2%@cv)?MGx%Q=Y#jMl0 zl+9uGpVI4@9f&tL_dotui)g&JiR-~6LhBSPL*D6p%?mQe<_zw z|FJ!3dEv!#4YTg&|LEVfPIT6;(2P^Q3)cm-?ph-40Dd;PkUf|hV(OiwoSj{Rj%$gp2eQn6L zJ}+M9%T5_vrn`!6JLBuy8fO>qJACqUZdX3rz&G=`c)tJsI*H@^9_V1|@m$HjX z7TB7><=X$cW}^7_ol6-T*Vg~yI-4)zf8c_JeDuV(5zC!t?O~b}#pARsue_>8@WQ2M z`g2}>yt1ugd6MkZo=-Q|u3ye_D{!|+>!SB1!4^AN$|vlu(@GXS;%wkE_mp#lS4QP) zD`uV$<>LL44^yLbc#jl_ZQQN2J9(dHoLlr{NrBC=y1kc#T-d^E*rS_R3{Tmf6z2A* z*AZ0o)%bLxpZCtG*DqvOq{zmyn!NNr<91@hg1Pfz&F8;3nYJPIf|%>=Gs`*Fq#gP2 zanhX6NvcJ@lULm_e$Q8H5|)0{Z|a3azqvb$X3X%{X)9pae*V!r!9Cw)cyH*Z=$tvZ zxM<;$r;}yG7r&PG!Xbm1Q9XpWWzM}DCyp$$V@+S8TJ*(P)&1Prj_7G; zyJmX?PjXFP%F#b-p>MNNHN*F3FShtbEHU1+X}h)2$~krJ4MEda-oCTzqt-d!&U0xE z&lk?SsjxCCX1Q`~@^8PZpPZjbg;prMwb_3tOGvGG*{MTs6pOaWy!ifR?LALTU7g>{ znIG-qTC)2=G4tF*MczHPg_f5c{n?|pUf4^n?NI$Tu0H-(ANrU5TCk?Md+(QL62JHK zT4Zm18j#){&E>YbI_1XaTRd57%MUMlKi5V5(TC?(E7%vUZ1Hn@$9i`|=9ZIteX7|? zH|gE@{%Z1u^1>A+N~%$9WXFK<&;y0^FR+&1-?5PDAQN>b02|F%(!ivItlT;-c1K( z^PJzrPP}iZ$8URepNot=!=2o85t*({?OhR{v^eiM0h+ z+YitFJ8LG&H99!iH(YtgaAR4uiGQl>Dz);C*|$pf1iyP;ZL#q{ipAuY+jx%uJMu{K zv&>i4E5*O`CQjQn`%Ixv8hh8Qx6jweI9o1wSGuCCU6AKVXs2q%{n-_^se8{YU$9Tz zFd*l=@6TX*E2w;1`?l!G ziHSGr?Ob(_3%$oi#Og z--*q?0%X=t`WagGq2yP^`-v4{Yt2gQDzA1e@1I{&{o%{U%l8lM-n91f&0T9>xow%6 zvEHbV?SK7L(@@Q@(yz~R?pc-o|Nj5TtN$HW|2yu{xV@&K|Ldw%lg-wj+Pl{^R@YTV z|JvJK=N?5o@&0kGYl+V4X?@txdCrqV)bcm)TD!_=`iywh z%ALy{u1#OPB+uxp4ZFZ=yngTcXh|;SawzYF$bROUY03SkAfEd$a2%y;~Kb>D_;tfAd}R`h2T)=Jd}= zbJH5Xcdz;^6u`0fv|Y;QcfYgdiNx+Wz2wcF?6k@UPnXRMf9kYfeVN5igY#h)IoEc- zzdrrmYo+`KcJsoZ>nGLrd3Kemoo04^miq0@DyFizZ+cZLMI)!XmU156Adr4}bA4sC zkp1tEPj1S`@A-FFF?87`8;ink3t7vy-+ZpxR}kcLguk-$|D4>gn}rkAdHbxy!I_WblzCQ*#DM}t42H98Ve?!`_mf^{p9(Pr8H|>+p=Xh z&RPED{lD{oxqhS6k7tjX6y&DQHg{QF*AT*2fAP24>iz5=B7=&SW*pL=A9_0S>Aqiz z-UZiIPS+EdvwC{u+iMoN!T()(uh4kD?z)Y|aLN^-Q~oCy zeRr&P(UR!tOfV{Y@ipu2BbDFD+fJ%%^LQlivEz&2^u|XG^POLF%Uz5Rw}@Wf#V1fz zS!eb%$g}X=;TJ`Ey}HfHmdbrgO@2K-f7WC3ZI+a**&^=tDz1}z`M;ZJ3QwwO=WyNc zy?NQ!`xjhF;?Hq1E>XRhng9OhN&e&SP2S(s@ZGbK}f+N7H>cUmW-MwC#GdIb+XubMI%@BWFIk z({}!O;LPdueiPX9r2gsa@brI;_x~Dy|JD9~j{m<}|GjR%YX7{TUjENoXFv3B(lUOq z?DU#FnOrk&?TU>2mXyEkT(`)P#-CfX5^4{e$~23Pe`T*FaWeJab)BQ;rEJA37phIO z^q8>!()`b*m&3dnxLmBAy$=3Qa6Gv=z=AbUlx z`~E=h-p`9N19N6g-}7n3@?)-(I%fO|JtMW`p2~tXN4Tez^uE*IysR!{&O*%te-_An z*%jH`@+toOs&8I(r+v92W5Z3?`2A{4^Ko`@*r)Nx)XUkoJtcT;^j8&0!;LK3yfIf`4h%O6jF z@896N_&<}zmPh;7ea`w?|1W%tc=(ZP*KexLTRf?1#gXmmbJg4a>rc4N_)d;n|8Lx> ztAGFgE#DEw%w$pSbx@Zh!^wBQw8W*XyDRiF{gP%Jt32h9@viw^_2gy?vCasQo9#7i z8Edp&97u{?;kn-@jLUCkLzTwX$*XnJBYU~Lcu%)8BFpNQMuDsKd76`J=3t)qmJQ7iS7AiU1CLR zyJs)Tk`6f1e?-H{CGeIciwDoAQ*HIf!aa*yH_5$D%G~@d!h<`%P~_3X3f@OjOYf!z zo(=eYYTD|u=BXRCy)VRF*cZW>C8e_wUEEFF87V`!?SUYHYaZu$XIlYnEe=M)g9m z1c}?Prtxf0X;r^?%|SmvypF*R0+J|RSo%0Xp zSk(Pa%38emKU=6YkFnaE%cU2(ua|O)M5lhuu-SUT?$|@NWn!<3rk-EVosq0`sq2Bj zrVrQG29`$6Ir?$o`cg&vSr2Du82NKd-MBI~zp3KqqsMb*@4S#|Y~;aWQrXYywpZPGj?K%&$VYxp z3Z1*%A3f)3<12N!7CFIqqvP3GZVMSzKW>W>*WA;P$o5!PqLi`8^W!GgFWm8VM<1hX-fZPjb&^!$=~#_O`}{NT@S7c-Vb9(%j&xIX8$ zXS3EVcKzaXD#Y=%(ibC7(VG_zvD~_Q^7!uzn}+6;_HFO-uWjpFm^x=mM0cpigu6i= zPTfClPHAKM&`gmMuvl21_m{;XB6so7<4$YvSK=wx2Kl296YU-Ch-c3R>p4CpQc@Q zCS~R7z`wa0|Gt{MfmQfzcLI0o-i>be%6{H=o>cuX%;9I_)Hzm>j2E1jvLD z@84vOU#)HX=PyXvz$3VRx=Q+2D~Fp7##}-9!SlO6%|CK_dVJq>`8&@UmEO60JnwSh zyLDzf`g)YTVnYTkY8 zNz}b@+U+Cffa;s;ebS50h5ePC7Aod;Qs(Z_sXM8XZ01c}?y50)yh;m)3uMU39rHSJ!;agTG#9 ze)pKTemR$!Fio48t>v%s=?$BmZaH7u(rUNi>(%!L8$!)B{)rru`8RV?bt%iTsVlW& z*|vm>7D%x$OgzpZ&y(BGu`%iSg#H!sfjbj>9_&k1nXjd@{NfF9_ZTTr`+1y!<^T8> zItiZ^w)6F9lSnCBIyv^JM?>i8_xofwm~Cjdz$M3dPn~c)z;)O0F|yRhzr;_l{SyH=PSg^JXhbiB&b1teTnTAvwc5xAFI! zo4#Sjrd=#+E^BX!pBmg9$9OYul9In}hNtp#vtO2{SJ^N)?mWfaaOBdOk25cPnNw@c zef+p{x4GX2yX^h#J2PyJ{o`-0NjR#+4K8;A5{4LG;aDIwqSp& zMVEQPr1+lB{HDBO`s>yWVecH=cN-gIKH9Y^YI9E8-KXbQeNxl@%u~BX$W(^&!8?J; zE{|89<=Ivztn+q(^QF(1KTeFbfF-O?}Hr=*5G|NQv}kJfI*Jgv5)ccpwbr^P+jHeb@};$4%`{>V3eO6Bk1AmtRT z%_lc2sopog#JlU!@l75dw3uFB=stF7p`Onhm9?jm7B}A1)!(pmqDZxo-0^GA!VNY* zIoG~LUi9C`$OHUd%BK!}_OO4VxhwSL+m7u1wW_U)E+u>^+`RPcv+z{A<+`!Aem~}z zc*NBuf@^X&7rD%R<1KW z{N|roOnfJd&YZrw>+LFmi~}c6t-Uj;PUYKkiCHt2{$>)J`Bx~UeRfWK!@1UtGlQeg zt6VUe5*Rk^qj2%9aF^yur%o(rtd;puyE`=G|FT(Z_j-ygZoOeSn*Pu3g7pTqPfKsH zR`D%*$F^*~Q|pmLu3ER!WleQgDrZ&ZTHbc#T@z@=wqR2Ego1F^2`l!!*zoLl*u_Os z5(y%oHk=OI=J9WG(cUvzA}g**CH!+?G&W;2JHA2X@}q>c5@*ae-0AW;{HQQvnHAsB z?x-n6tHYg+ce5xzHgh*owbQmpW_XTp=qvp>$*DafmHDS&fjrTWD1I|=DB%r+6z zr$0?dz5ej;$GeklbgY%#I`r1rUHLMjW%HBEH@Is4+huN&JGkx4=?MN^)07l4BTOUI z4F@L)|u_$`=sd&@4Gd)}C?QA5zy*JGXz1ey;T4Lk71wDUeH+cR`SNP>o z_u~$C$K_W?otJj5oyd~v87`tx`XI;3f7Y}|A5)(&einPp>|E&e_g6uM)W4LoJJ!Fd zj?(=tCK(mr#TDFIX~x@I+p*qb^W*LNzjxV698F~D=1F19&{RG5rDkWGR^y{>Rq`&j zN4AN*x?g$J&iK{oE6ThDx{aCP^O zkfmQ(id0lHCm5NWn>O>WwT4wk&^eBEdq1RnyKdwvec8QG&R;_*Z0F)_e$yB|uD-APZY_SR?0swggfBZZt7(JyE}B}H2j^! zUB03$Iy-L4o5Kem+x>m?s*kC^e1^rfjh8oE{9f6UEa$pYKPURd+#Pe~<{17ts$1Qo zoXTpcs_|_ZcecyBovwkZZ!0*|0+Oa(wL5X4Z`Zm*hu?%v+}&NYabxJs&#qhQ**7Ot z?w7x4@aVqm_oEfo?kB$H{O5D4ZMl7{gU_xv_MJhtgqF>xJ6)G9EcV-OwclgU+F5O4 zc9GoYjk^LDHMt#Yf5i7?+DZ}S+lr6;`M3oya_yVDV@mL3;rdwlHA~a{U%Z$dkry_* zPvXW^!6eyf+unLVesSq?VurVi=dK?)Nk30K;mkPQ^f^~VCnY<2OQ-9v#(V6BW#(^9 zkG!mUT7PBp)V{0BAMVVOy}pfiNkiLAd@gpvJ?84uf{!KH z%tCBVpD*3(b^ZBujwi;EHK#ZNcw~6CZvOl7-tM*gj;%d&)w19IQ^Y#ueEZXU{`aLs zPrP2RT)4O6*}ikPo^~y~dtKjZf%4*JtJh*30xyMb6z-LL^iV-{%J$feOO@6eti#mUC3ptiHcvN`#rp@A>N+XBD5G>sQ_4QR&M4zj+v)WxB;!<2k?_ZoUpFSdp3$4UO6tHxh9hToTvBprpLC_K zeZNVyU5Cw+TM`nL&uVWz?y;0;i#KI|yzlNmF3VkeBi}x1Wk-E?+U&?zRP`EQRd#Aj!i=0nx_IMKI{D_{aJRwR^csgKdDW- zT6XV`!ex)GPaT5R+VM;|cD1oJ!k&lktMD)Jx(&0Gz&Fr+-#d~31-A{5e9(<~Q4}u) zNtGS-*_{zwJ!kCJ7}R{Y-qO6Ge(qnbxq3mXkI&<}w!*MvN)wCWoVG*37KP$-|1a&m zcRMB7iFx;_uWQ(sD*9Sq2*Q?pL1X>cx&eR!`dl z4f}GPj@c|KKKSXz=kTv5J|Db!p}73a<+z_-lkAdw*1TwBYtxQ@Qe0$q%y;&s4QIo( ze?|M9(7bx!;GqicgPcVPE}S=B-YgF4wV2GsY8N)ctB)ZtPx*|=@yS&auk%K4`P8GY zvubnijP==5WcIL}2#DWl*ezF zWo5gS8JRPfhDy$9VY>%f!G>@4lQ} zJ-J}x%EQ5Lbj!^A4fjOL2_8RVGb8kP%cmL@19bvd+8 z4LzqCc4kHTk58}HD~FvCyC}PF?{cGMfqS>=Xs%N5D`92Y=BHO!%(8>um+Kz$g_pb4 zvRbW!LOwAsxA0pNm^Q1Xge|ivrSPxl7p-lNo!+i*44ld&%$Cu@yg;PFw&%q0PtAu8 zN*Ai`+;4YDHK&;=u;fapi-6q=>nJaEqvvWCbtG;!y z)A^2J?D`8VayLD8+$yjgT=IxKOW+YGBFr}|k_N%qbziaB=D_7C|bMaD6@bR<|o#!(a3%&6Bv2QvD%i6e|mO2mb`uFl*6IxKt zr*!VxMlRN;sX_+cr+UBromHnO@@7h+^>OVBiZ0&`)|zrQr&$O(+~QnYYxJCb=Hj03 z7PbdZaoDqn8z^i%5?CSCXxqN{wq=caHt)PoZ!P?rmc3W1%H4VWslr{!ocEpA`qnkE zzW-uWn*U{L{3(x`yO&S(Z|J)FfHU3h@8XqhLNDiasDvtn_%s~pz2@_7$L86wP(Cxe(>FPYkK_3IWt3+Z{R%jtL>Gt z+;r#N4gB7XTN$#XCc6l>O!B$J#rry%_0}`NStYUQt%ZR%WbPQR78S2yVQ<*5eCe%6 zEEnB*x|M5AUU=QIw&jBJ#EJ{Po?dq=kA|Y zZeS0reVl%Ycg@TnfgIo7EfPNvWFZ&i`0`d@x_mw$Y1o_~MtpAV1x zKhEBsu77Wjo$c{=@pFpj-}JvY+x+d~36B&fI%=PhH?dgkHBao8l}yAgyDN*WY}e^u zzhN*pnZ2@Xs_25d>%_Az*gSpzYuffhYrgC;>P{(mlabQ&`&O*?eW?wW2cF!T7nrXd zSzJ9k@0j(0P~OXIi*uV-Xjq-n%?-P5+M#`UMJa>#dDET=roG7zU%ZQ3(II%JT6Xr? z>#H+;HQioL`I=I}Cupeg_@c!HZ?zwlM2)xhmRKAKkIeMBz~C$>r$OX3vOHr-4TAK;nk|&%oPoq2l`+>5}&P%MSaynS;0dOglf#H0a0z zT?RLUXF1#b)pk|xER{+w?%DNdNmToyM^iZk4KgoUFE=jvmmHvO!LP3w>$Jb@@~8e| z*Z#SQ&p73yvuHB^%{iC*xhp15JU{Q;*5|1v98S+;#4Z+}d9mk&t-!sx4ABOgip{!T z9M^IDAExgUT%{c`^M~ZgZ47tW*8Vhb(b~Z}$4f?c(eyddmK@#QxvZ(1{FE)KvhFxa z$T3=bZ*|n*=Fj-9*P8odX3uqoIdU1(HF!cr@3F4%Y}94E+^zTF1zWK)Ph;@UZLVLm zG_QMJwBHj`#9h`qQ*8Ncw!{6~1cJkEuwC>1qr2#FRo&F>40*BX>hiN=Hm0b)L)txkN%T> zeMd=NfVoj)`;8W@9qY}+ZGYVqtW(>#t0P5c)7!_B#Thiz=a)JjaFIWr_IkyB&f|VB zQ$nxH`#JuN=~V3r>`V77ZTrp~S8>olDThymFUEo2@J3a`dTznei7s=39W}}Y?0?Ni`VCvJ4;P1Hd>@PT}aL3)9%H3HU+lv zllsohW#4t=fx*m{?*;)^O1oYgCoj!&xsaS1Kkq?fS^u}0r$zj{=Dqr%GmCdiyos9n z%mb!OTTjl(W4mee=C0144FRXtsC=3lAe+SbuwwGp)gG$nj_)}4O>5T+|22y?@t%nN zB3tlzJrl=bkIg%xLl)lXK3|YualgJIWsNuIk$>mIC3&0gWiNce{Nu#Tm*Spr6Vf=X zjL!W%@Z*N#$A6`4%U*wJx|6>;bfe&kl+>?A`9F;0CYWwgi<`^H#r0^J@+$f817H6Y zJ(v&|J7d9(2=2_peLBVa&Ky{@piWfR;q&VYXET|;W)*WYt49W%ce*!AVbO8En%d1A zu?0?>A9DU{f2XsM+izBG^V8Tb3QE}r4it$0VeHQixK%b&batfk-rgr4uRol2=B=QD z`A7be4=byg9{49n?-FFRaP)Y`S>VjB5$mX^p_8q8;L~xF6Bd7p_EZ-Xd+4uUCvdU) zPjjn@U08RkbDo?;U3RL|yGu80*FL$^wosaJ?W?F0<@@K(I4tGv-yZOF-9>?m##5QR z-X?sCWpg`XSGeng#R26PO?NN6<&)Q4z9%s7dsW8^p5i9WJ5sm)AMo#$ns$=;$^24# zjt!bN9A6jY6z*RBEH)(URPZVPjCW$8PRTyyJv-(nMobT#UFx<~`m5BQg)T1k527E> zb#=TG7us8UluhVC&FL#srcY~lW|{w4`pUMuTjlpXD-B)bn0w--w#7BIqqkpsU*lv+ zSCA6nUiD_nxq$!brBa0pO>8>4^$va#X}_ks>e%ID3^#a|r7ft;KelGYgOY8T#xL!@ zxlFFn-oa9MFu7Vt)@J66VDH1hUfY(2iFtmWuW86wzghFFI9ovA4`FYsa+%h;HucP} zTUK50b<%y6xS%@maN@Mj2COUZOU?eaGp;Kmne)qLX3;%H2V)z>uFC)IV$a@n*Do}; z(QjXJ^o$Ab6yN@oInG-&rHIj6{$081>O0aEzw+Xvlqz4TUO!;%dZ+bqQckqjC67aE z8lJ@+7rgtxg6l`;vWj_2C41GCQnFUAuh?)nTcNJ{lU`lvpEt+F*Zc3=viamhkGZoa zUO4Fx==n{O{m+rv+5!I@&L!`8uI_!{6z|Dp4#ADLryc6uxO>{|#a<7Womf(<;y1Ch zWGnOOb7`ID3W{#ePm>JEdETJd*_IZ^w5O%2yE5iw?9y*vHG(xKd}){Wx7@KhN{{i{ zE>FG9FYjGoYvn86=ysXEhLd+i?zh7m9=+H5m3H^a#f2UFPTs04vH!Sw$2svesi8X; z+D!Ygxcq)V;#XI@Qpc)wyfz=7H2h&xv*AmX_xN>>_4ZW1l4+~9&zY#RNv7xD>dWcx z&lya9va&PL>8jP!|DWvF?4)itqnZ zuTNZyyrWW~D>$3y{D&!Pssg2-*3D+)RNlmPb7z2x<&T{+WMrmXW!!t#`@xahtJiFK zvm^F~VdjSv+iEtRD@!aNT(@p`^L@*~o}FvX^sLMqmbP`( zE6?0^DWi|`s>6%ub1GlC-rLNMzw9~pZP}9hlPg0*wwCZEl)sSty)iHRkC|rPWZ%an zPAfZp1&1uEHG5(GL0WHX{u@jA<_QzqSo(q$Z~9&eTmMz{lsNkz9qRm=WH z=As_+%QC0S38AJ-_&81%?o-~qw&-+yM$K>MgmQzx^p86ESJ$q)wZhSsVRO)Or#ew( zlVt`AKg_$1gum-kyB@yno#Mi*wgqx?iHa=f4XH{HSy7N16GZE3vyHR5wVK*PmM&>K9Ue@5tv} zSx+RN?UTscS-sBG<9XDJ6BqTA^ebk)^8W6abI|gvj_Z`q>$cUU?C`PNQ?0wN=MKx; z)wkasbO`%+wfA<*XU{{$_j=i@g^pQA#y{fPYr0_$*J{B7w|Sn<@B7Sngn1RiD#sp$ z;QGlI7koN;#;8{HLHxy&oK1{UXNyc6bt}&O>oQ2|YVw@!|3mVYx)b}BiK|i%dh5uZ zwCJ)~R4AV`zgI`sT!yJM@u>JkLDLypr8);^Di<>LeVz5?K}yLf1HT_7cJ6WU>96LP z-)+4;wO_sP?WgRNpGAK14l&r)NV!*9W9-M3fm3v+i^v5wVMHnZ~YvmUKQ z?0>xC;w%H01%rQfJjt}#S0MWN(KYE?why(VKK5Lx>B~GC^4!4cUX%OD;$6)t?h zF2LZ(jjG%CJbNzM>weuUb^DW<@h6v-eVNaFm>102lX@$%%1QX7UIE`!y**#{ml^si z1Xx)41`Ee$PR^M&w7e5y*(Rh3&0A9SoTHDudH0ivPwto$TwG~W@q5!RTcPEAvLEIsod|y= zB!BLzdE56Z;ZrYbC39U|=u-T3cj=Su>`bkby34Ir@2tJc#msrXS?Omazsqug6CYZa z%-^Lc8k{11w7Fm5bWO#T=Xu^mv6HgJADA*%=OoK-dU5Tf<<1+swv1?Xt+{LtSzw`7O27*A}?D++jR__jh^H*GE@u zUu~0aJI7M=W&Xh=Q9xRH`)pO-?a>Tec!Suwc38 zy@h4_W&A_zG~*X#&pP$Sx`grl&T^#%>8A2l-2O$k7_O`4Ke4glTA$T2Z?n__&yB~< z8ct>OaQZMUC+=_gu3~|$E>;mu7oYh>vk8b@Sk5{%^qS-v#XEnJXG)#1Y~KH3MUrx4 z^`nEl>&~Z1{M_O8{*+CH?_cLHhS!5$?k>JNf8{?NS^f@|ldgBOO__Bq!XvT{U5#uG zSDDGkX*|0}Zm>PcX_7avyeK$1@ zK0C11D{3!aruOVuH7~D+S~C+oBI7Qe^tI9Se{S|J#JTT7(!Xz>!p}F!im~~$d}Pq| zZJp3?Yi`fMoaTVyq@{aqOj<7O<^MZk5C5Lfhsr&ZY^TrKR@&6m8fBpMsrO=EEpuw< z+Nv!n;SRez9(a5Ezm2>3%;|RO6;Ti4FJ-wgP5Ero|IO}WEm-FeccZPJU03Hz={l!m zDW4Tx$0wX>e(IP1^y=hFoqu_oWq&VAdmu7neV%krliW)cf%tBXzOvJL@4XpISStTn z&-&aOu}-=t?nrdTSJ}QRQtqNYM^ALH7GCbK4t-!Jmp@gA`Ov}5&;HGuR93#baJl6E z+Z|h?cKaB5*)3`CR-aa)G4b8a7qcgtTx@UkJYnDa_(|kbdDSAj^Gizp{G7!9W3Juv zKjm|#EacS8KXxxu%gtV9zU+RHo#wx0+|CF)xFRm>>Y4L4&sas!A!ml4T|78-7&mJ-G78yAKk1S92C0Vt-?;J-7MkT@{U2X6b>9 z?&@!^n*83tl$3nxzhM3AfS9RAYM;A_-pX8e`dG?3fx1K1b{^L@Iln0ezQ^&GzQg!_De*r3HTmHymbkHZ9J1zxn8EE#vL$eScT}d#K+$+v{bk z{Y+UGE(VnxNj}ez$U^n7OE2&2%)0lGxpJd|o0gf1MbXvsou#K2me-2kR*Tquj%(NJ z3FaL=cAwl-^REOHTxTs`yyo_H%XqG|tw)&|UtB#JqnxSg@X7h}^t8wz_pIYB@1&Y9 zb@Pf{*zjUXfBmg%j{aY{zPB6L)YVqDdK8{>R=$1Xq=r7<;SR?cb6!t<%doY5>0{%t zH2olU{$=}HS>NdXxjT6s=k)^@WOZz(FY}Do(KU2CB5|rftI)?r>v+YH_j6lLWCxM%K~SsmdA&Mn#TAYuEAv(uCUH5Twr&P-k>GvV^) zSIc()&Wc_Az|7J5t@*)RaA?&$wvyE=ya1Bl@j)p{&`d(4G^rJ7@IBO5SY!et*XMGrUH&OP?wPsVW~|btvbv zg2Z&jkQX-`g!j3Gqm8BZcha^cDCVxp$jZo_=yEW&1s29l62>h-d< z)8_kbT^aW5!BH2rTNBpY=M}NP9uUhpvFuw=M$?H8A2!}k@XwT~o*mK2>*0)nRt+`+@6@AJ?vpTev&! zvFNMz%z(TloTs)&&pzq4Zlzw&I^|VPi8qB8ZWF#L^KL=0gjR-4_fyvulU(PWK7DZx z&*S&I^_kx+nx^z!shG_}JnQj-lkToUr*58jU($OqHSp{tr-@ZfyYFTRFZq1(u8idB zgG(Q)%#i%O^PK;Uo_W>b`FqO)tK}Wu?(LT9QhoYsTBC-Z+q|NcV!`<(Z)RpTO7m}h zEWhK&8s3TOm$Fv(KbY&VaV~Swmzu9hxdk&Hv>o1+wPVsM;WdkX2hKTlwNCS-XC0F=E|zfs3muqVQ^-LZ-mXbwS76{0g2<=XRcWtS?+DwSB+pV<{0M z#ZOyLa`j|>2;cJ2;Eip_O)e|GAcK8>H!$s#`L^nZ(Z0pnEjJY=)dtx4il5hen0q2l zj&-lW=?9-=CAhSuAY^xH=_rtmC&7JQ;P{e&z>1)tChWkxTb-@5FTZ_*C2O|`qlbLD8KB`+S+u_t#jS2U7Zz9#r*l4U(?RS z?Nj}C;fme!dVlz|bN=$H*}?IO`(eOsnNL^WiD<5TBr$clai-Djvp3)C+%er-Zoqrw zr^B6Vt+N-+Q9IqY@7Vk6)3vfJi>-Z#U# zr-$45Cq4ZpeM{oJP~{|+Y6X3!qq z0k7aGrh48BcM`rVI4W<&f5X%5aJwmw;TwaNc~OTeybedDzvd5ERqe1QTxjRyiu(Hh zk)iieQvJkld$Juo>$ht~`=R6?8&=CNeIBz$cE;^_dx{FrzKWQaQ24Qx{e55ChG^Gm zuOmgy1WJCFdD)-ucmCM6GTBofMPs%xJ!|~-sv>c#kZE~AN`jx)&cEfd(zipoJa(`A zvHfvp^t-|(A>Zd2RNcP1B<|H)PvO$`NV|_(Q$GGo-t3nsb%4qEpqZqHOkc>tHEt3+ zJSVzqIrGn}T((A@bMB$ZT~ivJcTVbVddy?+(I+&%+kWvBrv)?Izl5pv1&ixV)>^;L zY}ZUEs;}sGD^`Et$du-Zh;3lYgCMa{|@%9R?309RFPkgty zzyJ66@^SyNh5Hz9pIyDdcy?r*!%6RZ-#&@Yc(JMVVduTW)0ezGz49U3_FaKJ^S=F1 ztxDXoW1-%TjcW^^JocFL!S&*kIrkZT%1C652Q@m#5_JFx27nx&iBmcCFsxIy;3`=%vH`;;S2&FlFq^-c1aW^U?- zE~})Byvit_XceXq?&<>{zfZ7XWjy6GSu~Ho*C+d5?xKUn8y+(7PM>w#v0WxR>WBur zQ7iK-Z643QB@%y5Y{`D%mgsza8MEN2(u~aBSaX&o)9pk0WjgK(Z=b(`JtfU{&%ug` z%RlJ5J(EzCz2CNM@9bMA1;UsLifse0MM+L~Vu%f6y|uuC^xGnrHV5FUv~qx9duEH@zr5Y@eR0`@18`U`0>f z(E@YP*qaM8e4?bj8azoVzg~0f#R>bUi;fn>?N9%lbgsG9voq;QfkzLY$CE?O?W=dX z%#mYz`S#PCMVy7xRlFSuav>dOMX!~TBAhM-5QQ>xh ze^l_WDoDs8X31s%_OU;ZDd-pLy)dioFX>c1gr-nxOkg zz_KuU>fOJ5Z5Q1iv_|?d&er^wE|QgT^NLlO zvajDVQHJG?1?T(4SEau*>vb}lEdRV});zTtclb|F{B0roAZ$smZ1y|R%YkVUiDfHx zdHi0X)h<0(Z^zWzYfoi<_P@ydqS)AD!R)5H!0pIWT)!$A4_q-3Kb9@n%kH2eOzvGTU^_XW-MeTUwkzpt-!Xya*lp(hXC z?r1N&eRy?o&F2FGoY~i+jE(o*6#Dp5S*HBa(Q0Q#nQ0+cxKD^#Ukl=0S?K%X&+0Qa zxmzD;_nYi~4XFNCW5K?a*L*yIQRb9k){7& zWxnO?l3!Mwk5;f|dnCS*U+Vw$(wiULJ=gEAcslQ6NmIdG4o2O9jP_*ZuADrhOH+a=U)H z)~fS~j&GyE`GTh(vRY1xahC78WcB=W=4M{kdAgG-wLi~Y@mJU8hs#T~k1H-4dB3=2 z%D;$xe{#XWSpl+VCs|3YY12JxWUpJc;>kyekB@g(?i5_Ybo|(v!tF;-7f7Be{N_<( z|L%>d|F%P&N`{-9B@E1&kNrIR)OUi&;pLYq``D`+kGEaq?+V{=*;ncHB)!Sk)6YiK z6?!ZuIIid6*E${VtgFE~GFL3zs!}#yTBB9-^e?R>_x9x%9^z6CQPo=GV zXC)FOS{B{Xl-KZp>x+Qml5&R)C0sd8fs+mHDNJ}`DC2YG%le42z)%0{r^nAaS8x9F z>jcj=r{7K4Tygu!-L`!Db5g&K`#oQGcgyLJZ`rlKO#XfU`Q!iBnkCVF|E8WlsMq(m zBCF%@XTR#{Rs8GQCSMG&ns{T^?wg!XO!bua1a;cFh5fN`ue*`C^lpMwgyG~p2TY6J zKGRJ+{BGLhf`{UL`)Kq++B9g)J0^*f*`BUYQ8^C zO~0Hq^QK~9uf)r04(W+oKW~h9Ha%STm|b$(DF%~|W~--LzFxFok8Y;lb1latUM4=? zO{R8w`F|U3+!lzt(cX3TRZmL$^r|MK--pU}TRn34b9Kdg2A;k^kD9ui z1w|h$da5lN{iA<)w|8aV$Xspmz-Wri9r-C=@=LyQdDIGTd>1=msZa{f{K@>%;&OjO z)*Mz^B;)q|cbpf``U>+i-J3RY%*)JTIVrWoQ2oGNU!~>+&5>iLH}t4@ffx4dz*^Lq8OL0Wdzqq{q_4_|0_ z6nW~%ao4W*t(&Ho7#zIkr77n3c%jwKb4;CSRaYOpj(z|9(ZBNiUW2n`%TMOT_2sU) zTptnlynb?$M;`lyLRRK~=W^f8dwOf`*Js+%g2&=RmzY1ZYI|^XLf!lqk1W@jhkeu7 z`}*{{o5z>D&XH13*A4r$?w!P$w-NE1)TKk41wJkEeyP9ubKg228>5A)u1__lyu5AT zUU-S=_!_6(1`hdNd)DNw3e{1Yee=bdqbcIEHeGOFn00Uc5x4xwRt4et3hbwr~s1={Pr&=Z>p2&x{~%rC%C);umK-Eqg6znjACTvXNQXO+++cU)m=h z3r`>I_jjjW5m;>9E&T5Id%5e5bZ4u+(4+oRTB?wY(;gn?nI2Lpo`0|P^DYH@yEQfX#Rihe;+etJ=2 zE@+SJU7PIUJ3A)c|Ej$q?CA;3^B4Nhw!4(yiaLJn)~e5|bu4#BcXb_{FhkN!rpbN6 zq%_h0-%HFHgdQX>DK!hv`?%i_KDefir3 zUN_3Wt~=9Z`nYTE>)0uK?mfAADes|jjmhusH|5bwn0j8;^F|xS-xU0I(Rx8{SjTi} z&MEUZ^M`6_O`O*$RkkJR`Nf~-<^3N`&-aV__x+bA4aZ(dB)@}9@bID?Uz2x^P z$r*}!8+LuT%xCjd9Nw# z^PcavG?@P}>+XfV&puZTRt1!XF^4z$bDZ?)zLBmPRJK`=mGNzy^-Y6$;Z4h}%R)~` zmy2fMdw;?MJACOk4b zIXmQt#{Az6vkIl`_c96bi8*$ZbO^li4O{Zb?dYRzVQ*%%HtrBTIEOWQ>I3}+V*jE( z&9ckAoUp9bH|%2=xAB=zN34EbetvoTdXq09YaC=1N*`H-D8--c4Oy{kR(Xwmzs__0 zbBnicn9p5NSIIfy{%NN()j6%xQ%?(>v+McQ_?)Y6vin;_pXbs{``PRIPyGHkTXri$ zLARjiZ^PUx^$izn5;%@Bd~{b^(R}$wR0x-wT~42*%;A-d&krUg?&G+XxFBiGm%|@j zPr7Tk-~0CUQ`(in)sK{>Y|7cneRlCob54fs%*LY9Gr7{;nNKybFou**>|Q>-IdB8( zRrzmkGA#IbuDGd$Ydro{vPenGwP~5o(ya;e=Y&|wah?kaN>!EFux7oMz_qh#&A~GY zSlii3rZET@XfiW*eq(g7-esD!TzkobIajapwe>b+Gx)J>^Vc;J<(>Wqg=?H zv|xU3^Tp`;FpN13eEc9z53C!R%Du z`R>8v=?U*m+N;}Kbr;z7Y|^jQxGnj;FTTlhZ&im%9K(~-`*iqH1vZq2m>U(WnEs*k zL>>R^SqhC;K0{&!95VyVoqj!Ca)PT83+n#R5^Zt2P= z0$;b@Q)K?WZfa$*&H;AjmkjMIOP5synX9aqnZq#QkU)z2qUsyrdhI^3sj8Egmdy=3 z@n5MIoYnc?7~mw&k87nbDB6S@y15=pHR6lA+4mWbBh1+$pylSJToUe zzjlC0KJm(n84K_9y>=F#mfkFOUe9)&@~UH^FE5%zi*LHLXWzrn#xonGSSz@^_|FJi zJ$kTO)I+iD6j$5fe{1(&Y3{I@_HxdOpnNITK64Lly@Vs@dM?B;THLzWZq5>)9WarP zf1dJ=9e(@PZGW@qj;*7PSjV)e6=|Lq+QcN1=W_gOZ|qcYxc6eWs*JCdv$2RnOMQy` zT5ZKHgC9Si=*;ldP7`a{AH2EYZy&2p$3L~rhr}1&WS018dc)C1jP1QW%jU@EhgPv& zTT}MY^t`lV#+e(BtUsKx6EEP)J^NLWy=Gm)WBozHr>Z|Uc=CNQN~T@bBm`d|9J^=@r&?ZM?ge(zquzk_MXD&6&_t&eBSkUjo^ zFH_|B=W`q9R;wRc7^t;@p>1QW%JHKMoD+1j zUK`8~IQ;Rtjkr~T(xU}mAF=<~<@4I|iQW4jzHfeP?Px#9$M-b3?Be%x`oGgJ94$T) z=<@jKu6d!sH&~N6K7PKCQ@-QEzQ?A2cX12FB{8WptO`9FQS+rCe8Ph4f9D$oj+!!L zICHB%{+P~pk84M>KuXO|tp^=fFPxs1@Z@B3*u+)A9}J#<+q+^FyT+WNJjX2_^;4E= zBpnS`GHEh;b!YlSvCl3}Xa1cvthUN~mQcmx^h&QGooVOVWe*>(ek3U|zf3IkgI}KT zLjN~f6`6MySZ`+0>JepsJS{7n+c+bk^j(b0ewW7m5&>&+E|gDoFt)h)P{D6WMw1(_ z`Hgd8Z*%f_GFGHGzv=9m9LDeCs!)FtS#rTk!08DOl&?xv)pUFCjlQxb6J0vz_+t zmw#7Z`hRO~##QZQ;*V83q|%rUskqng*kPB{Sgh5&bGC%ltL4_WukgRTwP1QPkJTgD zq-z^4H7aw)P1~^FYQldnxn+-^M%?Gp<#@oc?N;N_&(iyXd9Ezmbn{f|WX7wf=UmVD zp(Hl%T34=0#rAL3G15s~wwD;MotwGQo-z2s_hlSYims?_=UiaXvUSeY0v%U&;hg0v zYh)`V(*z9XG=7|VHtx-Zco(jcyi)O{3|9{>pBU~d`Fia!ho4#ER$neInYVM=8P7jF z&DzQW?*mN?&j+vh{qtethXw{06``uaw^Bdcepa8gbnn}j|M_cRoT=f-xE)WoYyEbW znJoBDMq1ifyKm>!_>wE4uS8~vDxQy#-!$3D^hrr})gp%)JL{icK3;B@_q#uDfqcx3 zlC`HC&hE4R{WHhUe*XEJr|(KlV_CRv@(ITf(WrSpI?IE8{guiy#J(xFD0)RSpO4ED$EH$yVZRVA$p@zHWlb>}jtz5v>J@9eGrn!qS^0-cmeJyP=Q)m4x%$TW8vhsG zTDjtM$k|V4n}x$Sqtv^;PR&E*t`u40hLFybst|*2*WV zmihBto4eU(C+C^6k~p>0#XIy20u0tR$d+a}WviaLsUYht@o0|ZFD1QS%69xroe4ZY zzEt_04?N`jd3pEXD)`Tm36MMy{(RoH$1ca$dk6o!ON~IJY@V*@+#=|+@4FVt8;p{Nz_~y z3tQjPwD^Ead1UpE4OvCIXSm%~W7+-qUK6v>WIO(XnTtZdF5K3-Qlu&^*x^lEjJNKk zQy0&?i+lLx_ob!5XBIR$MVkvrd}FxY*SsuF_n*L-4T%Y+Q#@BaZd&eh^#VljzwW2j6fWiecGm`-v916|FVx~PuyYVF}h}KTE+i*kAaBe^j(LIub&m^XuBu0r+52|Y5)7ngcnE3 zZx2)p7FhG@W$mxv&9e?StUH=;L-D$;^p}ROwUs;@{&Xdq|J=7GetUHmQ{jwkt~{{| z9!Z63-lw$ahD3Zx&COZSe4W+yyZ<%CgzDz0c3DimtS-T#>lNEB$ht@71|9n)60j#p zN~bE}N6(F^ISm^%D~{$hPh3;gJl%M;$ioRzotIX|Y&5AHU@%)fl`x~X(!%+Zb0ziX%|m*&bzFsIMxeAiwTp|2@7 z`6qL=<=H~1T#>I{H{SlDuyN_P$}UmQ(7)0{5@=U93>OZ}~Edy_u=P zJ2EET_A^^*Jj3GSljX(wQ4Y`htnAtDs4~8Nvg7T<$8qnr2%maB@u17|^65!?JlEg2 zUh_QZ+WW(sbBnF6uDktgN$#f2XD7Z`R;E!a9k^}}_ut()ottNuDX#hOeV5rq-m-~J zvRhtut~)Emd3)`z>Hzsm*WxPnF+{A0&EY$~%<7Eat~)L!T4Hm5UwqH`>w=o*vt43| zMzZT_GFBc?v57XV%nLeiz9`h#c1G+$W#{vI9LyHQ-_ug|S$egxPw-aT>AuB*{BmYD zZmLYb?J#j$BTMP*)ab03OOj5Tm+UyIQ_t4@En!m5@1Pf{Qxr@lI#d)j`s`9a`fvTF zNek|Mp4+cnx+Ejl^n*i=*~dpW7aTw2=RE(&FLnNqN$qd89yzzV?+|mSedb{|<4Nrg z3g49U*TsHJxe#;bs(#O#%FVL(OZL=OEONemZnnvevI8@2zB2XycUgDe{#5Vx`Iin^ zzo=JpKfe6x`ps)&B7>^FXPYu@bZj=TgiZYRt()0b{Hwvu>z7YnF?h9R-3=U%!19F!SBkkB17w^A5AEHE@`4Lt6Swj-A1e^9j!?x9xlUpzx5RqSxlDTm}rS zlIE#g_cCt07A`w_{Od_>@iP}~)Aw)-{pvlr|DfY{{>uBTx5cMTa5jBq^7pmO`pwt+ zezS@SbzIBZ9&fED=&`;Anzebo8QyLG#d&0KeOLGI0Yx3%+c zI2henwzxfK<8I8jj1$CHQOdi&C9 z|N4{8QyX-*T1#c@*<)8+a%4);2Tp^qeeXO)^IF#JaC^ci-FRr@aen1X8qaMkW%>8+ zIrQ>DkjwF61J?+K$O}tZZZol_ojhFS#`~%@=bCh^sm`4=?Khq8`_^Z@uarD_P-sD^ zp6&JI1vjF+Z@x0S+j`eb>$j!eddGFTTXs(u{?5`~LmKWk&$b9T_ z`{M)aao9VVs;tm&cRjUa zhTq{aSJPEbKCA7!dM~Sf6E_S^u`1x38`2&i*NGLd;nv+hSL4e3f2qvrQTfTIU{@jXrILl@8mC` z&*g)R^7s|{!tW(pi+?;6XMWY|aP4)DI@#i_Wwp9oj^U+xY_>7Qx-MTXYs6Xb+Q0BF zYVM0qFg822rG6%Z;q{ZfM)@LJlWwjqdG;tS#oFI1^0EAV20OvMHT8#-pXTJ$90@L- z+&NEf)AlP;J!(P8jl6T7Ewa1Iyd?3(kzWlfEHAL%m2G&}xbOL=1!5`kv!3_=_@MTF zQMKwi>+nnaX6WVLIx%P@7j6R^#%WA@vLyhwx35$cRv%I_T-&S&hOsu zyM3Ny?$12^w&&YPgYVzp9bPnPi`fKi3qJ3ATjNZFq z*n9ItHftR-dUifhVSA$xo7B!aDc-vo>z^&tTw~^`JoigUyNm?qoC{`KJ00VnvtDy~>i<2rG7sl?UViMat@|b2Y8l_X zB|G?1u4J0-IetgMXMfC71-U(&uD5@+5lx?#gbOFtGWt=)8@I#-`oFp{o?#C*SM`eFP=17 zzbh&9$=Sj$Rg-n~J}o}4uFpTuHUHJw+~7I!B^RIT$N%*%sha41|5*+sQ`mfJQ zy>gA6x0ZjwhnKDw-C|>>3;wyg?Wa(k%j-2-X19%Af6`dJ=+Cl@-HWWm1I}%8-N_QM zdXd(yMeEeq&oi70oT58DP;70Nk-I?TmDID9_b#qhi@zRjxi@=pXz=>C*S}1v-@d3O zP_guXp+oEClC5vI`OaGwxbfZq>-!e|0nUbr1e_lvz@b=j?jjRvc@8!&r z`6ufh|F`1P+1p>feC4mNDxX#N_i=f?pZ~u9?+#m3-7U+1mF4-Rra5MjyL$frDdClq zOG^*$=hV!)R`+qA<_6JYDZMdDYQ8VBBj1cePC-##qv1hT_`)#wotU9||+p$$= zr{3$&g)Ha0m#y1y_{ZO@Xx$B)qaxq!I%&3c#qN74zO&6gUCpeW=`6i|w%YE$>*AjM z_*&Dw;mG=Jy63e2{&r>$ne+Mk`USl+Qu)9fouBWockomSScrq_qcU1 zER8iex~zHL>P7YczTIV?uv`A#o`|&AZ;SSxIQ_{(JTG46zz3&{m022zU46D)SL`{r`;AuiGDCq+-=45nFqPyi`}lj!ZU;Vt4vvHa9&2Xb_Oy}8 z_`d$myU=@cuIBRD8wst;yyJA&=Itz9yD1zsynoK6How3sD& zc1F^baV4pCy-SxY(r65e|6Hg`-#jg{x`Oz>tuTE zD=9w_-gNWJMCDYqGILng>8y3znHk` znLTf}2*yr|d$#?pPf@ns{=$zx-+bQb$XCwkm1(o=+iyFcrFZ= zJ6FnYO3lhzGyzr04u!;Mi=uS>jYezghO5aOLCYd*pv;?SDB-M|HNtp7MDj zS68vEyxknXzOy9y8~euet1W9oK5UqKY10SCJ{-1%h(lUgYg-;F580C`}YPuXv2_^H%=ZQ#Ww%qb$2 z^N?J~nKZq}t$KkXFYm056LzY0?;MszXzj~>7^fK^dv zboIlz%U3;iE%4A<>T&(9Vzt!5cJBEZI#YXXeYnay9x^0NGFcz~Z_@p~o#F{%VzOIS ztPU;Qm%4@}zC`}7`i!h6yY>ha>)6JxnQmR}Ic0kl`>U7tDvv)o5aQLFlCjFzDQnaH zi{?_i?W`F!+KV`=yuaDLs(QItvaM-Lcm2IXZ+5Y$Z9P8e=!3e2%cn2%oIQQDVOOy> zPs*PM&Qhlju>UDexDwr9x$4#09r$Cm26? zz%Fq9;4%HrT86KC7wIp%_&m%rK};c2Y}Vdc(W(V6;<-2-onr;f8E3{d@3Q)KM6^VR zq4m?|>BU>N7OC3Lo4yTl35&fHiX!hfMYonTn^EwYoe_7}cg-lXHE++>pjl&Zp5;b#Reo#Ff7e zv(_*izE^&`z{0(~{Cj)H>0J!{Vk$Bl)}Qyds(7^g`^N=coV98;r-X!Bud4WJZE_j7m7cNyz0{oLG98LpMV+hrl(+ELJlMpjX&t1o^P2J#7o%zJ(;mDj z{IZX2qT+qQ`P&yXepYDT_(fkt*>&Tib-rbj%Wp613bWpN|6pUV>mK_B3mk6+$*3_l z^#wkie13hh@foYYKzl!ph$#zhxBlNh<-h8WbeC5JOVT>?FWMfQ;PqZ4Fwy(s%O};3 z>@?IQ8ys~Tl9GD3r5wG30`@UX{I~Ra@JHMIejm#h&3vBCw^{6;<%hTllhq0W^8=$r zyk2j=&3mk`;eEux;J*#q6wh&eu+U;kzL&lIf#07=61&dr?yg!{vC<=E%36caWqjx7 zx!!Md7&n8qbSCc3hJlNsxr*B4xP6k*zN*y*cor|r4>KZ9mDIp)NgF<#<# zo3gaf?D{h18Ba84KR*)1yh!6s!IcXRQXE!~kDQQIDB(3(*zwS*@8xgjlLs%nWogS| zEe;ggzc64cN1ygvWhbr>k+ug$ihI2)vgUd(oT#tFi>AC@_WWB*(BU;-trK=GI2@t)a<B7 z$NUf5E{>dCtDaxaf2MlPo==l58!Vn5;&T|9-9*ph3qRnWo&SYes<__f%>tJC)U{pItCrFypv&gX`8g7LHX}s-6pk5 zc75rFK8!g@tJ6PM1eE-lR;pfO^!iM8$jnTQen#V7gR`r}#Ls9h5;1A|BWIoyaa3>l z>)l`7Bp)q{^_{e*eP!O%*+1@be`po_k=p(G4fl3tG4Y>|jC5~_*gRU}BN6?~{+H#7 zZ29z>OI`Qmd?)u#Yzk(#kG^PUd->e8$qI%k`@ijytG&x6YG8Hk_|HI-xo+!g)*szE zd2ZI`>GuqZeO}4_x-Rl+d4Wt_Iv)#{oZvL8RdFy^;MTcLg*UiBABNq-NTys7Z zR%2VY!TQQPXKx;>1>U^oI-+gXZW9w{8NHBkH(|}tLfIZCH${{@e(`SJ>yJ6r=Ur39+OE#?a@#n2@hLa%qebqGwI82tQS(ZD zG)M7kud2&(lbIJppEZB!Jfqod_w~)h6Xx9^54z(mQ@xjLh_rW6RJPRq(y{u-myvW zHAQ#+yt~Vs8o!vFKRV0zT5L;Zu#CP5pH071>$&Z&vvSRBbEmkL&Wsb^#k#((u35@B z_FmY{UDJb)Rxt}*{w@?+AIZ1hx6*}W#S~UHiH({%nj5Vgo;2RHeUSZbpWHE*pI5|L zx?8t6E|q6n=4~kS*X5V)i^O#GkKb>(YPHUN_OP%-?|aUL{2t?Q*RO`nj(yz@n#&91 zFURoeXFe;LE66I;9P?>+?2-$=eHR4W51Zn0HLbLK-S^2cjky=DPEohpY`#!s@x5G) zM^pE9s!E=9=leG+V2Q*3P2CqSWh*ADZR7T4-F2(Pz1l0GVafZgbLMQ_B_rUvicyI9 zv&U?!^>6<;+RlwC{joAQL{n+ChW%C7_jm8bUVT~QT^LZk)sp%2?{D>=r?7QMZ(6+6 z(E-Q_vDVb|WWuz!1!9NRHtuA=s_z6G4Comm>ZXDzVG?}+f`&F|m&3T%p;IU_|}p2>7_=yS6R z`+|(sIxjtMkUQJK`_A(6nhJ}~oGaNZdAB`p>%Pu4SKK=*f$b&B>g}Dpc`_g0?Roh; z?f8K;4y%^%O9vlslabLC+o9LrZT#hlnf~ctR-L`2zy4}9bxuBVOiiZsg=?7Z=|9sI zvv)^rnUefo!TZQD8JQO|*u0wOy60>;#g)D@jg?1qmir0Q{{_rRR(rfMzgoQ5(qCzH zFf+{hzuqHV^*QJM^jtqAkZ3D$wCzknj#%_Dukxch&ld7K89K*j)!i3gzVzGN9ZL=d z=`C>Hx~THNjRiC0`_BC1TBI3fb7Z$Hx6Gm`JLfgdFL2X3DR$bgrfQbrUdR1CmZz?qPQNHE!jh#Q zTphmk>7}{ld2aKC`WG}vJLI&@UOmHBG>xbHo!4CP>xSkh*_ys7{hs(nah8j#57+T) z>L21Bc0N}5&J&)N#?M|5=}>&&>hXZ97jte2irwE$~X z6SH(bRlWG7 zd+fueht|eQM(c0&W!vE9pK7oBip!>dt)b^bb{z^b`eMsThBpQ2b>g~3_v-<4}&A-X6f3dN_i&^CT^6~@Szi&wF ztIk|;aHfmdTYm+nn2dWD4o>|ZGh>_X?A=RrzX|nbExHiL_;$jzcb^k$I&S($NbF_) z!RPO$?BAZ{5%k0PwZb-?7PrRk4a_y+-)CE<+X^Y{V%llNt7KVxYtHQAQ`cR$ZZYqj zd~-=z-|9t&W^Ib$ZUF`Q(d=&1JW&EKVy>T6HEZW}@*B)$)nz|Uc2A3NTK?_G`t7;x=?o21Tcnrp#~+BF z^7ilzNx_c~aK52yzM;4 znGoSMFt6IIl%J9+5e|k3?icJHe zl(OZ&m>QYf%A6dko9Cr!bE7KqQ>6NAA!VC6>DQ{Fau!|Rrhl`%c&*;@B}Th_TD^`h z;an<{Xi<5=#$9tk4b$;AiwsPn&$|7QvA4Yz!=I&SA9`q-^)=nAxeD3p6Lz^T`MP9P z)rEzxRBG%Pc)7Pv-1jB#r{jgjIlv)~)K128DJfDx!npG8#CU-8I zxk6m+`r{p|vTrvn@x5|tlHFz1eY@++%H>bj-kI09Nc3F9cF`?CF9a;DV%ZL#vUo6U ztEEX#anz2|+?br}FPWZA_$;&c|McpedY4w*IbD3w?a~Jqt!>d=!NuDSbu7NMf0Od% z<+?6P)BKNZe!(uA;CSiflmiT#56cvZoNy?K)30b+EC0YNW=le-Y2~&(y%M)m-vs9d z?pr)}JIB%341r$D5Vj~LnV44@D{lOYyLr68zDj8)@8y$8^<~y3wI_DlP1*}PmB-SI8`C0=PBz%s^|LBDP~h>uhZ>*!zB|np zd8F`2;|jm)-CJg#G~T#!{Bkr|uvp8#pin$+{uI4?8;h2jZJsed{o?(M?YAP;rsW)I z+7`VoJ!bW?E9>l+^Iqm-IQ)F-R=<$&@E1HBwYRxcvL-Uh^GkjC{p3x2-yVbP^#}E5 z-&(xk?&;~1g*IQiVDKz|+Va#1|6RXK;=8PT{>+&J8$VxT%CzuHl9rkvn!Y7jaBITK z=?A~C`RqLJ^ejB!Tx)IHrp@2^{~q1u-M!Q)>>uCStELqu;j^W4*~2-$uP`^B^=Vti ztF5OGZdhV-^$NdtRHjySi)v_AgK&R{ZjbV71xMNblC3R<9!FQz#s4{UEUu*E49kNU zjaQQ&Oqww9+dk{Io$RIkeMz(4IhM^l{dtPvi+1%xQ>XB(>zj4YjQ5xS9Iy4OK9yNt zs0}gCoZadt(X{`Zz`|WSWE6q~Hg6XD&$nE~Xr=MYBk$9uGCWWHx`?%*!@1zQ(nm)z z);TLvHEhHxc5a)wAu@&6YH1yNNrh*iK+m^8Ic~Z89?z>D>sQ8=&fOk*=i|ojek&&Z zPQ3j@=GBs9(~nzTA9^paT$wGSWM{8R#-hVYGUt>OE>5=A%UF5w#qZe@H!G~>iVA!$ zzk89+uISfGLUK$N+io&?=ze8t`_Je`FR#lit^H4fbri}NcRx4#^v78GjNXlzQ3?w_ zbnV-{(Kh3GTT$+z?@V8$o?S@%s#YmE!9)7{!j4{tJ4Y+cJ(RbY^O!OOcr19kq@=X# zAhU6Jb$Wu|J2|dpQ!DJcgkqgv{Cd1G@8n;h&vR}yHmvKI5#;#(fWgm7vCcWa?u4yg zBkdKgsvv(z(Oi9b{)D2f(sl+pPSJZ={drIRu-MRS(4jxePF_7J`?mnQ!OSMd;te8IOc6Ipq-_2`oCzYb@rJagUG*!IX_-7S8-o=56aqNd%r z-jgv^>RR33)22T@imcinvq?D1WBCfDg==@H@%$FxJDM|n_5Czn>xnET*-NW;l-yh@ zC6+f~?dhBEZ){!iBsj;}(Q(4tipgJ_YYhIpdDy;k-mAG)v$k#z`j>7ce<&*Bbp9q+ z_49kv{0_}#s@Q)rV0E;>l+VZI;xF{nbofnwD!=MFYrOoz+pcH(t#%5X{d@4ykBKwa zJ${iq|L&UaV)8Aed)D4?HoScK*|k^FulS}0=q(F>rzPLL>)o+OOJzEDt!DD#+0jsX z=y%+c<90riLUwG9xV`#R_o@AwhyU^%)$6^=acg7t$rW5`-k}epWq(LsTHM6m#$(7W zzsP&8nLxvO|BsJfh2&+m`|&z_*Sr(F{n2uLqZ_wdWmxZ2+nKeTpEK?3apujN*Z*~O zF*IagN$&dX%krc0Ov-`St;t3Q&hHa$a%f?@@2tl9=Zh~7N6z}rgSHC~oRqeFAN2CF zT(k*)HE;F{M!RR6XSKi2Sef9r^l{gW>$iTK<53TOH1FQ*@{&jK4qSFQpIkTYa&7!~ zvf-PscEH1#!7ffJM}xiG_uXrduaAofeE#`7!v}>~g7ViU=!z|vZ zW!22r_Z_HcazCkhyw%gDS1wWi#xBWY7ZawxvJs#3#5W9~`_HsF1q7pie_^ z<;Uw4dmP%oo5WwgT_jm(c6QUfMe9$=M5NRRZ$8YH%vSrM%CfGmaLqAG#W&`Hn{O`M zo;uh5k=e!frg_FIX4NNeoc(WySG-5^gkAQBr2ZEq_TH0y}h+-dZdr{s|4P2$NN9J@Ko=XS#2i2<;t{ePNK=jPt08yBYADTl4071FM8Ln z{Os!EI@B`t!K}>-9i2WhqzZqte)O&(ZAEXJRG;09UjA0W4R^G%?HDH|SbMH`CgA^O z2g}=BUWRGrw~iXIBnnLQU{ZZ_Yqt9E(CnQQ5%c^2F+IW9Qwc*FgSqkQ`>IPyMso!)aKFT94$ z*`$1XMcPxgKTU6=bOg9oe0XdztMk|-*4wJwcIhdX^1K*i)HhFe|8G`r*Y(n_>bZ{Q zmi?U^w*=FjeA9N^PIu~_<@rZR&h>xawZCU?IH^uNCa_nrTcbT(#C-WLrcUu4O;01< z6~*hExYld7f-8B`*Xc2NI-F%9YcCize~{zxKKAw}(pY(SMPWQ1W?o74u zm^J^j*qPw0Ol6OP-5d&XlU=V?Kd*VOTABaqtCVd0#$#QNiaQqY^)~bs{lD?v_-|S1 zuP^h?p1do^eNiX=^gqXYa~0=b{`>HFb^P(XTu_*UDWlaFRzEDJsCD~4!BdRv z4PmPf>|VWtWnP_lvQAmxW2b*6_sUi;uJ5h3Y45Fkm?1EWo#VQ3SmWnqD#BsXPo5Pi zy6y&TMbrvg1`-&gdZuH8VQ81LY6#3h_9f zdDm0ftD2&-T;FP|qfba$aog6LeG?Hm^3d618_q`oDZp{qB9AUx(gq-sL9H$=!e9h@q#+DTyf##~y`W{I=21 z{k`dHEltHat(<1h>Nwu|n_u;GF#Epg_OBDwcBYz6_h#Pe>(^aa!?wxb!yAF_u+LKr zwS?^UIk#6_y{djrQRkndc9jw9594c}KJVk)RUUV@Lpqy-Z6Z%`566<#EAn=oGt0_L zWP6o+=24c3%-LU7E}=%D;jWS8N~f&;W$)f5`{=WNP)GqAS5}A2G_m^Q6|z$2+(j%| z=dvc8^#i{hrmxzzyX*1-{Y~0tjq5*g8FMLj|5(QChNcvRjL<%H!Du&RC&g#2^&AUacyd_EX;S_tjEjIyJ6;g0;$M>NNxULr0sjPcG$NG%W@=W_#k)M1#{vO*=>G()Ne&_Ymr@kNE z)IL@2x|8{lsN|Sy54JCsTlJ7#F7+Sxi!y$fdKSiCc^wL@LR;m8#9lw;d($hN)}pTA z_*gXUn9kC4>CFe`8TURAyxSGl)X(>B{sqo!g(7)|>@%C0ed=y!tUMaL!qv(2%!lY1 zsXyZ^FNm{uZ&H}JZMmMQhpel@`*7R$^~+sON{T(&`r@_n-NbncFDEhQyIS1QVce?` zzwWPil2G=xRp~D4PiixEsj+unT(_2ofkQ9u>DkEKZ%kL2^_BisMmKbc=q)#GK6v}B z+TN_suTF93oJ`)(aJNWSVPmQ2QOlm+50||%wfW5G`=(m)?dKXDO%A=2vrksvznaCk zXen=FYkAd*33i9ygnA2KJ|VJv=FKlLg?A5~i(EQsLPhVPy-V-R{q=;ach;ZG+2=J9 zT)lV96MefSzGv|-o_<&Ux$n2E{^@n)>kri`Pc~KV*KP_oTdzrZE$HvvEwT4CXZ)-- z=li9SJ@)MqVXPiLmC^!fiq9?_`_G8F9dZ@VuW7D~3=D^v7#O4&7?8I^CKi|I$H!;p zWtPOp>lIX%1|G~hrUorep#W!W{_TM_R6~0VD6ne zPE!>3EJ$q<@t?6-uSVWtO6!Lw8o^?dVxsDeEmxjs`YaoNX%!2fyVsngNjrZ zDC66%nx03?|5nMh&i;BX+`iR9LNDQt7gNZ>n-(wfu6>YNuKn5mx8N7)>ru;3N9pdb zy0~lIO{-iB$Nvv`m;dHfe&JSI`Mss)>K>NsmsT%cBNhFz8*MMDLj#-OR9*&#m5vMy zvY3INnOBlpR8W)(S)p}TIyV2d#izUbo7(Me98+^Dmw%_|z<*-d&D%-0mu1|!BU{tx z;JxLn+p;|>Q?I=Ey8k|pr{@wE>&`o|GUptEyPxr-w;3P)xgc)BYyS2BD}PN(_t7Z( zrIzmWSvCDr&z$_sZE4HxRrTHVPq^;9>$xu~`_tvq^8dZkvfb_(hH_2XTpc#$Htzw! zvoF$ECZ}hw^9T!It@mE2Dc+UQ5D{?Hc+(n}WtEy42fy}l94ne894i#b>2$QOVByR= zUTpP@;hKpTXa0=)dmheb+#t7yUGwSlqR@w0r~3ZsNbdc8qHg_+q66Xzsd`#sJo}z?>2W0- zuz8x}x!P&DP=?04$g1OI>Wedb-c5Lxt?THtf0}Dt@-Io1^D92IPxv7^`QL?|Q%aum z?|C+J-Td37b0-`&&91vY-E#HD!oBwl&KlSAgnf{k`bb-_X`#yP<)@n-o)4)Cm_B>$ zH@W*y-hMClS?~S+XYI?xe*qy{+aC3JUesa8`J_5ib#r~~4j=uxy?cJVJ^OqA{F#;0 z{#LKsp(p+B{M3CLmG8~DajoLiru1d&Yp!2?)U4CHO3=A$NA-fDrFSmd@=kCmKf)1F zacDzSd$Hw<5{rM{oLsZk>T^Z?d}RJ9*3I4Z+`bm)aHE=ejZyIo{)Qj?-#dH{wPs7& ze(t&HjH9em&)mAHl2;eJTQQnp#lw43Z{B?>J}p#=_eEvF^%v!vw$~^+^3LPj#TND1 zae?^MOHb3-cAuJe^7(Nu8`(F1zP|qQ>(%As-QJ(N_Q=%ksVOflzS;N3LTah;4|$Wi z?WyJ=e#c(s*vKw5pLtLof`Sd zc#+AE%uwATz1!_;Lw{{Pz;Ns1mbpCh{+ua)elqp<-xdq)(DVa`uU?aj{T%senzn7e z@3(JaGiu_yW~KaDk;Sd!J9*ldX)k8Q&0nHd*|sUOn@?hWZClgTS?eF9-g@74;9wNCSJ*i*!6@}x+UqP z>%1nf^(uFHrnrZNSUR&ah6Q?^YkMhv_H#+-6z-@cTlx(8U0Y`Vk==0B?!)iIQ#YqM z&QRQxyd+flH(!iX(83p4GuYP7NLX;!dB6iJ-Log{IsO!%bltVI%GM`z&w-Q~`wb@kQMmBiXX&hUY|=iCM^`bK${(vZ zr{Ka8Uj5|V!sT)rKh-!TmTgfJXUI=f`jx2R<5wWkm7E|hs$X=0!AGEN?sPRnOEzY! zxylEWH+FaL=5KC1>YQ|j{r%rp5iCHQasU z>d9B89=kp5--51y|2fy$CZBoD#6Z6z5S3;iqnx(xk;Mnlv;`&1_iML*z z65`(2(!JqKk=UDAt6N{Rq)Fa>#W}xhNz&QpyS-IXIMOGF`<>)kWhxvN`oaIS0K1`K zBKyhsxQX%`XEtqBOyCX9nrQg>@gLO-4BdX#d)|dCGSTStkyE*zvq`32=*9Vsk%!-9 z1_qvu5zPK1Dp)&v(T_t-a+a*0*1!KDCawEl>~ZFXw!60{ZV%n2!lvGzz;D{`d->4h zJfSIL-2U zs=(#t`8HwtZ;DtJp1hv$GXL+}yf?|ozce%5x|Ooz{Z0S6C3&Y-1v465-|LZcdzB-% zvF&HW%oX{phi0U+Z1d*y^C|c%VJjjXB=LE+*<@wy)hXVgmp5&icY9_!LO+ooAQ1EK~6Nl5a*%d0P)I z^PO^K+x*WqMrZfgWESS`E@^yLF?aWj({~pe+g(}gBK7v9*TiBTq4^f_Ov2kVPQ7{8 z`FZ-508@wgpJn)Fth?ZR`k=B#NZ@t7oYqE(y>_2eTm)_uCsdsKX?yg8(}EqWZgsND zU;g~6^Y_cY?cc-ySIl3Q{QpdeZAtl^Ywh0W&z<4Y%Szh)>&?XVHhs%u_d3+yOL$S@ zcgG;i@XM?}4Y`}DnA&7+bf~u}+Suska#q~!o#n&ddG3Jergx{a=TBmJa$Ljat7b{j z-(R|myl*cG+O}I*BWSlFSiyP=YQ&9dbD?YZ(F z*=rjE$bxAth{j9MyZ?Sx82uG z{FgeAiC)-ZHU3qJ--lO^&>0<)FnO+=CNV9uyr6bL{xM1=0)Lk9L9=j{rf{v>C zgxbCku#P!V&mprU_ixUD?>pnH#H03q^|yO2D_&r*oFjVOkB_#3zZ4(0@>wn}>)olJ z_&83${=rKt{cfL>rAPLKMxBzmys2aP@|cbH?k+$1V~@s!W9$q$b9HZqaK$jqcqioi zJ$L1fsrA;XoocexmzE!sUX?l7^m@Y$Kl^GcEeC551d&)FKu)gN6fp2U8eCFLw|SY=J$e*asyTkl>vrKo83!PC6!do@>IVg*nC zZTG(SW@4NwW)^X`#aHuo9Q-HmY>@D3)4C~J=hPfi=Ki9;c;DJ<2icn5Ybsnbw|}K@ zM(*Pa;|7`JUk2*_-J9+>8LU71bo;-#raV5gPG^~keO&gSbe+M~-+Le3jIBN7-{ODa zb2!tNy=#sgZn}Qm>FjFze@nvuEj4RD5`I>=e4@EfjEl(4cT;s6zdc*LNY%IMnQ-IL zD@($B?~3&OeO-F7YrpX+KK6V13-|n<_Qd4F5@n(PFWE2oa7QM&`iDH+`ht08xysvj z?k;u?0iSrj8tpqj*YcoQvyFmE`Kc?~%f886o_FTPM6pL1lYZ>^#%XJx++H}@`rB51 z>xh5RGu+;oJWJ%M{KGioOW2Owl~s;uMdDX(Me_&goprvh`fZ7TX|vlT*~4ahceu-v zIrz1_n|DO3-R)oBSNlz&$nQ>ZkM64-)^Ds1ykxXboNj5zXR_)$uk|fkn`3^_4{TLt z8l3moRJ41xjn+@*opJoXjRo1KKAcx>=5bngbGZ7(i7mCyKE`b>zg4f=B^&AdEx;g& z`zwoFhvuZm*^Uz|S2MV7tlw{_B){oyk-eAjF8TFNt+r-7+56ItN@X1k6Me8)^_WeV zNtl99gyi=*ZFl$XSpU^m^@L-$`X=@%UmBkVr+)4z+nRr|BspQGYiz*XKU!~JvooGa z%YSa16z*(R{`t(N&exec*UeU3)GOFLbFa&)_1+2-YqNykZ29jsjdhl)fY+;&H(u}V z%RO9R<@;yfZ1?w_Q=fUBEw=yiGH*p>R{we)4xW1gzb|XH@Vqc&YW{xpUkMX8N3Z=6mi)@3=DIe6iPE-c=i2N<22SU%B(><+Lfk zUj_v;ZhRqE?DHpK@yU<5OC=u9;s1C7^@j*P&PixuuXJ3*}b~|shu+Hx8tuu|y ztDXPu`FBsh=!-dH?y`CH8zLBmX3Xf|nR-L!i+aPVB~?~OvwT7xR=@s!{oC8Cktr6M zRn0D$I`>Q0O(=;EG7}5l-{Jr7^2FWIYyQ<<+V|wssW|5BdCM37XF}~v&%L?z28R#> z!yj)326?RgxuX2yg4E=a%=|pPg37x#Z~Jb0^!@*>`O3^*C0SO{_y#lIv9EeYp6ScW z)lVi|+I*umQ6p>TooickD!)AJGk?Lp>-r9L>rHl%@?d=Vw_8)fLeZN3nx?*E-!vEj<8UDPsw`sNie@<_9BVr>+daOW6I^3?-LDnwdzNjsWsS6Sn2CEBUj+&!(>)F&G+?%bH+ zU2}oqdUEJ`(ROsVP%Im7Di-Sj}H;^DXbbxnBS5x4oX(WKZVx12;dfliFs@cjx8%*up9O;->D4 z8j^qhEzYh$M#Wr$BGoLl?(@zZks&#Rx?|M~eytN!cb+2Y~T^Y8CT zex9Rw%y&oNx|DO1=6CV^U%m9(gJk_F+Whz5wCqzY?p!7w<$iG8?Znml!uR-}3rkNg zTG(8%d)xhDL&N)@s`#(DDO4URyUI7sc1_ZRg%_Bvi7W_C=-kl|@tJqc!&NKWZ2YU^ z@}I3?iK*Sks2X>`#WPjwRjPf->X|Ox@-1g|G&be$Un}8bl*ux=g{k(k*_@A39TDqd zTzEfJJxIIoepQ_Svr-iQmOBgINX^q;>;7YUsHINtt@ATq{rY(C?XKzB-6dbxl$0LY zOE)&$xLC9O`i;tC^ZreYZD!oEh{0B7H`@+rz4GdvIX|qd%u{5SyxTP6C8zTHmUo=1 zm2`H*JuTmmc3Y4~cd~bCZ0QM+8ETymOK&&NyxU=?y2fWpo&6Tm{3o*%y44qR8?WPQ z^Zxwu_D1c>dt2XbaPU0ta4dU4)~c_&H1aDq&&vw)-d4WF-*;;4e(gC%AuBlAgr#im za%P16+Ne+_@!;)r6VEjD>SwdEUrf7ky;kTYYu(v}eLr@-Ib@gJb@I~Xt^&E}A1hPq z%G{2fVwCykJm1bR$V;tEZ|RcuO*4K89-omK=9!^x`My|b;!bthhvz@UPagR(PhZXAECAaL9c&)gd>32qt%d8c8CvzSubl>igN#;z>3MhQ|v$D8n=1j*Q z+S&1k{0w(*dsZzm*If0+H~%-QGIJ8P>i1NKcRfBSs(ips`uhQXe|xK}nIhQ+Yh8L3PN{nCsO4!nAu2f5 z9A zZHDzfcX#&w=}IS!zs-NM`gkvooeWQi^}U=xo=CPX)JrXdFt)51Ge{fM)68Y{4zNhZqP5WxIf{OnZVq2 zo$GeBNUQvlJN>wUFKwQxw8&<2cNS}n%bZhMf~MP>eCANl^@`3mt8tf}1)%6uf6; zsk|$eEvm%Xam6&@hRAhxEe8E7OoBS$#;Y}UCR%fxENN4IeKgALV%Y5DRqkqP@mrh) zG#3?3ywj*L(OG8k#?EaeS!;Ht96YvI_}-J0D>t0CR0v!X`M}xib>h^j#`nKUo*iwL zbCRu@D|N+Cj%fnh+u5sLvvVBa&(##lU6H=>xXf?!h{v37{#>`aeJRC6>rB|sO+1Hx za_xC&ujr#_Jw5V4Z%xi~3%`t-*f$oF1)dfg`Si`cC}Z}xvRBgIID5x}%Pa2hIOX+W zN9H1?39H{6XPfc;``=IbM$3hTZ*M*L{PACz-u^R8Cwu%iuUq=@Pze9sp7cPTgI8ES zx6Auf)@*Az@h>yVf6+%v1$e#_J= z3TCgkvzB2+)g{BZovqV$RNrL@pV>7_w(6JdmQ@nY+w}jg)2_cMD4)N}Uf{n-?fKn` zw}Oue8o70JpIXZPcK_|~hE5-Me7k%9lIO%VhYn0N(N?-;cU%0h+3%HWrA!0;{wXf0 zdBz>Ictur)?_NcvAeZR)hdxg3C4T*{MP<({y?4|w{puSRA6;2b@1x$If-YS3pC90R zJ~Yd@dis=S)@vF%4)4Ai_b#k@C#UoB*tjeHu07H3FHX95?oY#!FRylfY|ot0A+)i3 z@^p{fKU~(A`M%!PoSt%jUv2r<(*_-P>~H$*Hdgw6Icw$xouriVH(8l+G2a8fE_>AC zpdJ3I@0Llm!~LVb88QvlDBk!cv+MDp_>QUb^Oo$)X%t;_KIPq#OBuQk@0@;h@yLUn z8`JeFp9}tTVfuJyIwc-N={!-x-*9;fa*GV9(;vpewbr41e23Y-U(# zn*Yc7e)hFTMZLSi{Z+P& zQ*)2k%V|fPztvV^^k4+`1E+7RX=h`^Z*2_4O6btHO~dW_|gy>85boE93O_TpS|l3+Gi{?zVk? z#z@F8@R0WDjV^oNPLbtV96Uww&*}0_;zpZ$c!Z4CukN(?&M0;wogU8YG?mBBzx%@L^r&i#S>DQ!^%(hOPN#&d)e$a^U=K z@AMlnY78zPE|xK8Z1_~Z{ZB=npJ~dCY12!u-li|dmt5hEFk%Dwg2O1xxR0?fB%@#Zf=>Ny~&@8^@^Ke zNtaQ2PMojG{q3wQeXg;|Ih{YV|N7LtO?Q{oI%zfYTKqaDvy51mLkC5guh`0NpE3Pa zp{t=Q^W@2?jS(iF0s;>#a$9fF8*oIv?%k!ZrLI1&r}l&%i*k9N*Dd(%(637IL(8^p zWw~2s^_anBf7OHty)`MCyqyQD`YTm0cfaX>GxIvfGTUumB1bn@V7ogry{?pn(Kc_At#x`)c5q*Aoa^=EjzOS$>*~C>w`+gtl$<&+S?{y^>r)T+34T0xHj3A9*R^n`%ZJwrALLSG`JK{t z-*9rw@eT8}ZvS_>@~Pw<%RTi4?Cyo9q^GP6d;Rp&Qjf^fwM`YuZEPAfV#R{$Mp`c> z|4jMQaPe|zDYw1oU#Wz>^$m`zXFofV$ohw8_g=C2_cK=-wy`Up*wCWhn|&^*NX@E@ z?N`X9um`zkqT;jGD2fJiIrpdgr_Mad;Izf`zJrRU+|i|t1tt=wUr4=iyK|wxPR*Qq zSGB&a!#e+|Czw@@I`~By-_-0D|D~!m0s+OF~Pg{CxvLS@~|qt}u?)lB+!RK>NmufUkqKju{ro9O!P z=nl1Ge7@2-yA>NWxNl4?bKNic+<%6J?E+@*mHA2vpJj6S7QT+?;;_`|S;5P!+1#;L zpYy#@;X0R{=?nXq9wjTOtzLKZVx3@iocvcyhTrQ}yzfe$f9Jlv!<4XBz2!-#Z~L4m zN$6qiHLyAIAw%J`tFQh#F8DjU@IKqXwOfdCz^66UY)g#OP6zwkUY5H{u7^H zdyW+wrtq5o(KEkuynG&)__DSSS_1og_a?J;E;`{PY~<=c@%Wk3i>AA8U}Cv?OLk=x zy8?60gBXPx;iJpW-F+&WS6f=TE_1i6-I9Kt!q-7FR`PijH_m1aJoA}-YU~%!#MT3> zoIMi~kLQ0ps>~~?9`tH^#bT{+-A0aEg*W=0-*frw zO-8r3D(aJ&o-k|;Ul_*zr}gk=t*p1rMU~r5`fIJ&d~bi1(EE))nQCwOTwJL9ELbx9 z%!bLQh2`2(o~1}K{dxYYphn17=fonzv)fJFgo-ymE!WGu^y})Cjz?eq=u1t!_owDa zN5!{)+yxRcA!eM3)tQgtmwC@WvS4q;>*d?thPK~lQ=gYrab||J(tg3Tn7zd_FN%Bp zli%9m_PH99jN%`J&(UF5f#je|TSH)qXTRjIEAW&ETbu zr@+LtMcnT;CaZ7!WBW4ub=-_w1snYCCMg>5i>$d*cw+m8oTm-*cAK->-(MLtf3YA3 z|2zKY@jrhXP4qHYRsQ$=X&3E(EB>(_`Y#(NfBK20aZwRNVfdHNm4ex}+cM7lD)+Zg zzV=d{rQ_a-4J*Dlx-=iLR_NM$^sdaEA5RwrEP4Oj!l3qI$cD}O+xP4_`%z(E{d(5= zZwK%E{35k`qi1KO@b6_sC+-?gpXT!3Z0}Mt&q?7GvO?R}MYsfX>|vf8bm82?KiZXh z=I1Uj|M%qJwv0t7$&xGYY+Cd#I?ez6?D>s~60VWV2S4m`IbUOw+pJ!qkg$D8>J;An zQLC~zo!jiKK632NdUv$KHgn>aPOpaRk4j?Cr%dW^Ef(4TKz)+6dRD06CYNpR^7^iS zx!|2~uJvkp@eI9n8}2nngzVLbx}Y;esM`bQGRl2G3cD?vthZ}w+#gTy^AlL!0(%pe5)>c zL9E(md$DrKQzxknA|DU*awwciNobv9BUZ*IWOzhi4bxoD2St2NQCC*F zRS8YIloGR@ms6ELV54~EREFfhG~?c$MvtJSx~v|lJ8vJZXzl;6(zBr@v}%sj@1+}L z4!DXPUtPZb%ZAS(C)^*fEt{vVG~?Ih{Q2i^{&@WJ$K@x_ik>k3<;tmVoV6rII_a(G zPT?0WR*uD5(=`H%Z!O$^D<|MUi$tK96W8g8`w23<2|wiWrW{GS`*+UG{<${_nA-0d z>G}6uw4Zl$nq_Wr3-bi6BNv+{{cm*Ht?12M#Glv6^-Pq@Xl<-&cW=v?UcStiZ{w!f zge`g1;e%varY_>bPHvWv4G?m8^J`Yh{R?W{$Q zXQUtfn(epX-B+tfj&4tHsh_``*EGpTFSYEPce3Da;_D|$*Z0NxYi;U0!*$@Vi*aez z-JSNw*go^D4$SS+4UIfzdU(IbT)x=2^><7vogcnC+UK=zR%ZHciPXkZcYNe8CY1Jm zJr^N$)a2Rw%r$?0?LGZdt+I*5i&>WYZ_Tkey&F#{7RG(K`G7B?^+DRo(^IX*i{|@3 z?pkptLd|K@^9jEpK}zq#OXc;ss-CX8w7zcJpS^!nC9;1kb8v4u zxh?qi($c3-Hl8YXI{W9;-Xh)hwoEZmgEc?j?DqS7q%>~7>weAq*}luSY&*=N<@rNM zrGB;JlWl$0QY&_R<}2Uw^_W+FS=aMj4zh_m8XJssMBg?XoFDRZYtx%mzGdN2F1oID zo|FHGGwpMk{dvb@FCDS%mye&_F4p=l`WyP;M>8T6n9DdB7$(^;Fi2vK$QBfV*VyP4 zRGy9gpMQIo;QzbztkcaMzCDu2ZTxG%Tj5q*^t~>e*Cvg|8eF1znrx{|D3MZt5a26CKF#{k)Y+_VsLxWX%mk3 zACJyHyZWgAi`Yqa21Vui7iNdA|KGIkz2UEpN~epA^@j^5$u;tG%E*X2)YST0a6N7~ zT<+xRpyAGBk;m-kB$8?(qO#D@=Lqv63Hc9_|zJ8xWooUwzY5ko) zw%2WTd%xLY-8}K`IV}$#O|S6a-0RHfe!css(_z-O^FErp*HnbxW^Fpp=)OREq05hd zlO9_{G3#W$U)$3Duj}5Lzyt4Uy7nJ$NwPoVBIN4&c>Z+_Z~n#ubq8A4b7UOa{zB{x z!`b)EGam0z{yFiJInQUEX=mnz=U@2i)ch|nKj$?=%iHOt%O>2|^^ld{ByKa$_1Vd- zF2DFq_Gg_qE%CYd-1QC@vkx51X(dww_Gs|VQFEBEN#)2KZRPbBj-Ky2mMYz5ZnHbq z=zZJ#`vLjAHeYiE)@prpoRw5{!ku4)Yg-3*f!HjW58FPhJsbVuf_BB@SG(C_HsxJ$ z_GBrL{5rU)UtX;(yFw{h9-f{;%#ECY)M2>%w0Xivu-t6B?Of*10?I zKl}Xd(O019LWiZQ#ogv-Vz%y1cyjCmOU6sjuTy5%a6jUC z{qwPF(Hw7S(KEMZI~dMlIM%jKz_9n*vTO2ueyydGUBfx%NhE1~2-ahgdX&2Kc~sSn zQ(UK2X8HbK{idROrm*wXJITctZUq#WcDK!FcZ_<$T6FbIaMgkPUc3>S#%`*NKMJMT z6a3#lb33c{qhj@^w=>JPxuu)mySXZ}Ri8<0ll5}ZRnIQFU7lmOO7L08k*NWvO*Hpd zWc^%uu&(8&m&`>Y@q%ZS_as?ZlZECef8C@Rey&%~GN`L+a({>61+NtE2Q2M1?4BYE zCCm8QJTHiI9GX*hPV)b>hYwa;dx@^CU3J&%kmc81DLNGeuR^y)eUZAnQuOq>KITsv z`#28?EO%OOSN5*9`s}5SI>m(rOj=U&na@8~vGLclnSZD7+hpxy&pQ27s;u=kiRvd# zzO(As;%$y!*UipzzpI^a&x~KkV_uxl_1!Lu6gIql-Ql8kQb$A7onyCd3|sZzFYF#? z6}Bo}Ic+RC*L>d#4`Zcw{Sgy3D4&|4@;Oq)(~vu%+|u^1Wam$kK; zWK?$kh}K+~#CN$6vkXt6T0@xb}HACr{0& zI_=s0;#BJ7?>{?dUDi@NXsp7%Dpv0GPkpBElZuPnpLQ;`esRrX9`~p14WDLj7H>(l*8GJKN9_p3}Pi(7j?Kil4NK~>`D zhR&Y1KW7^qdVYS-*8AH}-nCk^0^J#kwGWA&ZOx?oI{p#+68>_ckh$T%e*~V|2a%v^dKE2*g-;=ez zv~7CK!F=2Aww2p9lk}SpHwc1csi2-uVO7wFr>g`*Zxwc~ zxwxsv)o>b9F?&?>Hdfw3-3@1Vxn0fZ%zGR8fWac~eejXZrPl9XTwLY8dHoN^x$~!U zJ=+$h{eDxTl!Dxn^!C}O`)zvv%RczV8@pR*mz(*Q{%>9x2VYvqZ9f?_DM~anR{wwc zhrKJNUw{49fa&Rz^`d91U+jiSkk!2M{kY5sw>GlZHl$$1KQubyRWhBo2Zt%rrpjd z<{`ODb-4Hae#~CxwQz0pamS^mhrdirPkyVlI`quMHH&P+lE0~RXE7gcO{-sX`@_iu z`J`i!T`A3bx2##zJKO#8tCZ%gms_{UU0tQ^{r|_OmsvlyE`7(b$MyOuzOBLAoz|I8 z|B}cb`19jRKb_BdwX7!}m^$s-=(q0aFY9m9JcZ=Xh@9W^WQqRTo-3QmPP?R=oSU=# z5tHs3Uu%ag`b*3le`_&rQG6csR{zI-$+n#Q>st=h?U79J4|=x#L;3Y>9U+C=x2KnE z^I61{ClORPCz+>N#}6U($meo;QQEVC%W%FeLuC?FLVR9(^mtx z^OxV{{gIn>u2A~%%rEPvvFyAe;oVdtV=Q4Dv%Vs)!!zpP>iZ`p7{6?>7M0a{J-cJ^ zp3GNIIfA9<9$xijVUNVC#2&BZ_w!dsyG?(~ZTcyje_dm^xQJKOPXo87n%jkUO<5P~ zP_Xiz@VwPF0!I5zrLQh}#xW;2U*^ohipPfz&e&jdn6=4@Y3_m2Gq%cCFNc;I-%ehB zaK`6j2mUk8`I*OIlD5U>l79M@ngU;i&_*qdtIq5>p-;j$lpoe-eRI5Jf2V1Mw~bn0 zYR8F8w#81dqH~YC9MoieW_f)}aMsbU|4t`mE_D9+C`JC&m5M0eidFAky{Nsb@orbg z+5cuKA_eYGZwbHNDaP+F?DwT!&s1#6o}lPYFQew)FZ^}YigU9}Yp<^2FCX~_rSq*% z@pH)ft?5meQ=qx`(?1De`@UY`mN>gd&$@i= z3yhw#@(y0x;&3^39*dh**Uo??Zb7+s;`ckp>YNnQycsg#)xiy=@7kZZe`=1ObbkK5 zzqQ}kyB#wucKbO_T=m8`E_dBvlZkWyd8yp zaK${6yd}tN620Kl+gX`H#j8FBb2-nvaAd(z*{eEgnTkn=mn@hwlUI;|Q{{-qA}+@j zHw4spR>UyYEz)24A@%!R$tq1=mG~uIX)0Zp4vBCr>e!$&{nUcS32nC|sy42;KhyWz z5!;2c?u4cB)JCfB+W70-rw;Ez{#y$|z4F4h|F7BArB}au*Pp+?o?SQRZt~CE6=iA` z={NP5jdOXPgTJhxD$%_RWul~zAtNh1} zJ5TBMs!Q*taLha~TkYN}rs{&`e6?S@4_GyFWgEU(HG&Dt*y40@;i-MZ*i-1-a|=RB=DH_kqi?d~=1 zbo+6{GA4CrVR@1^Z|)EG?@9kF?l`@ldQ#E!uTk}*qRXGRJ&f6*xPF>#aohAu&kh&t z{^sBl{&c7Hsu_{#2@!81(_^QmpZo5%>w1zd_vsJXPg#3-3Yo-Xj^$;%&y<<#*Y(`6 z?fAxB#Xb#ru{YW)FYgiKwAsba(QC3M+UMS1?v42#GkKr*eqo-mwPgM4FqvtqrnPn{ zO8G`y;IVlWxS}q8{?56qrJ{$tHoaebq2~P7wC{)FwC-?R2{5Og#5@ zRs0Bh=cBqv$NyJdK;@0ewHG?;^A~U#M>nV4KUll!#$_%WcFsn>-M(XKPYq3U=)>*FowSPWzm>V9^XJOlya_KqZawe9=4Zn!d-g|f&d-&}d#?n4(2)Bu zL62=>%fDN1WIlw7&-lr6W|94W&Ab2CJG^k3+M{(pWWJM4-^1SS88%DOH*c7BZe3`G z?9KmkCvJaw|NOLAk@znStmnm(kErb{Yu*zm&Au!-hp|8HrswmNh|6zf<<>UTm>KOi z6O%6J`F?n7NZibI$M~cUIqKYIysG}>?d@-|NB4hIEW5d3i+WkQ%j~N3H&G@%Z#7@; zvTNA?^^D%{Hy3x?m#tQxyZc${688+5|A|!%E8d;txAFbYk2;XWz`(%31R@w1801j< z-bsnc*{OLc$lHsU8BldMq^W#Wj$>dDoy>?!H`EmHIN095Ww+fnDg4_%pDFph<$@%x zE@Nx!a+C1l$2a`k(q(SjN1sbsw@99O(t?&5DiRD+)_nXmr*>BDhT2JM*Z<3FSn%df zEW)QYA=SoPKk>VKb^I?>6B;mlhxJV#Y(q&sZBn* z?e4XsDU+tG_6^AKTrKF_s_e&!qe z!sypU3(MH)Z}}_Fx{BZax@PUFrFuKoY@aO2*K&V)ZlLx1^zh1^&&p@-*&8)2`0F## zKYNyY9sF?hd(z(6mGAc-cyaans=)hZPv6|#6tF+*@7kSFV(-uP=SJ&YUH`2tc%A;_ zJ)Y;TEMC3m;5&|an|!`z@6w%K{Qu#e8zR{=_C!XS{M+pt8++}iR>i(UI}eNfd4Bp; zknQZy)xJA*vU=A)?~kwf{_SJ=`FlS1_F2|`y!+(m<^1{f|K6RIsJd{-rf+iUzI}5{ zZXaD#lfP@xte^n5??OLnxF;Mx`Q58^^RP)5PNnTzJ_oi8e_!u9j-*>9JsR9HSO z%Q(*R{O0eZZMmOcZmI~rlDbc4_8e}z==kZSTite2c zJz0N#-XGq8GM^1gp1G*SWzAa`W;5Y=>g9c_R&Q(inmGOZYj26T)u-$yuCrUGFTFd$ zAa~V**cAph0*~sn+*Cbnsx#Y{*Er_&yjkv#pRCE4^XYWaUp}eu`p~)NJHDi!f4MOB z&T_sb-S1ah3NY7INAAzeJ}@!%u=9`2sg1GcUPrtA<^Ng4eIz~0V4C{T-(olB#D0EL zc6d(OoINXUT|c`1wva}5wx67RyARA6VA=_FO_+gP!tvzr@;o;rjcuuer|MepKdQY|I^FmzgUr<;I#yr}g?b-tH|4=kMET zQCF}v_w(sT7MZuA>|;Mpvf6gMgpXCPgws{wc$Uk#=mno@T7z#NxqrH-bN`+Y=h-gZ zcbo4%+4*Ic=dIN;JF@rrg+2ZKWLeJoN$Q@V>i%+eEG~@EtFLKpIQ6zot^1mm@&?bm zZZQSl?#O+)Tn)i0XLTnqIA{vhFm+#F8(_xyNVsf!B(2~m)mbK zdrxg))i?eH(*mvhk9%^(X1=cJjbHycb)MXv-QTy~JK%P7z54xUr%>Kkx!fM5#9d44%XE3Ce1lVCe?#0UN&WX_ASqq zNJu|=TCD0u?9>kzBX{2WcGdOG%kt7Vv)1jmd&ORs{mqNLyGHm8>!lA|*N(I2JyaIz zbNb9uXfv~CR?<#|Gl%>y8*E{_%o4Ny0TbW6hm3k`(!Ce;W@mmlmd&V8awOYWY4>U+ zvqz?!-QWK#w^;nWD*W8!)QRiQzhVkwyy_R+_DST2yhp3)2{+HJx#qXUb_5&k^-Vi6 zZ}K#Ybq0%9|Jmj6k3qYB>iX?Jg(42|FpB#;?t0vCgWLM%x7tsicg6glR6KM3L&GWa z#l0WQ^9@>KcA);*vY%&OhNnnwzfxV!`@QE!$=xj%obSkgdnC4@u(>9!?(nnMd)%9D z`z(8EmGY5)(&p*@@p9@L}EO zNozQ@*Q&+OTYh2rvK8yQza;S-xZOSf_{p=Omb<<3mK1GT^-Aev`exlc{hm{k1PyMk z`}9ntJ}AB)lW8eUBe~t#O`S4pNzP>R_8xh>U2fOF4n{NOf2K=F#lrhQg`2qOUa6@@-hN}o>|bj>?rGHCF>}k+ zC3<03OB9H)!hXz+a7#FWm*+b!hE;)9AhG zgF)17CYx*d%Qjs4z}c&=EM)Uyy2RepvvbQc56pEk&Yyevnz=c1xk`#;ZSBiCtHSGd z)8E=$o&~rYU$+LP> zrUzg7^^44d`V|{L`!0Ms=l9XyH;b(G-#xy?(B!KXVYs4UA|uXx8|#hj>|rJRB5mW3-0Na<>_6!@ZyR!_xk_(Ro3`?;FO)5|M!!{ysvX| z|5!M^x+I!^(mYy#+u1R9;f-CtcjTxU3%r}f*|lr!*7IWTyHEP^R%>Njnbb}7GI4g? z+NQq#2V--z_|ZKxElSUx^R;qtNZYt&-np>PoH{Q$+?GVd%V+y$r@g%H5T+4$E^)7H z{_)AKakmm~7lj^}RJ?25(p$4v)u|tv>s)B`VcWS*7Ny!-Cw~6E5y*35wGf}yTU{^i zwQ^CCNnvu#lh(AaY3vPCT={QHn1iuNN8qY19_N}pWsn=!C{G~hUw{5$hs{f&Vi^(EKi@Ryl z9F|w^{`h*?=BKZpt=5;Xy?JrVXQR{GbB~I1PJW%H6tdZ0UEs#^7bp0?yylBI@OtAv zzM8Ao8`sa_t0~>i_h;XJ=Kb$qA9z2P@6W$H*&ojJ?EfxYZ?w0N{c-rN)raH%`2K8M z&s<+(_2KyL(g*&3Wq$<6v;TWi^1#3R)q(K8E%sv>9a=B&HVR5ke?3QRJ8+N1w9Go@m#b5zuw z_l{o4rg|R9T`W?2PW{}bG0k96)0Z79v>DIji*tuWJ`(MdNjfFA{&xOqT@PW`O&Yg- zj)Y0}-06$hzT}-t#HKkb?4NYZ|M&a0czyN9Pw(~rO}aIyxA@D;+Q}rqrS3mG8wqJ|$b1wK99lq|bPyg|Pma<>q}q^y-w3?YF`vFPrC>Tf1dNw`$k# z#d9AhxnJCOe%-{M&a2aJSejNJ-j&|?TW0TOckS>r{z;cvC)`{gZSjF&X8iQZN#{QC zMyYegdmVRbtN8A%|LoN?n}dsEH)&3>nyRTHDS9(?8TaL%@8`tF%FO6~n|V+>d#14Y z5!SFh=fh_&eZeC4!0K+uY+bPfwYl>dwKuXe3+?+z(- zkahE(lcM6~Aamw6d$H_n--yrlh3iTtmdvbEtC+gJ^uXgKrsmOSj+I5sJizwpsKw5c zufL{jjotRNCEwOmFGHSBulICotxunmeLq#nI?+z)P?QcJ}@WJf4 zyLj)aoD?)lcd-9^HZ#k3vDnWuJJ%}2WeaM43U67r>igtRD)Y^{=D2>}EAQLRa{K1V zeScLywl8!)zn}ln;fCas-;5%r8%+>+F30Ssc<76@QU19bdrvOb|EBd+J#tC3T;k6B z!hO55-B>m;US4nY(#iUTVwC%*`<*X1uKp2MmbUzv&~m%^_nwefTcwwWD*rf;*(~x! zW$GKJ2RDq^eWotW@T%rmusk(8>OM^J=98rW28wfz?R#HKI4Q(J{gq)u^41zQ-*&h%8&R;o;m%xG=5S`fY9 z+4c*ci>7&OpZdh}+J+M!i^UfDZp@r}eY@w)OIt2oyx-)ucA*m6`t29)gjxp{>@_@D zrtstblNT=n12=otb-tQ4ZP(15hh2{PxIWudwI+Fu|JMa}-+aGze&srS`;6Lkzjc3~ zX|2ClG3!;8kHkX%3v=!)w36#Oy7OY%*{b^+g%2GpoNHtEx6tR1-aNmw@Ta8{8NwfF z&T{fT5)qc+wb)JV=mQ%U*{h3Ha_&5M=-9?z!0~?OeyJ}fcjl;7{*?RGePTM_VdjKy z!kMZuI$~U}l$u%1z0Pm${`@D2rT$-X$-k!$=j+MI$`!&*qqaHKXU9zWwoOraS*TMlCWvTbt^o`NwhV+xf-+ zxC>8a7(c61+$FN=PO0?4OR`q72F)4wcmDr3Ep6+|QOKs@z8T@+Z$CS?`$U7k}Ys`pB@^*(YZ4 zfBtvRmEU%mdQJbSTf%Zf+*4w;(DUPyjeQQ+KJxMq3TyAol}~csH$kcOQM=p(p4Uv@ zJRUq#*devG-@16}Z-qGg@FR-#kczFTjf zFzJ9o|E>Q$8&(Ing$GPMb#sbb^x5OBj^3p*!sm8>_;_fyF#fe&@kFtuBl=3%Yc(n&9_&c`n6u-L7eu{d6MC!8_u&O+x!;r zD>(iA=EjHjSRG$GRM>s+|FEm%pm5I9WzT#L{{8*^>-WunWZz^~KI+j7FXt~Fwc0F}kXt(`c$&!7GB8zn@bf*WG=-fGUu!~RjYLgtx*W6|P%SCqeuU@^X zm+!UXqc7q7?mxYECQQ)uJY2SwZ)4;0x0jbZU--)-k6WDKPj>IQgLU6Oq|E$be%C01 zd)|gBtC`8J;`^*tPk+7lM@Y)VQx0>EW>l{c&i|boadBODMA=92#nZl)nY!wR?tFGO zP(JRW#O-@(=TB_gb3}E^$|xSQiTW445;y9k>q^e+KlyQciq5)^N+l=F8>wdg`b$W=wr(`mDNBO^ucFVPQre&&d({f85FY4R0& z&KF-UTwMNXFYe{TJR+QSFe+}O-X*C zX>MA^;=S|weOm2K+}r8Y7o)sAU_r&P=IyJ3_OM&!yD8r2sAGGzAd~sm)1z7y))IOu zrn5M97AxLUUC_fW!10w$HtO2uEWY;k$s+gU{>>H&{rFkorD zkD*uVmMiP)7-irl!34Glbm1-5H zoB#jExZ&4FefzbSH;6OdSipSOqh#Xq3BRk2_^(DU3ck82NxAgLl9&Tr@j6^N^(-;J z_C8BGcyXrqKACM&K`GUO##`ns3c7bRIf!>fyfuF-SHtU>URh?}{8&8>*l_&Ii`&oK z_2taIs<;D@%qcrLck}p9nEYm1_=SL8zRRbg3YwM+OKn*Fqr3Ksowi?XRE4CrqiTl0 z;^!vf)4njzT{>^k+e6C&XMa*L6WgjD?d4eKyU4pnfl*dqiKWL*73F=}d5PyI=*h?w zsPnj8WM39H^)-{TSYoml!<4fAt^f9{{VX}hI_}fwce~?_?|8nCSlZ~*`!hz4A?xyl z9o6iqlJ@sooqoLxQD$0OQh)c*v4}a=7v)dzo5+?cXubNJSzmSHv$0To|C3&p!adB3 zqM3I}Fu2(*+wgd^tG7s-l1QS0+Vvl+Zdw?zdGyYWa&zD8mm+Ia)vBV)^vm3MBBLey zloJ;UvS<98l-Yjl2ivck$Mg?7@U4BV^>#(`^ya>`d*oSr+HSC3eJ<~I<7uL8U(Y@( z34_V9JnM?RUNb#uRI8HK`A1Wo8C~a{x#XUVV6+-wvU&cx5)hpS?(m!V%L4@ z5BJ1(WsAPQdKulhr}cWLh}CI>pj*#375pwv?XZ*Nm+&hN)|>J#=}P{Yj%P8)`Qn!w z&)DE68g@SYorBNo%4YS&fnL2!4QDgGvy5u^z4&9(YKfx9^9~DWvYd4&KGVVTNOSTb zTYmRlTim?Vl>hAgD0=8y;V$>nZsn$@KTNtHc6w^s)(h7-CLiOA7TEo2#@c6rHy59t zZj+QJb^Ume$4QTfF1`By87-XO{?FG^JoG80;=l-7F6&onHIXQ^GEB90-wHik6VIkwQRRtHM0NrA<`pj0lL?;%5t*2fYuH2rz>%@nI zi#?AoPR@1sxNf5JvWMGE<3IYIHk~PU?S@#JW8k+9#r!iwRN}QHe3DqtpIHC7sPpaS zPW5RIv?VfZxsQdL9oyNtch8ZUl@;|<4`-|lujy`%={8@lU7zD&5g__^_pCSOvt!lw zlzcmT`|@-D-|0Kvb0n7XtW?vwcH66PamxQAb-$8&Zcp5O!0fx(#v7&&_xbGdo4xdl ztX_KaPtLjzP6=+4eX`aBT;bL)GOH=$^;&gK^JK#1`b!4aFIj&}{qJOTElhvqFZDeh z{$+Dx|9(rWzIb2Yphd>POB1ECxS}S8pWW$R+17Aj-pu(AFaBN@bAI*gHs1|vpKrc> zTH2)PlV@zZQBbe+#SjPIHBNiFbQw9KE%(3Je7|_M#eRduWy;wJwU>WA&HSENaVQ`y-pg*)}TXcbGH!2Y+L6xYD1a&R8BAsefhDr}_6yP6zg^ zzxlV7ch{?T%bSBAUu>7RTzjDSE3f}X=fx@~6xTZMlUpfgcSVMKz1w>B_+;yg``3R6Vo*{B}-snw`uwRo^2nY@1y44nFtdTJcO{)7;#|4~CnJ58dr6 zymiSTX5+t^zc;UTu9mH~R(n19{6WQ8-z+8A=RZ_`x*@Aode;i|e>bbw@J{^r^7y+4 z!nSec_b2S&QoQ}*Pp_0v+-qrHp0^Ga7KeoO@BfOvHm&B_#`EEUHx{ia3@ZPlw&hKv z%i3pNlMYSp`W$m*cGk|w-@Vg{szQSTE^g49eTI2nOHZKh<<37_Epz!aUI_my)K{|I z%BS%%^ASgbR7O`8kHxb`X}PnPYiOt~d#0x$VQ@t2J!5cAqDTCaHSCJ<_xmo*zor?v zeTFjMmmq&H`w~&ba84boqN}@hIQHH9@i;#I@;To6&9CR^Om2H9q_tz3ot&_TS%t%_ z&7#^T^0eZ_CY0~X>zVqBKfh##m;kSC#pfiqhp`4K(<1r;Ys6%wC9JlEJkBwaHF{;| z&bQxwTY~1ADKFxhz8V@j&ApSLo_Ff$q%>v8PI<0lpDuiB(79nUmG$ki2{H!3D+3&_ zUoRGs5Fwa8Q3HqF|3c3-;-`3bN{_&Y0<<}whM%`=E=d>Gk zoz}+nLN<+h%Gz?Ax;s@?Y+D-}vfN2|qm*{3pU#%bw@eMK48k!t-!@uDtn9Xnd~UWk z-at2C(w3f>CvKW+ncqLzwr^QY1mAHX7uEL-?=MbbQxP_fjqkUsvFVt-A@af%2eFs! zf9Y`H}*8WvSsI3+twE8@Ipk4?_1g& z78}!tHifUh%zA#-DeOX6%C|Sy-^Kj$IuiKYHekI)se0@bFNuWM@2_Q;RxJIZyy?x# z&v#hsHfldQ&Bt)=+_#n=TB%DX{GGqce;#AKTCGu@;v-RpH{vSJ>RMOMe7oe3F8jq? zSKn~vq>42Uc3zw#(aW)Psai~$$7II}mZGQG8p)|AcjkKdHdY_&y3-_J^EK?{@`Sll zjc*0$7?dV1T5S60ul9oCij(`)FW+u?WMIvC?#YsjgH4uua!YowZ{D2v&ckYox6sKo zJ5R~)JQyAz)%>~PwC_vFSH0`pcpe>Ko|$W2+_TXw(%o&wv(wY2E3utwW;mg8XYD?p zJ9o7nyL`Vc^2#z$VO`hvKPULkSga2GXfAm8wriH(cX^SO^>b<-RLL72?|XWkr}e~> zgbwR9SspuoM!ubO?}BD$&I!?+Epqc0-Q!4cE>o!9cou%KQk{ojP!QNM)t+WQ$Sk4R=ZyxMr#zWJt$4^Jw0TjQf}a6w0#NxGrP z>ogsi$LBBnn{i9kIlaxB?dGZ#jJB*%d!n9x-R=K&591YkrtJr7+Y+*8# zv0Bk7-fx0_Ysp+lIoa+VlT_t)M}gDW=<39K?Zr1*cEyTE&3}8PW%K34U7gRiG2X0A zS-RTTaMPhbk!k<*kFEJr)MV!FvsR)&$4z3t;bMi_gFcnVcD&1J6wy!EXmG!n>+c3z z(JyZ%^K6aqKQgC0DX5h<#z^bf#;4*tej7>b^(wHCy}97-#VucRpR5qw-Foq&%^XF+ zOhI7|%_*OK-E$RkCAXix6v-RD`sFvn&?Q}qj%)w9t+uDb>q~9OlXKSjQuT3Lubn^j z-HmT48}l0NH<3EKS6c_)_GBscTzOrRNt(Iu@Up`@U#bMJTzal*g3P~KOS>i8)h{l0 zV|(y#QOuut%wZ=dNnKs3SyyMYPh#%>|Bt)d*)wl+FN=C+n)V|%fBKS%%~}-}FW%mH z{h;xx`nubfS6_dg+%xy;&BeE`ihMp(Zd%vTKF3*8G~q;Lc9klBRn^mwp2s~~qDlA@8>GBvi_SKHh02>ji!lpVLRV!TDNWX#l_8QZt9#an<95S&+lV% z%Z#U=e`=JhoT7PY?$+gN*6%Lc`;YIoYJ2%i-vc6y0;@i*(ePgSq*$Xq|3%F!_bcD6 zd={=P_)(GV?G(4Yv&?*#$XksP%Pm)$^_Iu!@0v5|_k!1g&E zEDTcnE3vU?)wSH6_uBaOYE4pZIJ)i2Hp!-h^w)Dj0wtRHpC;}SKN0A!9eiA4$%bgF ze~+8qNnTv`)b!qjz_T+xB<)@@o%iO3;^$TxR#s|%ScKMo>J0yow05^hO3$QSDur*a zE=_${=)f_z`#A^I0pIo%;EcPT$lC+qLqNTIACu8L5`xmG`{U%nk^5n}3k~ z!ME%%1Ixbsf7L_I%B9Aq?tj1NZNS!kdDq2Tk{!Bdd7PJz{g8BAaKb8%dl%w4y{+~i zWtV>(V8}Kvr=WC}?H^99-nvu&p4VG@C`XouPJVdkhUwjp&K%3SSw2nokU64u+$l|# z;pnQ#&aDL}i+xrcm>PKf+lwDcQNeFgoS!8+B<=Zk<&TY@-3<9X>p8!7Stop+kA;Y?tG)h)=HIUD3B@=J08HFB9sg zPS=v_eZ1YIY|hNQby~8Yu5Q;aIKSp;o;ruITD8LOQ^HI24O`9yJ(2ZHD7BI`VsyB7 z;gM>0%(=zEMJ`LPublkDXW=TVRGVKdU5|85E5&8bTavu`cHQF*i#M=8Dz%K_$;o@U z>h$H?iR`f_PY3PY_-yTs4(rm(@)K;?bmUgDOs?_*4ClGeXq|r=P#TeaeEx_wlzM_mwl-zIe=$4s>g9mu3GZD?e+u|DnABit@8d zN)MIPNF4m&sPprs`X6(PAIH-3%1n%cY6^ zst%J<0_r38E9xH(X?(fyAn!?wNu@Izeotd-p3(JXL-X1XMWr^2FW&k!XTP-By0d3? z^Q|nj{<4(orrQHHmZE~UU$pj!oKSrcG_kI*~Xsz z)&*@xclMm=%8a$GU~lV8yCc?zBV!%-OLurB@t2&-+Lj^1^b>DF&=LrYRzInW?`Vbdhz*9W++@e?jAC&UvFa>bab`)OZ%E39yh{w}J080nZR@Z26zv>zO|q^fSM*7p z3Rm=O;ltGyAxV!utyY>ZzSZbjY!Qp7w!pfbhbQgu$_l$%9B_ndS)ihzm9o*#YW}!6 zCpdMR9P@Yd>MtvEcrsPI^+l`asggd)lT}YH+@38zZ$`txoqe@i1C}?3`~S;YpFQ&w zuUGV|CXZ(_-+I%S^le`8o#dYR$KvLPg*P>O3Ul$M)y!_3WXB zC2D8i?mBz_bXnKSA4-}WADMGczKj*Nns)4>-;=u@{aSM0&(58qw@m%O9m_`ty zYsSe*rm8w_%D+4&NLa;@QfRjU8pRT-Y0Tv9Csc*^jz^o@5zf3zpik8yTO`azcusk zxru8&mK?jO_t@v)u#F9+sG%d)#k0YLJ#|^pzcqHidQMweLV1d+kVUcMtfv8x*v=yeRqY$TFIZc z%&%wL!_avv_rjG>o5CBX(OgdD%(Z;*}gh1%QSb8%z1M;De`WL+}Dc* zQ}*+PSpR95`=?`yTuK$^8sP;tGGY8G8hqCc%kBUEy}5E>`9#x1&25jbacs-_;_|G- z-^g;#m4lD2S#wa;23oN+$K(grnhW%x`Y%m(uQCG`H%=<~KnX zF0SX@Jkw`Yxq8ZrP8b`kZTh@~_?Da<0bv2ao@nyx6Z&eDN@6&`t?`<=tN4 zMw2`0XYG*cU2=F@(|U8&=B(T|C8iIO?ds0H`pR$QT%#EiKIy<~N6V6w%`$z@HK+MG z_ZF>~+59ZG-lb4i;_uoWOGP$6ep1M-e&yvh-52G5n$8qScK+rSIjeJDM96*Z^>Bqv zp>_%jWh@;xAGv+1%~zvvqe+U8<@#-xpT1QzH`;z|QPk~+`Ij=+^%dH@@-9hP6rEFW>eo`qumhk5ArMR_*1W_&Z~Mmycn&cucshTu!Egb7oB0 z%VWE0r)`zo`}l$Dy>P?O@+m$tjSau~?==3FUn95lT~X0A#@8C@ZvSK+?Y?fD_xH2m z0ngl~>O*(Ll35cMh04xRd0260$wY@m0cuNMEw~@RB+M<~CU|hq8Z}!1d9RB$Z1o$O z&1`l(<#{h8x8y;H_m=Kk>7hB3zZ{rx`>WRa=j|EM2L4YJ8vZGG_x$kQmbHif#1Fs#bTNx*wd}wc3_3_is z)9+t=dQuRa*JZDMJ8ibUWzg9LaXVJYcf~%w{Ehh(A9t^xg{5iaui^u#X6KK7-eR&s z^mJa`p>txI?{&5=H)CY5N#HN2nsV!UjFES}{)ErL5h}mRE*&cCk3F8D<#a)B?ZfU< zyr+IUl>RC36qLz!-7FmHsdPJGL4AmY+zC;(*X3Q?J|1{_swDD$u7dXTgA7}a3LceQ zwzTy~+X?d@p3ZOles{^SP*M4^>$BE7Un^gEwoEHmh9{3v?6d3wX-lyM zYfjY{3B0`feM;HKdoKNDj%EK!t|T_QEr0d-#~bx)49||1R9h~195wF!peOU@ zE}eDCPja7CwUfib4T`U3d;hST=y`Ef!R!OfV&`U?%O5V;>h@YeY4TcmSLFwMFYZ6x zH0#;vjPEBduG2g-Z;$3H=gjMGemJ`&OLg)rTm0PrP~7Ep{dcDAIXi#C{F4_a%hxZ_ zFL;!hE^>Q*)6RrH(cDt^Isea{ERfWB?B?RF+hRC{_DEkW-FDbVfK}_to~s8f&P%*# z%+)Qp{Z04W;?m8fln7Z1kwfkJ;t!dh;uFSiwC)O9AC14!=F6^RfN7BOM z`fHN?k}e9{#5qj%&Ho~FuC(dsreiO+D*L#)T)NS7H!{@m)x50ZD}U%tEpgah%jy08 z!|FGur@Rn~?@WKNM6WJ)7jNAg{+TmH_G_(=mUbvteaRzIRBB&u=@@(Nq-V(Y$MtW% zHa%Qjag1O8=5~RE`SqnW8xQa1u)DnL#C=!qpS%Y`mL~Te6xeiLc9*)=Iq&4RN!im+ z`F6};2)@3cM&Pc{@ardLBqS$9A&-=f)`0@Pr zIec49-DYak-JN#8J5qn;`sN=|i}!rvQG4_!{on?hJ|{8jx9OkCj_}7l^(ni3LRWdE zn6>BSy>3(4nyNzrKIz7j#qv-j>*a_9{9}9!_t^ZaL&oKG%^vus$ zJnwhc9iJ@|CmX?O`6(hq*lOMimOP121l0B}I`k;! zsF``vey3$IkK7hqobe}uyGbi`gRiq$;`b*ig0nBL-qu=LQDU+_&6h3xS>?>dM*lLl z&3*pm{6eKghb=$I{%C$HKlR1pIk8(;KW+YSWr>}ig@M|s8*OK%$Se-MvSRaQ?w>lp zOypvcR*Iimp||DXffE9LaZ9bA|GlF>g~#3h(HDnchq%ml;RXwH&MZw14tI#ml`Mai zaJ_EDE4Bl?#*fNa%paI+)BK`p`(gLZu&a~IocFIjn``{D<=v`RdDE|6cDx(bnjHIo zT}G%_cZ>5C8B&Lw`r{E zxM93t?ceTIf5rGOs4V2k;N#3{{Icbw#R;#rtUH0fSY10gV-ios_^ZEcQ1V^F*~7K= z!A`?_oboBkRRYqg;Ty$n#&_rX3LfYe-e~HvFXEZ+Z`Yu+X}4!NO<3R@QPslk7b_^X zT0-p7mnH5ys*X;#J3ag4vaAUwv)8Xwwp*uk_F?m6clNlNNuGz^bVfMyx9PBWyu5Jj zpwEHpDW-e*|8~FJ9>-9yEy}E%Gob4KzrXo`&Z&>G88=t``m=NLRy}{cMEG`>W9*%pHHlHH9O*_wI=SWSnhi!@^4g8 zSZ~QT09o2Ua2}h_cI%+rb*=OgXX07Xb?tiAYDSv9~Ud%ncw(>&i!`zb# z{#x42ZL9BI;B+@~U(=ezDL2oE-n+Y|*imk;e^2?9#X${M{}!!1d?s^Uqnu;3QSzeb zP~qMykD|Qz^VdXa>{FC8JjumaA-hr3Q$2K1(@BF674>IFr(9`YTofa!edXlklcKV* z%UkapTzfM3@CjattM=WSG-j}de3<;m>!>o{h4Qw><%a~0YRMiv;^X3OkUV|wQLnm1 zZ?gHFb@^U5F&eCWY+>3LFSqW+$`47i*X&v-JfBNsW8U`VOCse&;&m>4T$!}z?p-#q z{oB^%%z3EO$uOt0be8MHJv(Hk{5;TgQ)pJ;#8(CH=CaGo3TP}^xYK{$L-v!Kzi$*| zfAaLm0j`;g`IH5NC%>ItX_0h5z^}0U{iCXe4ej3)PdXZ`Qkr7E>Fbq(pBYM=&z@WT zWzjjFuTlK>rFT-zyV6PX!w&mSelfH1Mcaw{Cso#b_?7&r_)l5vLm#z|aUJhH*e4i% z_I|v)$DDWNX3Yn}>Z$7%m#*vIeY#ca#k9HSUWvVT+4!*V)4CnDko#d> zZqQ-XOq3N|c5~G=wXa!*+w&eeanAizp_-GhdQ*vJZ}~hAr}G=4QgZHp-mV@VYN@>` z|K2>i`l|EEyUkTr98mN%x#tz1T0T)lSTQazv^XL<(0Gx|+xy2OwQ2<3US!%Vwt075xTg}%OdCHr#HdM?lGmBPCdI{34gH*m__$!$5mvf|Vw%Poth{}U5@dU#vz${Dv? z8fyzb{=7at|J<(iC(HfV!``nsyZzC-q6snI4de+t#8KAN@VUEz6d$TSmoyqywwL?>L zUQ2QAswD?4G_Kv^`1aes@=EY(X2lbZ+8QngEGIOhUu619t(%4tz!5# zw{z-lb!U-(HM2KMZo0a2hW+QEW+~r-gHAF`_B}r@-~77o%bZunI-zrvPd#Bi6j}CS7t7mU zUWEdib`=}k<-K=e-lcatZ(Lorq~K9S_=efrzm_dKYJFrv+>N#8LM&I*S9@&S$oF)e z%WnUg^%+Hzzs#;#r?h;^oT>iNsmtUfw@sQar5tp4mY&%U)@kO?w7O+|7E3Sc+Aele zH2M9r%DWqr^`6)4?=_qk==y$>!tq@eSKhAZ&-HVz$vI$Wc)xyrM3AuI*AwS#4`x+Z z9x1W<_A&72!Y|4`Y-yJYnro9DxkYtloRJB8vc=SITHY16X0!dD3-4u^%N%7F%va<4 zzxd0~%O^iy&X2RN``gd??5M|aoud&6d=_4tx3d(i=HWY`?8YPQr05<{XCM>GXdD0F zkNvTuHrJTmFEB8gc0{TwFttLYu=SRsgXe~OPfRByZIn@0Gqre_p}?{2*c_ctMV-nn z%0?C^Bi|G}__2jYsbcXS=Rb3=vNb;_nX{t&9aA}zTtq)#Q_;VBEzeyZ)#Y!T>+$aP ziRHHq-QO%`_miD6FRWMH{%Pr2vs>FZePkcxhRB;m2%1L3r>V<7ySqL3y(@32#_3(# zb6zf~SKsnlWa;K6cEA+dOlt{?nJruq@{bOOC;# zfX2H{UPm{@OC6V&vX2dXTlh^!E+soH|Iwk+_2A+b|83PeA2og( z_2&Fu>9K%!%Fhq)EOwrI@S^Xs(xKzK3ya>iwr&ic%JuWdMHPSJz}Y6XcN9~E%NoCZ zOLJLV$`dd@VVQEDPIS2Tjm6rAyALfhWXRYQySVbg(x>Yz&u8rHdXn^Ua{i}f8+J!U z#&7pBN}oL2r0w8~>V9~VM+F#;NOn+k#wf5*nN1b?;X}{^D4paTTHzQVCd1ghs?8~eV{ad*|CO%p}GKePOtsP-Y@6Ms-M>_`#SBp-QLCf zx+m{7d!H7z`0U9IWeE#2c`htVI6LiT-kZB=Qg>tCC@*HS@Dux1d0~FJn4>0#0_#q8 zw}LM1>Qg@%^p!4qiLIIx*%tO!sMf47@j7epi!cl3W4$LdOIkxEe>~1woSc`}c)>ue z`1D1S+q?Gc(aG9XJNne7@@-eY zpS_wpfxVmkllZ#pPctP`MC+Tb%uq0$RH>>q^Tdg#ia#56{K}X(QQ$m#)(eZ@@4NeZ zy*;DC<#((;H0>bQKCAtrtLA@`K2ml!;ERTpjq@SKEQS~7pKsYba~u2LSIk*?x5{oX zAKen8)wV+P@1%?3ce;X3aXnKKI{49g>&fJq^D=rBls#1D#%|QxtsFZk_3!obbvD(1 zD?a`BTAshJPvZLQvJ0kBRjU_8thmT)sU_twG1^kpGIBcS#FbZc=FZp>vq$xUVfe+E zDu*W*OA9wMFWuq2XZf1&Q}=U1mdEd}ss8rr?addo>7E5&^MZ0MGaLRM zKW_NgK56&z)uQ*M9>)Fh(#y#7-+E)E(Sa9&iDG55BCqDe|J%Ul+G3H}FJ?Gfz$mZd zNkCWZzMr)VH~YAV8|2#6D)}7xe?|Qd|}n5pPP^L<{c}! zVyq>U{*JpOXqmyI&bzv&W#5_d?zq@_;pu_hULj`YrA%ojn-}?I+<%~BCF8SjOLK}> zf66Y-qhE!3rGKuR@_b_Og0=6qJU%{YMO2lX=)S3U9kz$ut$Dt($iew{tmFIE<_R^f zZtl-QnYQ1Okx+he>FE+?!6$QqzrT-F(V2c@eO=$VpDWF)e?6a>@OsL;rS;2rXWcq1 z6MT|WYr$iSo&F2Y-4ya_I=edH*Z1A4SMSa&m3p&>r!1}E_0JXE^Dg>My}N8rqj>Wg zQ_)9@Pjl>TH!92E)-_F$_%Na9ysgc{aJ%y5K85doI{y|spr%wVSu6d9FY#T{oQlVN zbK{kEH$C3AadzpgzjqH>mPb}DX3IDpvXW2h;EEFU@0Z_8A3CpRb>np2&9}ezU3*!$ zquk-PZ1Kd|zOUCM->WX?IimE*_MKq&n%brLU*8x_?0%|nxFW*d?%?j~lbmjn8U|MR z`_4={?o|;La`mFgdQH9Aq0*60Qd<|DssHUzSLg2^UvCrCYIx0ZooG+n&9E@j-5YA` ze;;3+zH6Cm{tr&py9WaqQos!y)lChXy|PJVZM%Z$*9 z_)W3vxhLG3b!QrjXn${{<}EwTWN5)j6C9!{B{Cqy%S&PO|*XXG{{Z&>k`W?Q@_62@#IQqq5sBJJZD@3CpiQ>3cTUmulUdE z^sC$(nSMvlzFo5|`QxsKm9do{KCgLYt(*Ab>Vl)U94(Jbjaol_UPI-hKcDz0Zod}9C-B}c=EhFDn^#u8E~(J3*SB3{TPb?I z_)PlZwzn~SthZhqlL%CwlFfLfF?nnAFOIqiHqO;PQ&)zHvA?ZIbB;gz{l-q#Bk3IT z+890Mc8Q1^zUfdA=zrHV_tZ35t{4F$zj*btI_wuWrcG8ADG#45CeClYN<=AjQa(S| zb3u(6E7xXj)1F#CLHK>b6ZNjb>mgbW9LF-3TvFh3PR?5u^Yx+6=6Pi=|D5(%(|qpF z!M$8&>b5!Tzslq;tZd)yzjmW?R)~1Bp2goc3+k`@EpnM~@xhnl@8?fEaqd@kS8V4( zwdV(_E%$m?a&8iNs@3(){Hww$9q)pD4|_UO!b0^dlwx9axU7mh7s~Q{6W=2n{!^^n z*FEMk&(TZk@9(Mk{p;lBldq@ipWnAk#Us`Hme*v?YirH7&CD&ibZPxdhHdrMWm`8M zoo*df`RUu&*XjD_)Gpat{CRsdYR#^6`J~<}myKLXB(E%Aem<_Y`q!VxdUvsDQazKC zj{hlnbAHuMmDS?k4?S&ea14F;KFe(SW!niZ8J$_mUs)!9n7mIrFMn0lI@wp70;E^l z1$NuLNWG(0eJ^vSzV)2${yrV+dw()R`g#BKMOJ?O^`-rI`fksZ&Bm|U-0Jws{C_7e zSGa%S#-5zV7Z-kX%v$iQvLe5_`qkshj+bUE;QutYEA?W9j>wc64SS{1@a~k|Zf4un z)VQW_|3Ad{`C{?;roS=sH_Ty4znyt`+S{-vlXoTDXR*3-SZ^DkVOb5y?2zUJrM*<$zB8nDgW#(K_U^^VrZ^?UcIawr7daJ!_M$}>l= zTU7n3N8R19DUVN|bIW@WX*xBj<*?CXQ=K+;{ns}q<&~`OdO9h(y=qRw)-9`N8?3NV ziVr?yr{cCR#`>1urNt4kDoY>aZapgSYEI+Zy+Kb@-)QA%+nziU>m9nnBXv)*J40{! z$Ch*HC9X9yFU-61VczK^wxeyV)2yujWd=^<6Pvki{@TBcuV*e0nAWxZW7*yXHS!L* zYtoiB8U-GBs5|}4 zfmyP176d$4_j+w*7t6y{SqXWnN~^C)WK^G-@y*OvMWeQOtIoaj9dEiOzOB2X+?cic z&6C8Ud9$Wn=Dt5Qdw19wIR#hI{RjGWo%zo0nK!eoe!_+xO2np z;JP~=o?e@Lyi!dfCFEUyo>b7ey>DXrALkUdzVhtArs`&aud)HZVm2~;kyYP&gPWnI zn_t_xp zLZwBIENg0A`J@`l8xx;Rz5c~t)AZlRb zj_)0vlc!~`7QH5ZWbIiNVIh|-X{T3JZC|>5hA-Q^E87 zhlbxit-YzOz4-qgm-*+}4(sjJzHvp@ry*7O=bYLlBKEoGSS!V!$Hzr1TW=9rrt~<)#q7DN8fVd($`ao@3w5o2iOlZaU0wd|xW|q4Qg0e}M+Hy5 z9^109eEP&PZtm)a&xh43_Z^Zw=>07A=$E6{`-;L{?T))N?LKvopN-H z^*&j9n{L=BH&VJU&Nk~2 zueqMupTz#m$y@c>!U&c>Rzh8U)6_(_HU}9^mk5b86tNCIo|BQfQ}io?&$Ri9r~FM% zPp_LWzd(M$vZ|!t83z9fRxFaeydX2QMt=F6Akj?ymRCt=h2m03$`{{ ztrTpZH+h+g=S+33Rl>1nSLqsy1noL~U=dnBA3-B(+&?!AC*Pt-S6%*UjC!=RJE^aLWDvwr>YC!a~mQpDfhjJy6Q{ z_>tU}XWKHnLuco=9X1U8l08ZB`Oy+?p(opQ!WR2+?3|i(Kw>hhzpRM5XB4Nz-jia_ zTdsa6JKv~y$VabBZ~BRf1+q(v!V+~tH#@KGtT<|#y!q>aXKr0r3m?7h5pvSs>?Xd` zr+&holZJ}#76v_*T+JS$7AYEQSh+XWw{!uanmC#@*fCRw&=Ak{$9S_xk5I%S*4bp8Xp> zBhw;wL8GVF=e27c1FpWzn(=j);*zDgd!Jg_uYF(M;NSbQV~&o90N>MvwP`m5gO2X# zzQcR;eTzZz0j=W-FNB&pKU#h}%-8ppv*Y;W$B|oDm`^$DUcK{jijj!1_r-bc(|+hJ za4a?No*nKam2DFqT&1^bM?-L8?A-~c<}QA+Bu?<11{G19T$sqmdD7fn7>5&>QU2 zn$h`q?XG#6lk^j&ug>4^@gT-vt%h8&o^5}_l#L6fYVe5Ve=OkKI%#Qj?AnJ5wk^FQ z;E);qv$S*8XX&T|mm^l?t?*pq?AAW}wcuNaOLNtV-;1T(Q&J3kYbQ4C5cB4{{`r%` z_3zL5=OOHM^CN?bxYJt2)Xjo_{?t_bEM`$-kN0s)Wm9D>^t0y|xP5f}ZJ{#dJ>o5m zw)b+Tu{PFxE^f)PGSNJKQrOBhq5j^3h<*;fdfi)*(_*GN95moRvmq>0)>)@s?CCCx zJuLgSvgv%en08~o*{mu7UpwwbOp(2l~+6u4d8KERQEn?x?#@-=D9O}wK(m( zduWD^=x*kGqd1Z7n^&H8z1O^$6}e`W`=iR5lt-WB{!V%I=q+o?KpYWUeSRie1( z^c4LxbI&-`D74Ju@QFAFH`C758$;)8m20rDN%^gObkz^3ULJ)N^QHK_HFO%kG91ws zi&$f>7COsLZc?R7sH(EXqV%7F&$iofn2U2SFUnc;F=FDO^0c!Y=M#2sPO+IV=hlpi z)7A!EO>FX!nZiEZQs_xT8$Nq~o_xC(>meCg3xhtdONbsZ(^%G5XC#F{h zO`jf~F||hcUFAHsclw?xeHwK=OlR(J-uS#MWmEk$>G-GR|6cq^S~Jz?i*m^B3(4O( z82hVKCv7iy*A)DI{q04H(zbV=M|Wi>5KP5U-U=FESmYs;izA$k>K*Z@%LW2 z9$uwyl6;kI){L*}KWCOaw0ED{{Ahb_z}d1*8O1TzT{knTxoXK>%kas!{P1Ey+$3Su z-Y>_x%A1w!&K+i&Zd3hg1M~DJ^TPkAt-HH;2j|gWLhEmcXU+Kky#DX|*f$@as8k*~ zDE{Qw;>q#v*1I0;KbyK+a@BE-@)KN(<~G&`DyfEv-1)NF?5*DS{NVY$Cj~d{l9*e) z;$QC)*1py+(wr-5Za;T-+`BsD)@lEb#x7Ma?3`*3soB5VJ4?oE{r~rQuYRvt6#M#l zyt2X@&!YWDt$zAmKQZ@7?*%!9ym&K{;)rM0K0o?ir7xS^G%0v%mFuHMgG7btvd6X^ z=288UpZ84SZ1|M7rI|VFlYe|%!@;h^c4Vquc>V1@uj>z|DJf2P+B(;G{mOJ(uXz~) zOizXP`u|{bxhv{7rR3mTmiJ6icMao@t~=nO1(hq453x&_DZE?~_>c zc(`jjbN4@z@Z1^!Af=B&9nvpOsAVsFzfO^cnU_NZ^X z6X~>Lug(QC2Du&k7JA+P<#w8>-RYs@j&%HgjG1o!yy7iLHJz=E>9>>28-{G;DpG)hj!6K*TR?gKr z+mg!NeeOt_8pXK#O8fe%E)=+WSW~AiXv-$s91*_nQ4CVyUe)g^b9s|Zd)hZT$Y~#V zY>}z0Cw#J?JwzZ`;lrNkl`MUX%$6OimN(x$@m0mHF3L-*yghb>l-&6RXIiCZ88eAJ z;{I)LN%@@k%xw2p3uC_eoa)uxvBpNbX34Gv&t|?#(YKTR-nlR-!|&%>o0^Re|L=E7 znRR*28CiF;KSzudG*snz&uXrXynZL))_y&aa{kCUc?UfYl)dO%H}7QXv6xN2Rtf7} znYNboY*%9DB_+8@#}6JTS^VY5wf`-W4paJ9$hm5lopci8`law8Y6gES&!x);eL9Zq z_pXrAX6bp$$ZP%M?BAzm{MV$T7taux8{qU`;#!A(5@QRa(X_eqO?EA-O<&%4dY06e zAB$6ag%T&Ontrb;W|O3@)9sCN(|-kB-SW85<#}K2&7DRsZ&^9LKF9jSh`VztXQ|}` zzffbj*B!Ip>lllw2Zk1J3BNI4R%g#?pW}1$uCwRzi#MmqeKaiSuf6z1^)pYaZg;0U z)5_$=T62a!B`o##8P{pgkl)nR;jroeZ`3k_n3rn}&NWV*a`11-$}G7Q)6O-@JV_7z zN`=pEt#!YCEyG^zU}eyI4_nE7zh4<^-q8M1u6|FrN#1Yy)uJUeKANqQ9p-Jn<)g`G z+T>CxGjVpS)uTzpBBrl?CM2ARTjTid!bhvk?-#ILzK|cj`u3IbBE}#29eZRh%e^~m zadEME?Dhwr?y>QAeP8lm!TWu=4;idJmm8%B)>_Y8+L>Y|eE-6m2Lh>E%>;QuQl@xX zir)`35AvGvUM+N14!2yA*4qDP>VU;e(>Xo4=|1_T70l{pjDvPyX$bv!spk0&c3N4%#|P|%EtSxx-^iog|%e)R5{ zw|Fntp(b~R*G7R0_|EkIn*Ow`c-i``)1R(7lCAKi@Zjz*Pdb=Q#VUUHisi5x#eSOc z=tG3orgb~gL$ABWCU4Q%Wz3`!8~V9INa_3)ha*k9mrk5~mSdvP%FoimciaQ6h2G$O z+a<4ZHQs}zz|l?FH|p>I)bg5u{cqbZ{LnYN+bPI$tjr|h$&!U?XSd({u~geAZOzYu z^sTr5rA8FbpY}7qeEOFc3*B`zI={)5$?#dUF5CENo$}WS=Fbz~H8N|r^=ZDYTsD*A z$=rvH&ZjbDd){nVk{Ld`GwW!;#)__1r3Ff1yQ7wemDFc#J^p9r2~qp%i8(jg4?4Iv z7rp%|`S3Px^_Nn1-vX1SCz4gs_nuo!+%JoWp|rk`J2xL#lBac&5~1J$+E5|qG%Zxe}K>N?fl*|a|KV$b^o?0OEmu8sZQXI~#*|35=PEj3-qe-E3ulBHic1?>Y35xzAv1nG; z@*RmYo1H(;S9y6||NgwaRox38w>L})c&v6

)H$4bWoJ|!O&FK}r0GCotO2y|eN zJ{TMkWxVdPxQWiK)mJ~rTU(xZ`}wit^M-?epB$e#wQODB&l_BQHydW&OGtg?!I<-G z!esR&o%0ps>L)pucQ-Pe(}{j>>LH{$xACXP>B9DhnZ2CcA?ca zL}7C;!Jhh0i682wDfty}&RDqREKgiyDqG+?>xCX>e5=d$OuA=yX6E6uldsrr(va9I zdzZD#nCYjXV{jc?QoDz{9CyyYfNimwKE|9&CW^j4e)7EWs%Z@iX3N?A{r2?qmq+PB zjw->4$?dlBhs~Tt{AP)N^wE$~6SXoh$?0Bk)lg;0mMz8)6;)p!<39dZ%<_m+yT*kt z!RbF<$vjl~bLA0xvxsq$QRdVXgAHn2@zXnA^JZd!bq*S$QW=bHj;<_bUNT9mU|DQ&mI$BPfz$~pe5Wd0+# z{&0opJKZVD$JWOR+D-T+dLZLUt}KJUZ+LMYWB-XBBe5?FKOc8zJ7xXtkpEq_#nun{ ztcu)h{M7pA+fRRRE?;eicyYMzL+dGujZ>dlYZ>g$o>S4YiG7!3NV$gWuHe;iJa-*u zmK=I=@8jK1yj9{aWsBk#2KQ%(UAebhb4l{+%Nu@ebMSky_;bjy@8&#LYA?;4aQmr_ zgzYW96A{4*Li;<;7yW&^!9DMF+P7T_4ut}|DSWwoJB|5ElO-AAgX4BCoH^xuF4LY1 z*3UeDeO>*$-#>m&#h<^M*Khvma6f9{S>d13EsrlBKFRQT#(d}2s8BWY7R|#`UI)C} zYBcHV`U*w%*XLGF4|;j6dv_zB*iD`MTY*wK&r9CC{A#H2q2W^0T#*M$W?PuVO31D` z^*Y09cl-virsVTG=JIze_fg)HqZZvGw4-~zo7sf8ubYkaFLq2g^YrTB*EN!!dlxsE zaK4$c>y?}N@-Bs|%*#8?9_JX8{XD7^cku1>(8G7mnthz5HnVZcVeiUr*;1C-_OZs% zUR)tpmDDQXz=U54Xe`6?FzbGk0sS_y6oPxIOEv1Nju+q2&ebE zoo-OjP;_Q8Vd!hS_^NvTXQ}5W{bGOJ;}y7a<7e5=&F(4}Vry5gTeoW6>AI4C&u+%^ z$3MvoyK?j4O&#r7HS^N129|8{x-|REhnGKAyZ^qJx${=Y&#%71wtrupyzKnrdHmw; z**aA-uciu~-PC_`QH<`wqAMvUFRl9U|L^{Q=P$1ZJb#(LX!D|zVjng|O%1*DB=zHq zi+i-DUraHIH-D`)+h^le_b^5X{K>kT=?cS zr_#N`nq(yntQtUo)vQ;t??#@n{PUz`#9#D1-Dopro;X7Dub(CWao>YC@c z_Q$K9J*zbT)b{)Z|MMSQ)HqbX(_?aNR^IBArPe*he?0!2n!RQb+mq(~hjUK7Us|>6 z)Mfr|pMR^aY_GD74NaYObydpd$frk3r(C(%I%CVmoXS~C+eBJ#X$hxn`2-6N{9o2N!eLMdXR>J+WMT zwrEWyC-)rjn=gGiXD1zz_;>v4*U$c$+nk;V_xIdq`gqQG#wznjRfZmpAG25OS(niI zZ-&G2x~S>CW{n%|rM8v6w8~a1dNR2(()U$SY@&VMq^c$k^Oc*gYMlD$&f4vKUS&g3 z>f65SUp}9|>9%LO#samHiK<6kr@p+poOSB@{pY)Q%recY8MRHD*8Vn(d}182$TW%d z&8Zr>*dG0VpU-|i`I7&<|NokAuYM~_87JEy1vK{+9S$m;u&a2LKqVL$e4_O@+;EMcX{qJV%@~GEuKa1^a z_u74;b;iG4R{2^pvKPzrV8o zYk#TitNQZdV)^>}aeHhlO)aZSe?7Xp`T1mXb@71pw|=kx{&o8A`_Ei=MVvSKQ75_o z{%)x~`)w=i+v?lC6#HMT`}ynUZt=3G--Fd^-!Fgvnt6>yOM7#Ee_g4;zoH*c#dp2? zx!c~Ubh+MzxY)m^zcaHGe9?7OwD{rMTv494eeb%>7q{Pu2^14P;2SZ?%D?^5!;6CT z$r}9+W^`YAn&b2(nPGKtOR!ZB-~NO9_q_bJ*y?oOqS?zLue$fkpFg!lj(OK1p=qDl zK0Yf9lUeur`1VRe?florH?R43Van>`C$;Kh?x%FK)@eR#|MWZYls3n1g>0{k&yU~M zP1T+J%K5K{Nk>KS;lwK8)Y4gNj4U>r2Y%z6!1(>snwZ0UX|uw+rNS6PW&6@^v}T$u z-rSw-;&7LxLo&2Y?!lMT6J4lQ1O(eX?_|b$x%YPuZvVY^M!NKhE4#ElJXLQ-=A z)Me{4wdzj?RNxCZA0k+4uX1Z9lo}Ur^35soNqOo*l59zCFTlujh+3 zEzCR>Wfp}Eug+{*(N>OKvh0R<+FFt#23edKD>m#o6T?OMzfZ z=PPBI3G&7-{3h!+_I8}I^ld({$l2uz$F>ut#mvDQ&dheVn0%JEz}sAP&b#AM$0ulR zisM_Mc#lzjvX*ZH0>d0O z6Af+Q95F_(q{IoLToFbV8luk`q%GKQUDYwWShCoGchkqOGu*PbvEKR5wbA>aTL5RL z;DM-=Jxgp>+^*T!a8BKrp{<+$^wlNF3*<^UgPgYzK2H&@%w_2D@6r2qZ7xD}rbCfl4`aBKG;C9A6Vn76!ZAJtn{-VT~^`jX$G z#O~S_C*fSxN5z@jeFUQW7HNKwd{8s}fZX=)k`tEvs1>|??!c=r6UwSWb+ebPFx;%> zP?T6z@a@&tE{S4PYL9xuG2X5%+dXT)ytpWPsC8xqbByr<<{X|$I|^ny zNjDg7w@fHvI(zuml87amnO7s-=B-<={3N(BR>s*sLx2eD2qyrioShAKWnFb1$*!(;6%W^~G!H7!I!C z*u;|TkXZR9({t0Yz!D+dhb)elxT@#ecinQlQn*X5=;f@k9kc9iJ>Jk5eB-%?M??;j zmT(4t-!v7a&PdbX)zdnSI;V+w7Vh<1XM8NVhHHzrDW`qmvYxPnq{>NaZZ_r~O>eNh z(QT*|u9umv+uZx}a-K6#MoE?2ChYTj(@@aT_ZuUu*iUmhy zjw!9^wTSCa31^u0!|d2RfuohIxi54Zz8)_Ly47D7#BgN0yW8yx(=u)?OFZV6xFhBz zziH>J(`$BWrgQNq9Zp!7COe&(ZNVpCI3%tkhz$ea(-?-1dmZ<8GQ)1NrVujp9c6=~R zjqg>|{^R%O&*Nk5ogU1Y8#9H?XxRZP&#`FLztQD&ZV+b zjLO`8ov7N(FU#UN(XT2d+@kBllJ$#cuQ}CWx!ZN+){faP)P8!%IDF$YFhASqSfaqm zYHm2~!X5u?i`ukFF$(2zxp{(Xtc3*9`aIb_TJP1L;rL|#?5tjOzRw45D7J^@NN@D! zO4z2tJB6j(4hXr~L~V7+tsde>z)ty)rE} z@$jsOx|71zN`0JSrt4??&S&h{^Peg5kMH+gUt(t~eLlV-HF1CK;y@?IDTSvhmj|>a zx!?ZsX*7 z;6C+7jgMfZOYNn2j@}gCE3#?&?8i18xGNJoUBi6ZffJlondMFX&9YS!3iyTIForFA z)*IHj|BIBY;v;R*?yS2O$qRHJPmj(Cyh@n_bb??(D;)4xnw9op=_Ct<$MGq%p7jmH{RFXWXlQ|bsSp7Z6jYG}OB&Qr%e z@0p=k?rU{I|J}X&a(DilU$}C+`Qi5I|Gyr+I(px4>ep|=Wz>bz0scbvD=q=&tMj|X9FV`>xwEaDBp-_q?`IWOyMdSAiDm>B` zvMp*XZtDil;W)9;&0~GTybs$#KVDGVvXY_meNNbir$6KU=62uyc=V*@J?(qe#ZO9) zUN+gzb9!>gBmGZFN?+`2%oV0*d9<+AUGI5UDY!X_RrHHXg?7-9S-LTjr{B%nbI>oE zD`hsX+EnY@0>#sF7i!q1p8Rzxxj=W(^Z7q#+eDvPWWg7HS!CnZXDniB*KCh)He>}Q zt6AMropm-@N8;|v#XtV>G2DqZn*KtB>6qnmv%hksi$Z0OEie#&a-!mu&{KQlZ;}t#C{&*2 zD_^o;*~wccZ@r4Fejf4X`>r(Yj1>X9tCr;|GI=}^kN%=5l0TDi;m(8NOUxHbFYnwl zRY*N0E`fI?y9mRP^I^+0qf<_c?Oncb>h2^~*St$%Pv-o*7a7lX^;q<-P>qXgI9xZm zNbH?@G)Y)>b!ceaqJ7uTupUw9S*DuV zyS(ny&og6|+B2`bxO>K?UA??gJolcido_bqI`ZOk8I^0_TRU2F7yNjlWzct8g0DnN z^O{`@M?j;u=Ny9_k7`5SDVScov`LK3R9)e{eZS!MnxJIamEK#|t31x<4`;NtVXL#% zPCKzyUTV$mP^-O>;_@GqOxLhFp5>jkqo&C9_}kbAYo;pfcyL)+gnJDi>&bPouTNeK z-4IfM%nfI`ZYMv}RJmZF61{GgZ$%Pc#jLA>&H?R*-4E_v-lNu` zvBPFtOXkYj{~gxPEEVim?p?{HT|G7EM#$G_uf&9@>J4tIOPxa=+f2@?Zj4@1pLEx- zZfc09&AI7oJX{368opy`z9MvLhH8lGjP%uNTra0zb2AQET;?ud&3tGs^WRHTJ zw(nQ#TCE#;rI>l`gS8(|H~R6bmxTASK8{e%GMeqXAhB`z2jA6q_CD58u)cNK#+f;E z&pfWP`h59&;?90}(fa;y?qsj03#(&pyxK5F@9yjKuV>Br7yE0ucB?g~%!!~9fkHKE5`*83Vh359A-moAncsW_j$u5QmR&2HZ_Y%VI{%Ieo; z7EN^1JTsx^p+(E5h->_-=DjewIFEPB)QJj9g0_XncB^DtdQ$7WSM zjXh<@v8dEf@^o(8&dUt3f(LEPZ<(%mcihVRclhNMrak)<&u%uWzPzNh#589rr%w{w zZ?m&my?S!ITLd43>vAY?vjtpy{!ynTfa@!B<{U-)M7E17a<0~F_!U%k*XpT^2ybN6 zɹ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

}D#p%L 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

#f8^jZi}l4-BK9w_>m)P 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%0Av^yl5rtPN{kiB9+uJIVL+ZlQ)p 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%0Am8q|ZrpwqN=7 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