diff options
| -rw-r--r-- | .gitlab-ci.d/buildtest-template.yml | 14 | ||||
| -rw-r--r-- | .gitlab-ci.d/buildtest.yml | 60 | ||||
| -rw-r--r-- | MAINTAINERS | 63 | ||||
| -rw-r--r-- | accel/tcg/tcg-accel-ops-rr.c | 4 | ||||
| -rw-r--r-- | docs/devel/index-build.rst | 14 | ||||
| -rw-r--r-- | docs/devel/index.rst | 1 | ||||
| -rw-r--r-- | docs/devel/testing/acpi-bits.rst (renamed from docs/devel/acpi-bits.rst) | 62 | ||||
| -rw-r--r-- | docs/devel/testing/avocado.rst | 581 | ||||
| -rw-r--r-- | docs/devel/testing/ci-definitions.rst.inc (renamed from docs/devel/ci-definitions.rst.inc) | 0 | ||||
| -rw-r--r-- | docs/devel/testing/ci-jobs.rst.inc (renamed from docs/devel/ci-jobs.rst.inc) | 0 | ||||
| -rw-r--r-- | docs/devel/testing/ci-runners.rst.inc (renamed from docs/devel/ci-runners.rst.inc) | 0 | ||||
| -rw-r--r-- | docs/devel/testing/ci.rst (renamed from docs/devel/ci.rst) | 0 | ||||
| -rw-r--r-- | docs/devel/testing/functional.rst | 338 | ||||
| -rw-r--r-- | docs/devel/testing/fuzzing.rst (renamed from docs/devel/fuzzing.rst) | 0 | ||||
| -rw-r--r-- | docs/devel/testing/index.rst | 16 | ||||
| -rw-r--r-- | docs/devel/testing/main.rst (renamed from docs/devel/testing.rst) | 577 | ||||
| -rw-r--r-- | docs/devel/testing/qgraph.rst (renamed from docs/devel/qgraph.rst) | 0 | ||||
| -rw-r--r-- | docs/devel/testing/qtest.rst (renamed from docs/devel/qtest.rst) | 0 | ||||
| -rw-r--r-- | docs/system/arm/emulation.rst | 1 | ||||
| -rw-r--r-- | hw/arm/boot.c | 10 | ||||
| -rw-r--r-- | hw/arm/sbsa-ref.c | 16 | ||||
| -rw-r--r-- | hw/arm/smmuv3.c | 1 | ||||
| -rw-r--r-- | hw/arm/virt.c | 17 | ||||
| -rw-r--r-- | hw/arm/xilinx_zynq.c | 8 | ||||
| -rw-r--r-- | hw/core/machine.c | 3 | ||||
| -rw-r--r-- | hw/core/platform-bus.c | 5 | ||||
| -rw-r--r-- | hw/i386/pc.c | 3 | ||||
| -rw-r--r-- | hw/i386/pc_piix.c | 15 | ||||
| -rw-r--r-- | hw/i386/pc_q35.c | 13 | ||||
| -rw-r--r-- | hw/m68k/virt.c | 9 | ||||
| -rw-r--r-- | hw/misc/xlnx-versal-cfu.c | 8 | ||||
| -rw-r--r-- | hw/misc/xlnx-versal-trng.c | 12 | ||||
| -rw-r--r-- | hw/nvram/xlnx-bbram.c | 13 | ||||
| -rw-r--r-- | hw/nvram/xlnx-versal-efuse-ctrl.c | 6 | ||||
| -rw-r--r-- | hw/nvram/xlnx-zynqmp-efuse.c | 13 | ||||
| -rw-r--r-- | hw/ppc/spapr.c | 15 | ||||
| -rw-r--r-- | hw/s390x/s390-virtio-ccw.c | 14 | ||||
| -rw-r--r-- | include/hw/arm/virt.h | 1 | ||||
| -rw-r--r-- | include/hw/boards.h | 3 | ||||
| -rw-r--r-- | include/hw/i386/pc.h | 3 | ||||
| -rw-r--r-- | include/hw/misc/xlnx-versal-trng.h | 1 | ||||
| -rw-r--r-- | include/hw/nvram/xlnx-bbram.h | 1 | ||||
| -rw-r--r-- | include/hw/nvram/xlnx-versal-efuse.h | 1 | ||||
| -rw-r--r-- | include/hw/nvram/xlnx-zynqmp-efuse.h | 1 | ||||
| -rw-r--r-- | migration/file.c | 3 | ||||
| -rw-r--r-- | migration/file.h | 2 | ||||
| -rw-r--r-- | migration/meson.build | 1 | ||||
| -rw-r--r-- | migration/multifd-nocomp.c | 389 | ||||
| -rw-r--r-- | migration/multifd-qpl.c | 79 | ||||
| -rw-r--r-- | migration/multifd-uadk.c | 104 | ||||
| -rw-r--r-- | migration/multifd-zero-page.c | 13 | ||||
| -rw-r--r-- | migration/multifd-zlib.c | 99 | ||||
| -rw-r--r-- | migration/multifd-zstd.c | 98 | ||||
| -rw-r--r-- | migration/multifd.c | 553 | ||||
| -rw-r--r-- | migration/multifd.h | 152 | ||||
| -rw-r--r-- | migration/ram.c | 10 | ||||
| -rw-r--r-- | migration/savevm.c | 10 | ||||
| -rw-r--r-- | migration/trace-events | 9 | ||||
| -rw-r--r-- | python/wheels/pycotap-1.3.1-py3-none-any.whl | bin | 0 -> 5119 bytes | |||
| -rw-r--r-- | pythondeps.toml | 3 | ||||
| -rw-r--r-- | target/arm/cpu-features.h | 5 | ||||
| -rw-r--r-- | target/arm/cpu.h | 1 | ||||
| -rw-r--r-- | target/arm/helper.h | 12 | ||||
| -rw-r--r-- | target/arm/tcg/cpu64.c | 4 | ||||
| -rw-r--r-- | target/arm/tcg/helper-sme.h | 4 | ||||
| -rw-r--r-- | target/arm/tcg/sme_helper.c | 78 | ||||
| -rw-r--r-- | target/arm/tcg/translate-a64.c | 40 | ||||
| -rw-r--r-- | target/arm/tcg/translate-neon.c | 43 | ||||
| -rw-r--r-- | target/arm/tcg/translate-sme.c | 3 | ||||
| -rw-r--r-- | target/arm/tcg/translate-sve.c | 25 | ||||
| -rw-r--r-- | target/arm/tcg/translate-vfp.c | 8 | ||||
| -rw-r--r-- | target/arm/tcg/vec_helper.c | 208 | ||||
| -rw-r--r-- | target/arm/tcg/vec_internal.h | 37 | ||||
| -rw-r--r-- | target/arm/tcg/vfp.decode | 12 | ||||
| -rw-r--r-- | target/arm/vfp_helper.c | 8 | ||||
| -rw-r--r-- | target/ppc/machine.c | 2 | ||||
| -rw-r--r-- | tests/Makefile.include | 44 | ||||
| -rw-r--r-- | tests/avocado/avocado_qemu/__init__.py | 17 | ||||
| -rw-r--r-- | tests/avocado/avocado_qemu/linuxtest.py | 4 | ||||
| -rw-r--r-- | tests/avocado/boot_linux_console.py | 20 | ||||
| -rw-r--r-- | tests/avocado/boot_xen.py | 13 | ||||
| -rw-r--r-- | tests/avocado/load_bflt.py | 54 | ||||
| -rwxr-xr-x[-rw-r--r--] | tests/avocado/machine_arm_n8x0.py | 0 | ||||
| -rw-r--r-- | tests/avocado/machine_microblaze.py | 61 | ||||
| -rw-r--r-- | tests/avocado/machine_mips_loongson3v.py | 39 | ||||
| -rw-r--r-- | tests/avocado/machine_sparc64_sun4u.py | 36 | ||||
| -rw-r--r-- | tests/avocado/ppc_amiga.py | 38 | ||||
| -rw-r--r-- | tests/avocado/tesseract_utils.py | 46 | ||||
| -rw-r--r-- | tests/functional/acpi-bits/bits-config/bits-cfg.txt (renamed from tests/avocado/acpi-bits/bits-config/bits-cfg.txt) | 0 | ||||
| -rw-r--r-- | tests/functional/acpi-bits/bits-tests/smbios.py2 (renamed from tests/avocado/acpi-bits/bits-tests/smbios.py2) | 0 | ||||
| -rw-r--r-- | tests/functional/acpi-bits/bits-tests/smilatency.py2 (renamed from tests/avocado/acpi-bits/bits-tests/smilatency.py2) | 0 | ||||
| -rw-r--r-- | tests/functional/acpi-bits/bits-tests/testacpi.py2 (renamed from tests/avocado/acpi-bits/bits-tests/testacpi.py2) | 0 | ||||
| -rw-r--r-- | tests/functional/acpi-bits/bits-tests/testcpuid.py2 (renamed from tests/avocado/acpi-bits/bits-tests/testcpuid.py2) | 0 | ||||
| -rw-r--r-- | tests/functional/meson.build | 205 | ||||
| -rw-r--r-- | tests/functional/qemu_test/__init__.py | 14 | ||||
| -rw-r--r-- | tests/functional/qemu_test/asset.py | 171 | ||||
| -rw-r--r-- | tests/functional/qemu_test/cmd.py | 193 | ||||
| -rw-r--r-- | tests/functional/qemu_test/config.py | 36 | ||||
| -rw-r--r-- | tests/functional/qemu_test/tesseract.py | 35 | ||||
| -rw-r--r-- | tests/functional/qemu_test/testcase.py | 202 | ||||
| -rw-r--r-- | tests/functional/qemu_test/utils.py | 56 | ||||
| -rwxr-xr-x[-rw-r--r--] | tests/functional/test_aarch64_sbsaref.py (renamed from tests/avocado/machine_aarch64_sbsaref.py) | 148 | ||||
| -rwxr-xr-x[-rw-r--r--] | tests/functional/test_aarch64_virt.py (renamed from tests/avocado/machine_aarch64_virt.py) | 71 | ||||
| -rwxr-xr-x[-rw-r--r--] | tests/functional/test_acpi_bits.py (renamed from tests/avocado/acpi-bits.py) | 83 | ||||
| -rwxr-xr-x | tests/functional/test_arm_bflt.py | 44 | ||||
| -rwxr-xr-x[-rw-r--r--] | tests/functional/test_arm_canona1100.py (renamed from tests/avocado/machine_arm_canona1100.py) | 30 | ||||
| -rwxr-xr-x[-rw-r--r--] | tests/functional/test_arm_integratorcp.py (renamed from tests/avocado/machine_arm_integratorcp.py) | 56 | ||||
| -rwxr-xr-x[-rw-r--r--] | tests/functional/test_avr_mega2560.py (renamed from tests/avocado/machine_avr6.py) | 22 | ||||
| -rwxr-xr-x[-rw-r--r--] | tests/functional/test_cpu_queries.py (renamed from tests/avocado/cpu_queries.py) | 12 | ||||
| -rwxr-xr-x[-rw-r--r--] | tests/functional/test_empty_cpu_model.py (renamed from tests/avocado/empty_cpu_model.py) | 7 | ||||
| -rwxr-xr-x[-rw-r--r--] | tests/functional/test_info_usernet.py (renamed from tests/avocado/info_usernet.py) | 11 | ||||
| -rwxr-xr-x[-rw-r--r--] | tests/functional/test_linux_initrd.py (renamed from tests/avocado/linux_initrd.py) | 37 | ||||
| -rwxr-xr-x[-rw-r--r--] | tests/functional/test_loongarch64_virt.py (renamed from tests/avocado/machine_loongarch.py) | 46 | ||||
| -rwxr-xr-x[-rw-r--r--] | tests/functional/test_m68k_nextcube.py (renamed from tests/avocado/machine_m68k_nextcube.py) | 29 | ||||
| -rwxr-xr-x[-rw-r--r--] | tests/functional/test_mem_addr_space.py (renamed from tests/avocado/mem-addr-space-check.py) | 52 | ||||
| -rwxr-xr-x | tests/functional/test_microblaze_s3adsp1800.py | 40 | ||||
| -rwxr-xr-x | tests/functional/test_microblazeel_s3adsp1800.py | 42 | ||||
| -rwxr-xr-x[-rw-r--r--] | tests/functional/test_mips64el_fuloong2e.py (renamed from tests/avocado/machine_mips_fuloong2e.py) | 33 | ||||
| -rwxr-xr-x | tests/functional/test_mips64el_loongson3v.py | 39 | ||||
| -rwxr-xr-x[-rw-r--r--] | tests/functional/test_netdev_ethtool.py (renamed from tests/avocado/netdev-ethtool.py) | 51 | ||||
| -rwxr-xr-x[-rw-r--r--] | tests/functional/test_pc_cpu_hotplug_props.py (renamed from tests/avocado/pc_cpu_hotplug_props.py) | 11 | ||||
| -rwxr-xr-x[-rw-r--r--] | tests/functional/test_ppc64_hv.py (renamed from tests/avocado/ppc_hv_tests.py) | 52 | ||||
| -rwxr-xr-x[-rw-r--r--] | tests/functional/test_ppc64_powernv.py (renamed from tests/avocado/ppc_powernv.py) | 56 | ||||
| -rwxr-xr-x[-rw-r--r--] | tests/functional/test_ppc64_pseries.py (renamed from tests/avocado/ppc_pseries.py) | 56 | ||||
| -rwxr-xr-x[-rw-r--r--] | tests/functional/test_ppc_405.py (renamed from tests/avocado/ppc_405.py) | 29 | ||||
| -rwxr-xr-x[-rw-r--r--] | tests/functional/test_ppc_40p.py (renamed from tests/avocado/ppc_prep_40p.py) | 65 | ||||
| -rwxr-xr-x[-rw-r--r--] | tests/functional/test_ppc_74xx.py (renamed from tests/avocado/ppc_74xx.py) | 74 | ||||
| -rwxr-xr-x | tests/functional/test_ppc_amiga.py | 43 | ||||
| -rwxr-xr-x[-rw-r--r--] | tests/functional/test_ppc_bamboo.py (renamed from tests/avocado/ppc_bamboo.py) | 33 | ||||
| -rwxr-xr-x[-rw-r--r--] | tests/functional/test_ppc_mpc8544ds.py (renamed from tests/avocado/ppc_mpc8544ds.py) | 29 | ||||
| -rwxr-xr-x[-rw-r--r--] | tests/functional/test_ppc_virtex_ml507.py (renamed from tests/avocado/ppc_virtex_ml507.py) | 29 | ||||
| -rwxr-xr-x[-rw-r--r--] | tests/functional/test_rx_gdbsim.py (renamed from tests/avocado/machine_rx_gdbsim.py) | 54 | ||||
| -rwxr-xr-x[-rw-r--r--] | tests/functional/test_s390x_ccw_virtio.py (renamed from tests/avocado/machine_s390_ccw_virtio.py) | 81 | ||||
| -rwxr-xr-x[-rw-r--r--] | tests/functional/test_s390x_topology.py (renamed from tests/avocado/s390_topology.py) | 86 | ||||
| -rwxr-xr-x | tests/functional/test_sparc64_sun4u.py | 41 | ||||
| -rwxr-xr-x[-rw-r--r--] | tests/functional/test_version.py (renamed from tests/avocado/version.py) | 13 | ||||
| -rwxr-xr-x[-rw-r--r--] | tests/functional/test_virtio_gpu.py (renamed from tests/avocado/virtio-gpu.py) | 64 | ||||
| -rwxr-xr-x[-rw-r--r--] | tests/functional/test_virtio_version.py (renamed from tests/avocado/virtio_version.py) | 8 | ||||
| -rwxr-xr-x[-rw-r--r--] | tests/functional/test_x86_cpu_model_versions.py (renamed from tests/avocado/x86_cpu_model_versions.py) | 65 | ||||
| -rw-r--r-- | tests/meson.build | 1 | ||||
| -rw-r--r-- | tests/qtest/dm163-test.c | 2 | ||||
| -rw-r--r-- | tests/qtest/libqtest.c | 17 | ||||
| -rw-r--r-- | tests/qtest/libqtest.h | 2 | ||||
| -rw-r--r-- | tests/qtest/meson.build | 1 | ||||
| -rw-r--r-- | tests/qtest/migration-helpers.c | 20 | ||||
| -rw-r--r-- | tests/qtest/migration-test.c | 114 | ||||
| -rw-r--r-- | tests/qtest/stm32l4x5_usart-test.c | 2 | ||||
| -rw-r--r-- | tests/unit/crypto-tls-x509-helpers.c | 13 | ||||
| -rw-r--r-- | tests/unit/crypto-tls-x509-helpers.h | 6 |
149 files changed, 4219 insertions, 2923 deletions
diff --git a/.gitlab-ci.d/buildtest-template.yml b/.gitlab-ci.d/buildtest-template.yml index 844c26623d..5f2fc7e6f4 100644 --- a/.gitlab-ci.d/buildtest-template.yml +++ b/.gitlab-ci.d/buildtest-template.yml @@ -59,6 +59,10 @@ - cd build - find . -type f -exec touch {} + # Avoid recompiling by hiding ninja with NINJA=":" + # We also have to pre-cache the functional tests manually in this case + - if [ "x${QEMU_TEST_CACHE_DIR}" != "x" ]; then + $MAKE precache-functional ; + fi - $MAKE NINJA=":" $MAKE_CHECK_ARGS .native_test_job_template: @@ -72,12 +76,13 @@ reports: junit: build/meson-logs/testlog.junit.xml -.avocado_test_job_template: +.functional_test_job_template: extends: .common_test_job_template cache: key: "${CI_JOB_NAME}-cache" paths: - ${CI_PROJECT_DIR}/avocado-cache + - ${CI_PROJECT_DIR}/functional-cache policy: pull-push artifacts: name: "$CI_JOB_NAME-$CI_COMMIT_REF_SLUG" @@ -86,6 +91,7 @@ paths: - build/tests/results/latest/results.xml - build/tests/results/latest/test-results + - build/tests/functional/*/*/*.log reports: junit: build/tests/results/latest/results.xml before_script: @@ -96,11 +102,13 @@ - echo -e '[job.output.testlogs]\nstatuses = ["FAIL", "INTERRUPT"]' >> ~/.config/avocado/avocado.conf - if [ -d ${CI_PROJECT_DIR}/avocado-cache ]; then - du -chs ${CI_PROJECT_DIR}/avocado-cache ; + du -chs ${CI_PROJECT_DIR}/*-cache ; fi - export AVOCADO_ALLOW_UNTRUSTED_CODE=1 + - export QEMU_TEST_ALLOW_UNTRUSTED_CODE=1 + - export QEMU_TEST_CACHE_DIR=${CI_PROJECT_DIR}/functional-cache after_script: - cd build - - du -chs ${CI_PROJECT_DIR}/avocado-cache + - du -chs ${CI_PROJECT_DIR}/*-cache variables: QEMU_JOB_AVOCADO: 1 diff --git a/.gitlab-ci.d/buildtest.yml b/.gitlab-ci.d/buildtest.yml index aa32782405..1d2afae996 100644 --- a/.gitlab-ci.d/buildtest.yml +++ b/.gitlab-ci.d/buildtest.yml @@ -22,14 +22,14 @@ check-system-alpine: IMAGE: alpine MAKE_CHECK_ARGS: check-unit check-qtest -avocado-system-alpine: - extends: .avocado_test_job_template +functional-system-alpine: + extends: .functional_test_job_template needs: - job: build-system-alpine artifacts: true variables: IMAGE: alpine - MAKE_CHECK_ARGS: check-avocado + MAKE_CHECK_ARGS: check-avocado check-functional AVOCADO_TAGS: arch:avr arch:loongarch64 arch:mips64 arch:mipsel build-system-ubuntu: @@ -53,14 +53,14 @@ check-system-ubuntu: IMAGE: ubuntu2204 MAKE_CHECK_ARGS: check -avocado-system-ubuntu: - extends: .avocado_test_job_template +functional-system-ubuntu: + extends: .functional_test_job_template needs: - job: build-system-ubuntu artifacts: true variables: IMAGE: ubuntu2204 - MAKE_CHECK_ARGS: check-avocado + MAKE_CHECK_ARGS: check-avocado check-functional AVOCADO_TAGS: arch:alpha arch:microblazeel arch:mips64el build-system-debian: @@ -85,14 +85,14 @@ check-system-debian: IMAGE: debian MAKE_CHECK_ARGS: check -avocado-system-debian: - extends: .avocado_test_job_template +functional-system-debian: + extends: .functional_test_job_template needs: - job: build-system-debian artifacts: true variables: IMAGE: debian - MAKE_CHECK_ARGS: check-avocado + MAKE_CHECK_ARGS: check-avocado check-functional AVOCADO_TAGS: arch:arm arch:i386 arch:riscv64 arch:sh4 arch:sparc arch:xtensa crash-test-debian: @@ -129,14 +129,14 @@ check-system-fedora: IMAGE: fedora MAKE_CHECK_ARGS: check -avocado-system-fedora: - extends: .avocado_test_job_template +functional-system-fedora: + extends: .functional_test_job_template needs: - job: build-system-fedora artifacts: true variables: IMAGE: fedora - MAKE_CHECK_ARGS: check-avocado + MAKE_CHECK_ARGS: check-avocado check-functional AVOCADO_TAGS: arch:microblaze arch:mips arch:xtensa arch:m68k arch:riscv32 arch:ppc arch:sparc64 @@ -243,14 +243,14 @@ check-system-centos: IMAGE: centos9 MAKE_CHECK_ARGS: check -avocado-system-centos: - extends: .avocado_test_job_template +functional-system-centos: + extends: .functional_test_job_template needs: - job: build-system-centos artifacts: true variables: IMAGE: centos9 - MAKE_CHECK_ARGS: check-avocado + MAKE_CHECK_ARGS: check-avocado check-functional AVOCADO_TAGS: arch:ppc64 arch:or1k arch:s390x arch:x86_64 arch:rx arch:sh4 @@ -274,14 +274,14 @@ check-system-opensuse: IMAGE: opensuse-leap MAKE_CHECK_ARGS: check -avocado-system-opensuse: - extends: .avocado_test_job_template +functional-system-opensuse: + extends: .functional_test_job_template needs: - job: build-system-opensuse artifacts: true variables: IMAGE: opensuse-leap - MAKE_CHECK_ARGS: check-avocado + MAKE_CHECK_ARGS: check-avocado check-functional AVOCADO_TAGS: arch:s390x arch:x86_64 arch:aarch64 # @@ -302,15 +302,15 @@ build-system-flaky: ppc64-softmmu rx-softmmu s390x-softmmu sh4-softmmu x86_64-softmmu MAKE_CHECK_ARGS: check-build -avocado-system-flaky: - extends: .avocado_test_job_template +functional-system-flaky: + extends: .functional_test_job_template needs: - job: build-system-flaky artifacts: true allow_failure: true variables: IMAGE: debian - MAKE_CHECK_ARGS: check-avocado + MAKE_CHECK_ARGS: check-avocado check-functional QEMU_JOB_OPTIONAL: 1 QEMU_TEST_FLAKY_TESTS: 1 AVOCADO_TAGS: flaky @@ -485,14 +485,14 @@ check-cfi-aarch64: IMAGE: fedora MAKE_CHECK_ARGS: check -avocado-cfi-aarch64: - extends: .avocado_test_job_template +functional-cfi-aarch64: + extends: .functional_test_job_template needs: - job: build-cfi-aarch64 artifacts: true variables: IMAGE: fedora - MAKE_CHECK_ARGS: check-avocado + MAKE_CHECK_ARGS: check-avocado check-functional build-cfi-ppc64-s390x: extends: @@ -523,14 +523,14 @@ check-cfi-ppc64-s390x: IMAGE: fedora MAKE_CHECK_ARGS: check -avocado-cfi-ppc64-s390x: - extends: .avocado_test_job_template +functional-cfi-ppc64-s390x: + extends: .functional_test_job_template needs: - job: build-cfi-ppc64-s390x artifacts: true variables: IMAGE: fedora - MAKE_CHECK_ARGS: check-avocado + MAKE_CHECK_ARGS: check-avocado check-functional build-cfi-x86_64: extends: @@ -557,14 +557,14 @@ check-cfi-x86_64: IMAGE: fedora MAKE_CHECK_ARGS: check -avocado-cfi-x86_64: - extends: .avocado_test_job_template +functional-cfi-x86_64: + extends: .functional_test_job_template needs: - job: build-cfi-x86_64 artifacts: true variables: IMAGE: fedora - MAKE_CHECK_ARGS: check-avocado + MAKE_CHECK_ARGS: check-avocado check-functional tsan-build: extends: .native_build_job_template diff --git a/MAINTAINERS b/MAINTAINERS index c14ac014e2..0c1bc69828 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -222,7 +222,7 @@ S: Maintained F: docs/system/target-avr.rst F: gdb-xml/avr-cpu.xml F: target/avr/ -F: tests/avocado/machine_avr6.py +F: tests/functional/test_avr_mega2560.py CRIS TCG CPUs M: Edgar E. Iglesias <edgar.iglesias@gmail.com> @@ -266,7 +266,7 @@ M: Song Gao <gaosong@loongson.cn> S: Maintained F: target/loongarch/ F: tests/tcg/loongarch64/ -F: tests/avocado/machine_loongarch.py +F: tests/functional/test_loongarch64_virt.py M68K TCG CPUs M: Laurent Vivier <laurent@vivier.eu> @@ -318,6 +318,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 RISC-V TCG CPUs M: Palmer Dabbelt <palmer@dabbelt.com> @@ -734,7 +735,7 @@ S: Odd Fixes F: include/hw/arm/digic.h F: hw/*/digic* F: include/hw/*/digic* -F: tests/avocado/machine_arm_canona1100.py +F: tests/functional/test_arm_canona1100.py F: docs/system/arm/digic.rst Goldfish RTC @@ -785,7 +786,7 @@ S: Maintained F: hw/arm/integratorcp.c F: hw/misc/arm_integrator_debug.c F: include/hw/misc/arm_integrator_debug.h -F: tests/avocado/machine_arm_integratorcp.py +F: tests/functional/test_arm_integratorcp.py F: docs/system/arm/integratorcp.rst MCIMX6UL EVK / i.MX6ul @@ -971,7 +972,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/avocado/machine_aarch64_sbsaref.py +F: tests/functional/test_aarch64_sbsaref.py Sharp SL-5500 (Collie) PDA M: Peter Maydell <peter.maydell@linaro.org> @@ -1023,7 +1024,7 @@ S: Maintained F: hw/arm/virt* F: include/hw/arm/virt.h F: docs/system/arm/virt.rst -F: tests/avocado/machine_aarch64_virt.py +F: tests/functional/test_aarch64_virt.py Xilinx Zynq M: Edgar E. Iglesias <edgar.iglesias@gmail.com> @@ -1283,6 +1284,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 q800 M: Laurent Vivier <laurent@vivier.eu> @@ -1330,7 +1332,7 @@ M: Edgar E. Iglesias <edgar.iglesias@gmail.com> S: Maintained F: hw/microblaze/petalogix_s3adsp1800_mmu.c F: include/hw/char/xilinx_uartlite.h -F: tests/avocado/machine_microblaze.py +F: tests/functional/test_microblaze*.py petalogix_ml605 M: Edgar E. Iglesias <edgar.iglesias@gmail.com> @@ -1383,7 +1385,7 @@ S: Odd Fixes F: hw/mips/fuloong2e.c F: hw/pci-host/bonito.c F: include/hw/pci-host/bonito.h -F: tests/avocado/machine_mips_fuloong2e.py +F: tests/functional/test_mips64el_fuloong2e.py Loongson-3 virtual platforms M: Huacai Chen <chenhuacai@kernel.org> @@ -1398,7 +1400,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/avocado/machine_mips_loongson3v.py +F: tests/functional/test_mips64el_loongson3v.py Boston M: Paul Burton <paulburton@kernel.org> @@ -1424,14 +1426,14 @@ PowerPC Machines L: qemu-ppc@nongnu.org S: Orphan F: hw/ppc/ppc405* -F: tests/avocado/ppc_405.py +F: tests/functional/test_ppc_405.py Bamboo L: qemu-ppc@nongnu.org S: Orphan F: hw/ppc/ppc440_bamboo.c F: hw/pci-host/ppc4xx_pci.c -F: tests/avocado/ppc_bamboo.py +F: tests/functional/test_ppc_bamboo.py e500 L: qemu-ppc@nongnu.org @@ -1454,7 +1456,7 @@ L: qemu-ppc@nongnu.org S: Orphan F: hw/ppc/mpc8544ds.c F: hw/ppc/mpc8544_guts.c -F: tests/avocado/ppc_mpc8544ds.py +F: tests/functional/test_ppc_mpc8544ds.py New World (mac99) M: Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk> @@ -1507,7 +1509,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/avocado/ppc_prep_40p.py +F: tests/functional/test_ppc_40p.py sPAPR (pseries) M: Nicholas Piggin <npiggin@gmail.com> @@ -1531,8 +1533,8 @@ F: tests/qtest/spapr* F: tests/qtest/libqos/*spapr* F: tests/qtest/rtas* F: tests/qtest/libqos/rtas* -F: tests/avocado/ppc_pseries.py -F: tests/avocado/ppc_hv_tests.py +F: tests/functional/test_ppc64_pseries.py +F: tests/functional/test_ppc64_hv.py PowerNV (Non-Virtualized) M: Cédric Le Goater <clg@kaod.org> @@ -1549,6 +1551,7 @@ F: include/hw/ppc/pnv* F: include/hw/pci-host/pnv* F: pc-bios/skiboot.lid F: tests/qtest/pnv* +F: tests/functional/test_ppc64_powernv.py pca955x M: Glenn Miles <milesg@linux.ibm.com> @@ -1563,7 +1566,7 @@ M: Edgar E. Iglesias <edgar.iglesias@gmail.com> L: qemu-ppc@nongnu.org S: Odd Fixes F: hw/ppc/virtex_ml507.c -F: tests/avocado/ppc_virtex_ml507.py +F: tests/functional/test_ppc_virtex_ml507.py sam460ex M: BALATON Zoltan <balaton@eik.bme.hu> @@ -1596,6 +1599,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 Virtual Open Firmware (VOF) M: Alexey Kardashevskiy <aik@ozlabs.ru> @@ -1662,7 +1666,7 @@ R: Yoshinori Sato <ysato@users.sourceforge.jp> S: Orphan F: docs/system/target-rx.rst F: hw/rx/rx-gdbsim.c -F: tests/avocado/machine_rx_gdbsim.py +F: tests/functional/test_rx_gdbsim.py SH4 Machines ------------ @@ -1717,7 +1721,7 @@ 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/avocado/machine_sparc64_sun4u.py +F: tests/functional/test_sparc64_sun4u.py Sun4v M: Artyom Tarasenko <atar4qemu@gmail.com> @@ -1744,7 +1748,7 @@ S: Supported F: hw/s390x/ F: include/hw/s390x/ F: configs/devices/s390x-softmmu/default.mak -F: tests/avocado/machine_s390_ccw_virtio.py +F: tests/functional/test_s390x_ccw_virtio.py T: git https://github.com/borntraeger/qemu.git s390-next L: qemu-s390x@nongnu.org @@ -1807,7 +1811,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/avocado/s390_topology.py +F: tests/functional/test_s390x_topology.py X86 Machines ------------ @@ -1835,6 +1839,9 @@ 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_mem_addr_space.py +F: tests/functional/test_pc_cpu_hotplug_props.py +F: tests/functional/test_x86_cpu_model_versions.py PC Chipset M: Michael S. Tsirkin <mst@redhat.com> @@ -1901,6 +1908,8 @@ F: include/hw/boards.h F: include/hw/core/cpu.h F: include/hw/cpu/cluster.h F: include/sysemu/numa.h +F: tests/functional/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 @@ -2067,8 +2076,8 @@ ACPI/AVOCADO/BIOSBITS M: Ani Sinha <anisinha@redhat.com> M: Michael S. Tsirkin <mst@redhat.com> S: Supported -F: tests/avocado/acpi-bits/* -F: tests/avocado/acpi-bits.py +F: tests/functional/acpi-bits/* +F: tests/functional/test_acpi_bits.py F: docs/devel/acpi-bits.rst ACPI/HEST/GHES @@ -2105,6 +2114,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: docs/virtio-net-failover.rst T: git https://github.com/jasowang/qemu.git net @@ -2240,6 +2250,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 virtio-balloon M: Michael S. Tsirkin <mst@redhat.com> @@ -2490,7 +2501,7 @@ R: Sriram Yagnaraman <sriram.yagnaraman@ericsson.com> S: Maintained F: docs/system/devices/igb.rst F: hw/net/igb* -F: tests/avocado/netdev-ethtool.py +F: tests/functional/test_netdev_ethtool.py F: tests/qtest/igb-test.c F: tests/qtest/libqos/igb.c @@ -2973,6 +2984,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: util/keyval.c F: util/qemu-option.c @@ -4148,6 +4160,11 @@ F: .gitlab-ci.d/cirrus/freebsd* F: tests/vm/freebsd W: https://cirrus-ci.com/github/qemu/qemu +Functional testing framework +M: Thomas Huth <thuth@redhat.com> +R: Philippe Mathieu-Daudé <philmd@linaro.org> +F: tests/functional/qemu_test/ + Windows Hosted Continuous Integration M: Yonggang Luo <luoyonggang@gmail.com> S: Maintained diff --git a/accel/tcg/tcg-accel-ops-rr.c b/accel/tcg/tcg-accel-ops-rr.c index c59c77da4b..8ebadf8e9e 100644 --- a/accel/tcg/tcg-accel-ops-rr.c +++ b/accel/tcg/tcg-accel-ops-rr.c @@ -302,9 +302,7 @@ static void *rr_cpu_thread_fn(void *arg) rr_deal_with_unplugged_cpus(); } - rcu_remove_force_rcu_notifier(&force_rcu); - rcu_unregister_thread(); - return NULL; + g_assert_not_reached(); } void rr_start_vcpu_thread(CPUState *cpu) diff --git a/docs/devel/index-build.rst b/docs/devel/index-build.rst index 90b406ca0e..0023953be3 100644 --- a/docs/devel/index-build.rst +++ b/docs/devel/index-build.rst @@ -1,9 +1,8 @@ -QEMU Build and Test System --------------------------- +QEMU Build System +----------------- -Details about how QEMU's build system works and how it is integrated -into our testing infrastructure. You will need to understand some of -the basics if you are adding new files and targets to the build. +Details about how QEMU's build system works. You will need to understand +some of the basics if you are adding new files and targets to the build. .. toctree:: :maxdepth: 3 @@ -11,10 +10,5 @@ the basics if you are adding new files and targets to the build. build-system kconfig docs - testing - acpi-bits - qtest - ci qapi-code-gen - fuzzing control-flow-integrity diff --git a/docs/devel/index.rst b/docs/devel/index.rst index abf60457c2..a53f1bfda5 100644 --- a/docs/devel/index.rst +++ b/docs/devel/index.rst @@ -31,6 +31,7 @@ the :ref:`tcg_internals`. index-process index-build + testing/index index-api index-internals index-tcg diff --git a/docs/devel/acpi-bits.rst b/docs/devel/testing/acpi-bits.rst index 1ec394f5fb..78aeb6aa3c 100644 --- a/docs/devel/acpi-bits.rst +++ b/docs/devel/testing/acpi-bits.rst @@ -1,6 +1,6 @@ -============================================================================= -ACPI/SMBIOS avocado tests using biosbits -============================================================================= +================================== +ACPI/SMBIOS testing using biosbits +================================== ************ Introduction ************ @@ -35,7 +35,7 @@ for developing biosbits and its real life uses can be found in [#a]_ and [#b]_. For QEMU, we maintain a fork of bios bits in gitlab along with all the dependent submodules `here <https://gitlab.com/qemu-project/biosbits-bits>`__. This fork contains numerous fixes, a newer acpica and changes specific to -running this avocado QEMU tests using bits. The author of this document +running these functional QEMU tests using bits. The author of this document is the sole maintainer of the QEMU fork of bios bits repository. For more information, please see author's `FOSDEM talk on this bios-bits based test framework <https://fosdem.org/2024/schedule/event/fosdem-2024-2262-exercising-qemu-generated-acpi-smbios-tables-using-biosbits-from-within-a-guest-vm-/>`__. @@ -44,12 +44,12 @@ framework <https://fosdem.org/2024/schedule/event/fosdem-2024-2262-exercising-qe Description of the test framework ********************************* -Under the directory ``tests/avocado/``, ``acpi-bits.py`` is a QEMU avocado -test that drives all this. +Under the directory ``tests/functional/``, ``test_acpi_bits.py`` is a QEMU +functional test that drives all this. A brief description of the various test files follows. -Under ``tests/avocado/`` as the root we have: +Under ``tests/functional/`` as the root we have: :: @@ -60,12 +60,12 @@ Under ``tests/avocado/`` as the root we have: │ ├── smbios.py2 │ ├── testacpi.py2 │ └── testcpuid.py2 - ├── acpi-bits.py + ├── test_acpi_bits.py -* ``tests/avocado``: +* ``tests/functional``: - ``acpi-bits.py``: - This is the main python avocado test script that generates a + ``test_acpi_bits.py``: + This is the main python functional test script that generates a biosbits iso. It then spawns a QEMU VM with it, collects the log and reports test failures. This is the script one would be interested in if they wanted to add or change some component of the log parsing, add a new command line @@ -79,35 +79,22 @@ Under ``tests/avocado/`` as the root we have: you to inspect and run the specific commands manually. In order to run this test, please perform the following steps from the QEMU - build directory: + build directory (assuming that the sources are in ".."): :: - $ make check-venv (needed only the first time to create the venv) - $ ./pyvenv/bin/avocado run -t acpi tests/avocado + $ export PYTHONPATH=../python:../tests/functional + $ export QEMU_TEST_QEMU_BINARY=$PWD/qemu-system-x86_64 + $ python3 ../tests/functional/test_acpi_bits.py - The above will run all acpi avocado tests including this one. - In order to run the individual tests, perform the following: - :: - - $ ./pyvenv/bin/avocado run tests/avocado/acpi-bits.py --tap - - - The above will produce output in tap format. You can omit "--tap -" in the - end and it will produce output like the following: - :: - - $ ./pyvenv/bin/avocado run tests/avocado/acpi-bits.py - Fetching asset from tests/avocado/acpi-bits.py:AcpiBitsTest.test_acpi_smbios_bits - JOB ID : eab225724da7b64c012c65705dc2fa14ab1defef - JOB LOG : /home/anisinha/avocado/job-results/job-2022-10-10T17.58-eab2257/job.log - (1/1) tests/avocado/acpi-bits.py:AcpiBitsTest.test_acpi_smbios_bits: PASS (33.09 s) - RESULTS : PASS 1 | ERROR 0 | FAIL 0 | SKIP 0 | WARN 0 | INTERRUPT 0 | CANCEL 0 - JOB TIME : 39.22 s + The above will run all acpi-bits functional tests (producing output in + tap format). - You can inspect the log file for more information about the run or in order - to diagnoze issues. If you pass V=1 in the environment, more diagnostic logs - would be found in the test log. + You can inspect the log files in tests/functional/x86_64/test_acpi_bits.*/ + for more information about the run or in order to diagnoze issues. + If you pass V=1 in the environment, more diagnostic logs will be put into + the test log. -* ``tests/avocado/acpi-bits/bits-config``: +* ``tests/functional/acpi-bits/bits-config``: This location contains biosbits configuration files that determine how the software runs the tests. @@ -117,7 +104,7 @@ Under ``tests/avocado/`` as the root we have: or actions are performed by bits. The description of the config options are provided in the file itself. -* ``tests/avocado/acpi-bits/bits-tests``: +* ``tests/functional/acpi-bits/bits-tests``: This directory contains biosbits python based tests that are run from within the biosbits environment in the spawned VM. New additions of test cases can @@ -155,7 +142,8 @@ Under ``tests/avocado/`` as the root we have: (a) They are python2.7 based scripts and not python 3 scripts. (b) They are run from within the bios bits VM and is not subjected to QEMU build/test python script maintenance and dependency resolutions. - (c) They need not be loaded by avocado framework when running tests. + (c) They need not be loaded by the test framework by accident when running + tests. Author: Ani Sinha <anisinha@redhat.com> diff --git a/docs/devel/testing/avocado.rst b/docs/devel/testing/avocado.rst new file mode 100644 index 0000000000..eda76fe2db --- /dev/null +++ b/docs/devel/testing/avocado.rst @@ -0,0 +1,581 @@ +.. _checkavocado-ref: + + +Integration testing with Avocado +================================ + +The ``tests/avocado`` directory hosts integration tests. They're usually +higher level tests, and may interact with external resources and with +various guest operating systems. + +These tests are written using the Avocado Testing Framework (which must be +installed separately) in conjunction with a the ``avocado_qemu.QemuSystemTest`` +class, implemented at ``tests/avocado/avocado_qemu``. + +Tests based on ``avocado_qemu.QemuSystemTest`` can easily: + + * Customize the command line arguments given to the convenience + ``self.vm`` attribute (a QEMUMachine instance) + + * Interact with the QEMU monitor, send QMP commands and check + their results + + * Interact with the guest OS, using the convenience console device + (which may be useful to assert the effectiveness and correctness of + command line arguments or QMP commands) + + * Interact with external data files that accompany the test itself + (see ``self.get_data()``) + + * Download (and cache) remote data files, such as firmware and kernel + images + + * Have access to a library of guest OS images (by means of the + ``avocado.utils.vmimage`` library) + + * Make use of various other test related utilities available at the + test class itself and at the utility library: + + - http://avocado-framework.readthedocs.io/en/latest/api/test/avocado.html#avocado.Test + - http://avocado-framework.readthedocs.io/en/latest/api/utils/avocado.utils.html + +Running tests +------------- + +You can run the avocado tests simply by executing: + +.. code:: + + make check-avocado + +This involves the automatic installation, from PyPI, of all the +necessary avocado-framework dependencies into the QEMU venv within the +build tree (at ``./pyvenv``). Test results are also saved within the +build tree (at ``tests/results``). + +Note: the build environment must be using a Python 3 stack, and have +the ``venv`` and ``pip`` packages installed. If necessary, make sure +``configure`` is called with ``--python=`` and that those modules are +available. On Debian and Ubuntu based systems, depending on the +specific version, they may be on packages named ``python3-venv`` and +``python3-pip``. + +It is also possible to run tests based on tags using the +``make check-avocado`` command and the ``AVOCADO_TAGS`` environment +variable: + +.. code:: + + make check-avocado AVOCADO_TAGS=quick + +Note that tags separated with commas have an AND behavior, while tags +separated by spaces have an OR behavior. For more information on Avocado +tags, see: + + https://avocado-framework.readthedocs.io/en/latest/guides/user/chapters/tags.html + +To run a single test file, a couple of them, or a test within a file +using the ``make check-avocado`` command, set the ``AVOCADO_TESTS`` +environment variable with the test files or test names. To run all +tests from a single file, use: + + .. code:: + + make check-avocado AVOCADO_TESTS=$FILEPATH + +The same is valid to run tests from multiple test files: + + .. code:: + + make check-avocado AVOCADO_TESTS='$FILEPATH1 $FILEPATH2' + +To run a single test within a file, use: + + .. code:: + + make check-avocado AVOCADO_TESTS=$FILEPATH:$TESTCLASS.$TESTNAME + +The same is valid to run single tests from multiple test files: + + .. code:: + + make check-avocado AVOCADO_TESTS='$FILEPATH1:$TESTCLASS1.$TESTNAME1 $FILEPATH2:$TESTCLASS2.$TESTNAME2' + +The scripts installed inside the virtual environment may be used +without an "activation". For instance, the Avocado test runner +may be invoked by running: + + .. code:: + + pyvenv/bin/avocado run $OPTION1 $OPTION2 tests/avocado/ + +Note that if ``make check-avocado`` was not executed before, it is +possible to create the Python virtual environment with the dependencies +needed running: + + .. code:: + + make check-venv + +It is also possible to run tests from a single file or a single test within +a test file. To run tests from a single file within the build tree, use: + + .. code:: + + pyvenv/bin/avocado run tests/avocado/$TESTFILE + +To run a single test within a test file, use: + + .. code:: + + pyvenv/bin/avocado run tests/avocado/$TESTFILE:$TESTCLASS.$TESTNAME + +Valid test names are visible in the output from any previous execution +of Avocado or ``make check-avocado``, and can also be queried using: + + .. code:: + + pyvenv/bin/avocado list tests/avocado + +Manual Installation +------------------- + +To manually install Avocado and its dependencies, run: + +.. code:: + + pip install --user avocado-framework + +Alternatively, follow the instructions on this link: + + https://avocado-framework.readthedocs.io/en/latest/guides/user/chapters/installing.html + +Overview +-------- + +The ``tests/avocado/avocado_qemu`` directory provides the +``avocado_qemu`` Python module, containing the ``avocado_qemu.QemuSystemTest`` +class. Here's a simple usage example: + +.. code:: + + from avocado_qemu import QemuSystemTest + + + class Version(QemuSystemTest): + """ + :avocado: tags=quick + """ + def test_qmp_human_info_version(self): + self.vm.launch() + res = self.vm.cmd('human-monitor-command', + command_line='info version') + self.assertRegex(res, r'^(\d+\.\d+\.\d)') + +To execute your test, run: + +.. code:: + + avocado run version.py + +Tests may be classified according to a convention by using docstring +directives such as ``:avocado: tags=TAG1,TAG2``. To run all tests +in the current directory, tagged as "quick", run: + +.. code:: + + avocado run -t quick . + +The ``avocado_qemu.QemuSystemTest`` base test class +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The ``avocado_qemu.QemuSystemTest`` class has a number of characteristics +that are worth being mentioned right away. + +First of all, it attempts to give each test a ready to use QEMUMachine +instance, available at ``self.vm``. Because many tests will tweak the +QEMU command line, launching the QEMUMachine (by using ``self.vm.launch()``) +is left to the test writer. + +The base test class has also support for tests with more than one +QEMUMachine. The way to get machines is through the ``self.get_vm()`` +method which will return a QEMUMachine instance. The ``self.get_vm()`` +method accepts arguments that will be passed to the QEMUMachine creation +and also an optional ``name`` attribute so you can identify a specific +machine and get it more than once through the tests methods. A simple +and hypothetical example follows: + +.. code:: + + from avocado_qemu import QemuSystemTest + + + class MultipleMachines(QemuSystemTest): + def test_multiple_machines(self): + first_machine = self.get_vm() + second_machine = self.get_vm() + self.get_vm(name='third_machine').launch() + + first_machine.launch() + second_machine.launch() + + first_res = first_machine.cmd( + 'human-monitor-command', + command_line='info version') + + second_res = second_machine.cmd( + 'human-monitor-command', + command_line='info version') + + third_res = self.get_vm(name='third_machine').cmd( + 'human-monitor-command', + command_line='info version') + + self.assertEqual(first_res, second_res, third_res) + +At test "tear down", ``avocado_qemu.QemuSystemTest`` handles all the +QEMUMachines shutdown. + +The ``avocado_qemu.LinuxTest`` base test class +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The ``avocado_qemu.LinuxTest`` is further specialization of the +``avocado_qemu.QemuSystemTest`` class, so it contains all the characteristics +of the later plus some extra features. + +First of all, this base class is intended for tests that need to +interact with a fully booted and operational Linux guest. At this +time, it uses a Fedora 31 guest image. The most basic example looks +like this: + +.. code:: + + from avocado_qemu import LinuxTest + + + class SomeTest(LinuxTest): + + def test(self): + self.launch_and_wait() + self.ssh_command('some_command_to_be_run_in_the_guest') + +Please refer to tests that use ``avocado_qemu.LinuxTest`` under +``tests/avocado`` for more examples. + +QEMUMachine +----------- + +The QEMUMachine API is already widely used in the Python iotests, +device-crash-test and other Python scripts. It's a wrapper around the +execution of a QEMU binary, giving its users: + + * the ability to set command line arguments to be given to the QEMU + binary + + * a ready to use QMP connection and interface, which can be used to + send commands and inspect its results, as well as asynchronous + events + + * convenience methods to set commonly used command line arguments in + a more succinct and intuitive way + +QEMU binary selection +^^^^^^^^^^^^^^^^^^^^^ + +The QEMU binary used for the ``self.vm`` QEMUMachine instance will +primarily depend on the value of the ``qemu_bin`` parameter. If it's +not explicitly set, its default value will be the result of a dynamic +probe in the same source tree. A suitable binary will be one that +targets the architecture matching host machine. + +Based on this description, test writers will usually rely on one of +the following approaches: + +1) Set ``qemu_bin``, and use the given binary + +2) Do not set ``qemu_bin``, and use a QEMU binary named like + "qemu-system-${arch}", either in the current + working directory, or in the current source tree. + +The resulting ``qemu_bin`` value will be preserved in the +``avocado_qemu.QemuSystemTest`` as an attribute with the same name. + +Attribute reference +------------------- + +Test +^^^^ + +Besides the attributes and methods that are part of the base +``avocado.Test`` class, the following attributes are available on any +``avocado_qemu.QemuSystemTest`` instance. + +vm +"" + +A QEMUMachine instance, initially configured according to the given +``qemu_bin`` parameter. + +arch +"""" + +The architecture can be used on different levels of the stack, e.g. by +the framework or by the test itself. At the framework level, it will +currently influence the selection of a QEMU binary (when one is not +explicitly given). + +Tests are also free to use this attribute value, for their own needs. +A test may, for instance, use the same value when selecting the +architecture of a kernel or disk image to boot a VM with. + +The ``arch`` attribute will be set to the test parameter of the same +name. If one is not given explicitly, it will either be set to +``None``, or, if the test is tagged with one (and only one) +``:avocado: tags=arch:VALUE`` tag, it will be set to ``VALUE``. + +cpu +""" + +The cpu model that will be set to all QEMUMachine instances created +by the test. + +The ``cpu`` attribute will be set to the test parameter of the same +name. If one is not given explicitly, it will either be set to +``None ``, or, if the test is tagged with one (and only one) +``:avocado: tags=cpu:VALUE`` tag, it will be set to ``VALUE``. + +machine +""""""" + +The machine type that will be set to all QEMUMachine instances created +by the test. + +The ``machine`` attribute will be set to the test parameter of the same +name. If one is not given explicitly, it will either be set to +``None``, or, if the test is tagged with one (and only one) +``:avocado: tags=machine:VALUE`` tag, it will be set to ``VALUE``. + +qemu_bin +"""""""" + +The preserved value of the ``qemu_bin`` parameter or the result of the +dynamic probe for a QEMU binary in the current working directory or +source tree. + +LinuxTest +^^^^^^^^^ + +Besides the attributes present on the ``avocado_qemu.QemuSystemTest`` base +class, the ``avocado_qemu.LinuxTest`` adds the following attributes: + +distro +"""""" + +The name of the Linux distribution used as the guest image for the +test. The name should match the **Provider** column on the list +of images supported by the avocado.utils.vmimage library: + +https://avocado-framework.readthedocs.io/en/latest/guides/writer/libs/vmimage.html#supported-images + +distro_version +"""""""""""""" + +The version of the Linux distribution as the guest image for the +test. The name should match the **Version** column on the list +of images supported by the avocado.utils.vmimage library: + +https://avocado-framework.readthedocs.io/en/latest/guides/writer/libs/vmimage.html#supported-images + +distro_checksum +""""""""""""""" + +The sha256 hash of the guest image file used for the test. + +If this value is not set in the code or by a test parameter (with the +same name), no validation on the integrity of the image will be +performed. + +Parameter reference +------------------- + +To understand how Avocado parameters are accessed by tests, and how +they can be passed to tests, please refer to:: + + https://avocado-framework.readthedocs.io/en/latest/guides/writer/chapters/writing.html#accessing-test-parameters + +Parameter values can be easily seen in the log files, and will look +like the following: + +.. code:: + + PARAMS (key=qemu_bin, path=*, default=./qemu-system-x86_64) => './qemu-system-x86_64 + +Test +^^^^ + +arch +"""" + +The architecture that will influence the selection of a QEMU binary +(when one is not explicitly given). + +Tests are also free to use this parameter value, for their own needs. +A test may, for instance, use the same value when selecting the +architecture of a kernel or disk image to boot a VM with. + +This parameter has a direct relation with the ``arch`` attribute. If +not given, it will default to None. + +cpu +""" + +The cpu model that will be set to all QEMUMachine instances created +by the test. + +machine +""""""" + +The machine type that will be set to all QEMUMachine instances created +by the test. + +qemu_bin +"""""""" + +The exact QEMU binary to be used on QEMUMachine. + +LinuxTest +^^^^^^^^^ + +Besides the parameters present on the ``avocado_qemu.QemuSystemTest`` base +class, the ``avocado_qemu.LinuxTest`` adds the following parameters: + +distro +"""""" + +The name of the Linux distribution used as the guest image for the +test. The name should match the **Provider** column on the list +of images supported by the avocado.utils.vmimage library: + +https://avocado-framework.readthedocs.io/en/latest/guides/writer/libs/vmimage.html#supported-images + +distro_version +"""""""""""""" + +The version of the Linux distribution as the guest image for the +test. The name should match the **Version** column on the list +of images supported by the avocado.utils.vmimage library: + +https://avocado-framework.readthedocs.io/en/latest/guides/writer/libs/vmimage.html#supported-images + +distro_checksum +""""""""""""""" + +The sha256 hash of the guest image file used for the test. + +If this value is not set in the code or by this parameter no +validation on the integrity of the image will be performed. + +Skipping tests +-------------- + +The Avocado framework provides Python decorators which allow for easily skip +tests running under certain conditions. For example, on the lack of a binary +on the test system or when the running environment is a CI system. For further +information about those decorators, please refer to:: + + https://avocado-framework.readthedocs.io/en/latest/guides/writer/chapters/writing.html#skipping-tests + +While the conditions for skipping tests are often specifics of each one, there +are recurring scenarios identified by the QEMU developers and the use of +environment variables became a kind of standard way to enable/disable tests. + +Here is a list of the most used variables: + +AVOCADO_ALLOW_LARGE_STORAGE +^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Tests which are going to fetch or produce assets considered *large* are not +going to run unless that ``AVOCADO_ALLOW_LARGE_STORAGE=1`` is exported on +the environment. + +The definition of *large* is a bit arbitrary here, but it usually means an +asset which occupies at least 1GB of size on disk when uncompressed. + +SPEED +^^^^^ +Tests which have a long runtime will not be run unless ``SPEED=slow`` is +exported on the environment. + +The definition of *long* is a bit arbitrary here, and it depends on the +usefulness of the test too. A unique test is worth spending more time on, +small variations on existing tests perhaps less so. As a rough guide, +a test or set of similar tests which take more than 100 seconds to +complete. + +AVOCADO_ALLOW_UNTRUSTED_CODE +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +There are tests which will boot a kernel image or firmware that can be +considered not safe to run on the developer's workstation, thus they are +skipped by default. The definition of *not safe* is also arbitrary but +usually it means a blob which either its source or build process aren't +public available. + +You should export ``AVOCADO_ALLOW_UNTRUSTED_CODE=1`` on the environment in +order to allow tests which make use of those kind of assets. + +AVOCADO_TIMEOUT_EXPECTED +^^^^^^^^^^^^^^^^^^^^^^^^ +The Avocado framework has a timeout mechanism which interrupts tests to avoid the +test suite of getting stuck. The timeout value can be set via test parameter or +property defined in the test class, for further details:: + + https://avocado-framework.readthedocs.io/en/latest/guides/writer/chapters/writing.html#setting-a-test-timeout + +Even though the timeout can be set by the test developer, there are some tests +that may not have a well-defined limit of time to finish under certain +conditions. For example, tests that take longer to execute when QEMU is +compiled with debug flags. Therefore, the ``AVOCADO_TIMEOUT_EXPECTED`` variable +has been used to determine whether those tests should run or not. + +QEMU_TEST_FLAKY_TESTS +^^^^^^^^^^^^^^^^^^^^^ +Some tests are not working reliably and thus are disabled by default. +This includes tests that don't run reliably on GitLab's CI which +usually expose real issues that are rarely seen on developer machines +due to the constraints of the CI environment. If you encounter a +similar situation then raise a bug and then mark the test as shown on +the code snippet below: + +.. code:: + + # See https://gitlab.com/qemu-project/qemu/-/issues/nnnn + @skipUnless(os.getenv('QEMU_TEST_FLAKY_TESTS'), 'Test is unstable on GitLab') + def test(self): + do_something() + +You can also add ``:avocado: tags=flaky`` to the test meta-data so +only the flaky tests can be run as a group: + +.. code:: + + env QEMU_TEST_FLAKY_TESTS=1 ./pyvenv/bin/avocado \ + run tests/avocado -filter-by-tags=flaky + +Tests should not live in this state forever and should either be fixed +or eventually removed. + + +Uninstalling Avocado +-------------------- + +If you've followed the manual installation instructions above, you can +easily uninstall Avocado. Start by listing the packages you have +installed:: + + pip list --user + +And remove any package you want with:: + + pip uninstall <package_name> + +If you've used ``make check-avocado``, the Python virtual environment where +Avocado is installed will be cleaned up as part of ``make check-clean``. diff --git a/docs/devel/ci-definitions.rst.inc b/docs/devel/testing/ci-definitions.rst.inc index 6d5c6fd9f2..6d5c6fd9f2 100644 --- a/docs/devel/ci-definitions.rst.inc +++ b/docs/devel/testing/ci-definitions.rst.inc diff --git a/docs/devel/ci-jobs.rst.inc b/docs/devel/testing/ci-jobs.rst.inc index 3756bbe355..3756bbe355 100644 --- a/docs/devel/ci-jobs.rst.inc +++ b/docs/devel/testing/ci-jobs.rst.inc diff --git a/docs/devel/ci-runners.rst.inc b/docs/devel/testing/ci-runners.rst.inc index 67b23d3719..67b23d3719 100644 --- a/docs/devel/ci-runners.rst.inc +++ b/docs/devel/testing/ci-runners.rst.inc diff --git a/docs/devel/ci.rst b/docs/devel/testing/ci.rst index ed88a2010b..ed88a2010b 100644 --- a/docs/devel/ci.rst +++ b/docs/devel/testing/ci.rst diff --git a/docs/devel/testing/functional.rst b/docs/devel/testing/functional.rst new file mode 100644 index 0000000000..bf6f1bb81e --- /dev/null +++ b/docs/devel/testing/functional.rst @@ -0,0 +1,338 @@ +.. _checkfunctional-ref: + +Functional testing with Python +============================== + +The ``tests/functional`` directory hosts functional tests written in +Python. They are usually higher level tests, and may interact with +external resources and with various guest operating systems. +The functional tests have initially evolved from the Avocado tests, so there +is a lot of similarity to those tests here (see :ref:`checkavocado-ref` for +details about the Avocado tests). + +The tests should be written in the style of the Python `unittest`_ framework, +using stdio for the TAP protocol. The folder ``tests/functional/qemu_test`` +provides classes (e.g. the ``QemuBaseTest``, ``QemuUserTest`` and the +``QemuSystemTest`` classes) and utility functions that help to get your test +into the right shape, e.g. by replacing the 'stdout' python object to redirect +the normal output of your test to stderr instead. + +Note that if you don't use one of the QemuBaseTest based classes for your +test, or if you spawn subprocesses from your test, you have to make sure +that there is no TAP-incompatible output written to stdio, e.g. either by +prefixing every line with a "# " to mark the output as a TAP comment, or +e.g. by capturing the stdout output of subprocesses (redirecting it to +stderr is OK). + +Tests based on ``qemu_test.QemuSystemTest`` can easily: + + * Customize the command line arguments given to the convenience + ``self.vm`` attribute (a QEMUMachine instance) + + * Interact with the QEMU monitor, send QMP commands and check + their results + + * Interact with the guest OS, using the convenience console device + (which may be useful to assert the effectiveness and correctness of + command line arguments or QMP commands) + + * Download (and cache) remote data files, such as firmware and kernel + images + +Running tests +------------- + +You can run the functional tests simply by executing: + +.. code:: + + make check-functional + +It is also possible to run tests for a certain target only, for example +the following line will only run the tests for the x86_64 target: + +.. code:: + + make check-functional-x86_64 + +To run a single test file without the meson test runner, you can also +execute the file directly by specifying two environment variables first, +the PYTHONPATH that has to include the python folder and the tests/functional +folder of the source tree, and QEMU_TEST_QEMU_BINARY that has to point +to the QEMU binary that should be used for the test, for example:: + + $ export PYTHONPATH=../python:../tests/functional + $ export QEMU_TEST_QEMU_BINARY=$PWD/qemu-system-x86_64 + $ python3 ../tests/functional/test_file.py + +Overview +-------- + +The ``tests/functional/qemu_test`` directory provides the ``qemu_test`` +Python module, containing the ``qemu_test.QemuSystemTest`` class. +Here is a simple usage example: + +.. code:: + + #!/usr/bin/env python3 + + from qemu_test import QemuSystemTest + + class Version(QemuSystemTest): + + def test_qmp_human_info_version(self): + self.vm.launch() + res = self.vm.cmd('human-monitor-command', + command_line='info version') + self.assertRegex(res, r'^(\d+\.\d+\.\d)') + + if __name__ == '__main__': + QemuSystemTest.main() + +By providing the "hash bang" line at the beginning of the script, marking +the file as executable and by calling into QemuSystemTest.main(), the test +can also be run stand-alone, without a test runner. OTOH when run via a test +runner, the QemuSystemTest.main() function takes care of running the test +functions in the right fassion (e.g. with TAP output that is required by the +meson test runner). + +The ``qemu_test.QemuSystemTest`` base test class +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The ``qemu_test.QemuSystemTest`` class has a number of characteristics +that are worth being mentioned. + +First of all, it attempts to give each test a ready to use QEMUMachine +instance, available at ``self.vm``. Because many tests will tweak the +QEMU command line, launching the QEMUMachine (by using ``self.vm.launch()``) +is left to the test writer. + +The base test class has also support for tests with more than one +QEMUMachine. The way to get machines is through the ``self.get_vm()`` +method which will return a QEMUMachine instance. The ``self.get_vm()`` +method accepts arguments that will be passed to the QEMUMachine creation +and also an optional ``name`` attribute so you can identify a specific +machine and get it more than once through the tests methods. A simple +and hypothetical example follows: + +.. code:: + + from qemu_test import QemuSystemTest + + class MultipleMachines(QemuSystemTest): + def test_multiple_machines(self): + first_machine = self.get_vm() + second_machine = self.get_vm() + self.get_vm(name='third_machine').launch() + + first_machine.launch() + second_machine.launch() + + first_res = first_machine.cmd( + 'human-monitor-command', + command_line='info version') + + second_res = second_machine.cmd( + 'human-monitor-command', + command_line='info version') + + third_res = self.get_vm(name='third_machine').cmd( + 'human-monitor-command', + command_line='info version') + + self.assertEqual(first_res, second_res, third_res) + +At test "tear down", ``qemu_test.QemuSystemTest`` handles all the QEMUMachines +shutdown. + +QEMUMachine +----------- + +The QEMUMachine API is already widely used in the Python iotests, +device-crash-test and other Python scripts. It's a wrapper around the +execution of a QEMU binary, giving its users: + + * the ability to set command line arguments to be given to the QEMU + binary + + * a ready to use QMP connection and interface, which can be used to + send commands and inspect its results, as well as asynchronous + events + + * convenience methods to set commonly used command line arguments in + a more succinct and intuitive way + +QEMU binary selection +^^^^^^^^^^^^^^^^^^^^^ + +The QEMU binary used for the ``self.vm`` QEMUMachine instance will +primarily depend on the value of the ``qemu_bin`` class attribute. +If it is not explicitly set by the test code, its default value will +be the result the QEMU_TEST_QEMU_BINARY environment variable. + +Attribute reference +------------------- + +QemuBaseTest +^^^^^^^^^^^^ + +The following attributes are available on any ``qemu_test.QemuBaseTest`` +instance. + +arch +"""" + +The target architecture of the QEMU binary. + +Tests are also free to use this attribute value, for their own needs. +A test may, for instance, use this value when selecting the architecture +of a kernel or disk image to boot a VM with. + +qemu_bin +"""""""" + +The preserved value of the ``QEMU_TEST_QEMU_BINARY`` environment +variable. + +QemuUserTest +^^^^^^^^^^^^ + +The QemuUserTest class can be used for running an executable via the +usermode emulation binaries. + +QemuSystemTest +^^^^^^^^^^^^^^ + +The QemuSystemTest class can be used for running tests via one of the +qemu-system-* binaries. + +vm +"" + +A QEMUMachine instance, initially configured according to the given +``qemu_bin`` parameter. + +cpu +""" + +The cpu model that will be set to all QEMUMachine instances created +by the test. + +machine +""""""" + +The machine type that will be set to all QEMUMachine instances created +by the test. By using the set_machine() function of the QemuSystemTest +class to set this attribute, you can automatically check whether the +machine is available to skip the test in case it is not built into the +QEMU binary. + +Asset handling +-------------- + +Many functional tests download assets (e.g. Linux kernels, initrds, +firmware images, etc.) from the internet to be able to run tests with +them. This imposes additional challenges to the test framework. + +First there is the the problem that some people might not have an +unconstrained internet connection, so such tests should not be run by +default when running ``make check``. To accomplish this situation, +the tests that download files should only be added to the "thorough" +speed mode in the meson.build file, while the "quick" speed mode is +fine for functional tests that can be run without downloading files. +``make check`` then only runs the quick functional tests along with +the other quick tests from the other test suites. If you choose to +run only run ``make check-functional``, the "thorough" tests will be +executed, too. And to run all functional tests along with the others, +you can use something like:: + + make -j$(nproc) check SPEED=thorough + +The second problem with downloading files from the internet are time +constraints. The time for downloading files should not be taken into +account when the test is running and the timeout of the test is ticking +(since downloading can be very slow, depending on the network bandwidth). +This problem is solved by downloading the assets ahead of time, before +the tests are run. This pre-caching is done with the qemu_test.Asset +class. To use it in your test, declare an asset in your test class with +its URL and SHA256 checksum like this:: + + ASSET_somename = ( + ('https://www.qemu.org/assets/images/qemu_head_200.png'), + '34b74cad46ea28a2966c1d04e102510daf1fd73e6582b6b74523940d5da029dd') + +In your test function, you can then get the file name of the cached +asset like this:: + + def test_function(self): + file_path = self.ASSET_somename.fetch() + +The pre-caching will be done automatically when running +``make check-functional`` (but not when running e.g. +``make check-functional-<target>``). In case you just want to download +the assets without running the tests, you can do so by running:: + + make precache-functional + +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. + +Skipping tests +-------------- + +Since the test framework is based on the common Python unittest framework, +you can use the usual Python decorators which allow for easily skipping +tests running under certain conditions, for example, on the lack of a binary +on the test system or when the running environment is a CI system. For further +information about those decorators, please refer to: + + https://docs.python.org/3/library/unittest.html#skipping-tests-and-expected-failures + +While the conditions for skipping tests are often specifics of each one, there +are recurring scenarios identified by the QEMU developers and the use of +environment variables became a kind of standard way to enable/disable tests. + +Here is a list of the most used variables: + +QEMU_TEST_ALLOW_LARGE_STORAGE +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Tests which are going to fetch or produce assets considered *large* are not +going to run unless that ``QEMU_TEST_ALLOW_LARGE_STORAGE=1`` is exported on +the environment. + +The definition of *large* is a bit arbitrary here, but it usually means an +asset which occupies at least 1GB of size on disk when uncompressed. + +QEMU_TEST_ALLOW_UNTRUSTED_CODE +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +There are tests which will boot a kernel image or firmware that can be +considered not safe to run on the developer's workstation, thus they are +skipped by default. The definition of *not safe* is also arbitrary but +usually it means a blob which either its source or build process aren't +public available. + +You should export ``QEMU_TEST_ALLOW_UNTRUSTED_CODE=1`` on the environment in +order to allow tests which make use of those kind of assets. + +QEMU_TEST_FLAKY_TESTS +^^^^^^^^^^^^^^^^^^^^^ +Some tests are not working reliably and thus are disabled by default. +This includes tests that don't run reliably on GitLab's CI which +usually expose real issues that are rarely seen on developer machines +due to the constraints of the CI environment. If you encounter a +similar situation then raise a bug and then mark the test as shown on +the code snippet below: + +.. code:: + + # See https://gitlab.com/qemu-project/qemu/-/issues/nnnn + @skipUnless(os.getenv('QEMU_TEST_FLAKY_TESTS'), 'Test is unstable on GitLab') + def test(self): + do_something() + +Tests should not live in this state forever and should either be fixed +or eventually removed. + + +.. _unittest: https://docs.python.org/3/library/unittest.html diff --git a/docs/devel/fuzzing.rst b/docs/devel/testing/fuzzing.rst index 3bfcb33fc4..3bfcb33fc4 100644 --- a/docs/devel/fuzzing.rst +++ b/docs/devel/testing/fuzzing.rst diff --git a/docs/devel/testing/index.rst b/docs/devel/testing/index.rst new file mode 100644 index 0000000000..45eb4a7181 --- /dev/null +++ b/docs/devel/testing/index.rst @@ -0,0 +1,16 @@ +Testing QEMU +------------ + +Details about how to test QEMU and how it is integrated into our CI +testing infrastructure. + +.. toctree:: + :maxdepth: 3 + + main + qtest + functional + avocado + acpi-bits + ci + fuzzing diff --git a/docs/devel/testing.rst b/docs/devel/testing/main.rst index af73d3d64f..e9921a4b10 100644 --- a/docs/devel/testing.rst +++ b/docs/devel/testing/main.rst @@ -862,46 +862,24 @@ supported. To start the fuzzer, run Alternatively, some command different from ``qemu-img info`` can be tested, by changing the ``-c`` option. -Integration tests using the Avocado Framework ---------------------------------------------- - -The ``tests/avocado`` directory hosts integration tests. They're usually -higher level tests, and may interact with external resources and with -various guest operating systems. - -These tests are written using the Avocado Testing Framework (which must -be installed separately) in conjunction with a the ``avocado_qemu.Test`` -class, implemented at ``tests/avocado/avocado_qemu``. - -Tests based on ``avocado_qemu.Test`` can easily: - - * Customize the command line arguments given to the convenience - ``self.vm`` attribute (a QEMUMachine instance) - - * Interact with the QEMU monitor, send QMP commands and check - their results - - * Interact with the guest OS, using the convenience console device - (which may be useful to assert the effectiveness and correctness of - command line arguments or QMP commands) +Functional tests using Python +----------------------------- - * Interact with external data files that accompany the test itself - (see ``self.get_data()``) +The ``tests/functional`` directory hosts functional tests written in +Python. You can run the functional tests simply by executing: - * Download (and cache) remote data files, such as firmware and kernel - images +.. code:: - * Have access to a library of guest OS images (by means of the - ``avocado.utils.vmimage`` library) + make check-functional - * Make use of various other test related utilities available at the - test class itself and at the utility library: +See :ref:`checkfunctional-ref` for more details. - - http://avocado-framework.readthedocs.io/en/latest/api/test/avocado.html#avocado.Test - - http://avocado-framework.readthedocs.io/en/latest/api/utils/avocado.utils.html +Integration tests using the Avocado Framework +--------------------------------------------- -Running tests -~~~~~~~~~~~~~ +The ``tests/avocado`` directory hosts integration tests. They're usually +higher level tests, and may interact with external resources and with +various guest operating systems. You can run the avocado tests simply by executing: @@ -909,537 +887,8 @@ You can run the avocado tests simply by executing: make check-avocado -This involves the automatic installation, from PyPI, of all the -necessary avocado-framework dependencies into the QEMU venv within the -build tree (at ``./pyvenv``). Test results are also saved within the -build tree (at ``tests/results``). - -Note: the build environment must be using a Python 3 stack, and have -the ``venv`` and ``pip`` packages installed. If necessary, make sure -``configure`` is called with ``--python=`` and that those modules are -available. On Debian and Ubuntu based systems, depending on the -specific version, they may be on packages named ``python3-venv`` and -``python3-pip``. - -It is also possible to run tests based on tags using the -``make check-avocado`` command and the ``AVOCADO_TAGS`` environment -variable: - -.. code:: - - make check-avocado AVOCADO_TAGS=quick - -Note that tags separated with commas have an AND behavior, while tags -separated by spaces have an OR behavior. For more information on Avocado -tags, see: - - https://avocado-framework.readthedocs.io/en/latest/guides/user/chapters/tags.html - -To run a single test file, a couple of them, or a test within a file -using the ``make check-avocado`` command, set the ``AVOCADO_TESTS`` -environment variable with the test files or test names. To run all -tests from a single file, use: - - .. code:: - - make check-avocado AVOCADO_TESTS=$FILEPATH - -The same is valid to run tests from multiple test files: - - .. code:: - - make check-avocado AVOCADO_TESTS='$FILEPATH1 $FILEPATH2' - -To run a single test within a file, use: - - .. code:: - - make check-avocado AVOCADO_TESTS=$FILEPATH:$TESTCLASS.$TESTNAME - -The same is valid to run single tests from multiple test files: - - .. code:: - - make check-avocado AVOCADO_TESTS='$FILEPATH1:$TESTCLASS1.$TESTNAME1 $FILEPATH2:$TESTCLASS2.$TESTNAME2' - -The scripts installed inside the virtual environment may be used -without an "activation". For instance, the Avocado test runner -may be invoked by running: - - .. code:: - - pyvenv/bin/avocado run $OPTION1 $OPTION2 tests/avocado/ - -Note that if ``make check-avocado`` was not executed before, it is -possible to create the Python virtual environment with the dependencies -needed running: - - .. code:: - - make check-venv - -It is also possible to run tests from a single file or a single test within -a test file. To run tests from a single file within the build tree, use: - - .. code:: - - pyvenv/bin/avocado run tests/avocado/$TESTFILE - -To run a single test within a test file, use: - - .. code:: - - pyvenv/bin/avocado run tests/avocado/$TESTFILE:$TESTCLASS.$TESTNAME - -Valid test names are visible in the output from any previous execution -of Avocado or ``make check-avocado``, and can also be queried using: - - .. code:: - - pyvenv/bin/avocado list tests/avocado - -Manual Installation -~~~~~~~~~~~~~~~~~~~ - -To manually install Avocado and its dependencies, run: - -.. code:: - - pip install --user avocado-framework - -Alternatively, follow the instructions on this link: - - https://avocado-framework.readthedocs.io/en/latest/guides/user/chapters/installing.html - -Overview -~~~~~~~~ - -The ``tests/avocado/avocado_qemu`` directory provides the -``avocado_qemu`` Python module, containing the ``avocado_qemu.Test`` -class. Here's a simple usage example: - -.. code:: - - from avocado_qemu import QemuSystemTest - - - class Version(QemuSystemTest): - """ - :avocado: tags=quick - """ - def test_qmp_human_info_version(self): - self.vm.launch() - res = self.vm.cmd('human-monitor-command', - command_line='info version') - self.assertRegex(res, r'^(\d+\.\d+\.\d)') - -To execute your test, run: - -.. code:: - - avocado run version.py - -Tests may be classified according to a convention by using docstring -directives such as ``:avocado: tags=TAG1,TAG2``. To run all tests -in the current directory, tagged as "quick", run: - -.. code:: - - avocado run -t quick . - -The ``avocado_qemu.Test`` base test class -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -The ``avocado_qemu.Test`` class has a number of characteristics that -are worth being mentioned right away. - -First of all, it attempts to give each test a ready to use QEMUMachine -instance, available at ``self.vm``. Because many tests will tweak the -QEMU command line, launching the QEMUMachine (by using ``self.vm.launch()``) -is left to the test writer. - -The base test class has also support for tests with more than one -QEMUMachine. The way to get machines is through the ``self.get_vm()`` -method which will return a QEMUMachine instance. The ``self.get_vm()`` -method accepts arguments that will be passed to the QEMUMachine creation -and also an optional ``name`` attribute so you can identify a specific -machine and get it more than once through the tests methods. A simple -and hypothetical example follows: - -.. code:: - - from avocado_qemu import QemuSystemTest - - - class MultipleMachines(QemuSystemTest): - def test_multiple_machines(self): - first_machine = self.get_vm() - second_machine = self.get_vm() - self.get_vm(name='third_machine').launch() - - first_machine.launch() - second_machine.launch() - - first_res = first_machine.cmd( - 'human-monitor-command', - command_line='info version') - - second_res = second_machine.cmd( - 'human-monitor-command', - command_line='info version') - - third_res = self.get_vm(name='third_machine').cmd( - 'human-monitor-command', - command_line='info version') - - self.assertEqual(first_res, second_res, third_res) - -At test "tear down", ``avocado_qemu.Test`` handles all the QEMUMachines -shutdown. - -The ``avocado_qemu.LinuxTest`` base test class -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -The ``avocado_qemu.LinuxTest`` is further specialization of the -``avocado_qemu.Test`` class, so it contains all the characteristics of -the later plus some extra features. - -First of all, this base class is intended for tests that need to -interact with a fully booted and operational Linux guest. At this -time, it uses a Fedora 31 guest image. The most basic example looks -like this: - -.. code:: - - from avocado_qemu import LinuxTest - - - class SomeTest(LinuxTest): - - def test(self): - self.launch_and_wait() - self.ssh_command('some_command_to_be_run_in_the_guest') - -Please refer to tests that use ``avocado_qemu.LinuxTest`` under -``tests/avocado`` for more examples. - -QEMUMachine -~~~~~~~~~~~ - -The QEMUMachine API is already widely used in the Python iotests, -device-crash-test and other Python scripts. It's a wrapper around the -execution of a QEMU binary, giving its users: - - * the ability to set command line arguments to be given to the QEMU - binary - - * a ready to use QMP connection and interface, which can be used to - send commands and inspect its results, as well as asynchronous - events - - * convenience methods to set commonly used command line arguments in - a more succinct and intuitive way - -QEMU binary selection -^^^^^^^^^^^^^^^^^^^^^ - -The QEMU binary used for the ``self.vm`` QEMUMachine instance will -primarily depend on the value of the ``qemu_bin`` parameter. If it's -not explicitly set, its default value will be the result of a dynamic -probe in the same source tree. A suitable binary will be one that -targets the architecture matching host machine. - -Based on this description, test writers will usually rely on one of -the following approaches: - -1) Set ``qemu_bin``, and use the given binary - -2) Do not set ``qemu_bin``, and use a QEMU binary named like - "qemu-system-${arch}", either in the current - working directory, or in the current source tree. - -The resulting ``qemu_bin`` value will be preserved in the -``avocado_qemu.Test`` as an attribute with the same name. - -Attribute reference -~~~~~~~~~~~~~~~~~~~ - -Test -^^^^ - -Besides the attributes and methods that are part of the base -``avocado.Test`` class, the following attributes are available on any -``avocado_qemu.Test`` instance. - -vm -'' - -A QEMUMachine instance, initially configured according to the given -``qemu_bin`` parameter. - -arch -'''' - -The architecture can be used on different levels of the stack, e.g. by -the framework or by the test itself. At the framework level, it will -currently influence the selection of a QEMU binary (when one is not -explicitly given). - -Tests are also free to use this attribute value, for their own needs. -A test may, for instance, use the same value when selecting the -architecture of a kernel or disk image to boot a VM with. - -The ``arch`` attribute will be set to the test parameter of the same -name. If one is not given explicitly, it will either be set to -``None``, or, if the test is tagged with one (and only one) -``:avocado: tags=arch:VALUE`` tag, it will be set to ``VALUE``. - -cpu -''' - -The cpu model that will be set to all QEMUMachine instances created -by the test. - -The ``cpu`` attribute will be set to the test parameter of the same -name. If one is not given explicitly, it will either be set to -``None ``, or, if the test is tagged with one (and only one) -``:avocado: tags=cpu:VALUE`` tag, it will be set to ``VALUE``. - -machine -''''''' - -The machine type that will be set to all QEMUMachine instances created -by the test. - -The ``machine`` attribute will be set to the test parameter of the same -name. If one is not given explicitly, it will either be set to -``None``, or, if the test is tagged with one (and only one) -``:avocado: tags=machine:VALUE`` tag, it will be set to ``VALUE``. - -qemu_bin -'''''''' - -The preserved value of the ``qemu_bin`` parameter or the result of the -dynamic probe for a QEMU binary in the current working directory or -source tree. - -LinuxTest -^^^^^^^^^ - -Besides the attributes present on the ``avocado_qemu.Test`` base -class, the ``avocado_qemu.LinuxTest`` adds the following attributes: - -distro -'''''' - -The name of the Linux distribution used as the guest image for the -test. The name should match the **Provider** column on the list -of images supported by the avocado.utils.vmimage library: - -https://avocado-framework.readthedocs.io/en/latest/guides/writer/libs/vmimage.html#supported-images - -distro_version -'''''''''''''' - -The version of the Linux distribution as the guest image for the -test. The name should match the **Version** column on the list -of images supported by the avocado.utils.vmimage library: - -https://avocado-framework.readthedocs.io/en/latest/guides/writer/libs/vmimage.html#supported-images - -distro_checksum -''''''''''''''' - -The sha256 hash of the guest image file used for the test. - -If this value is not set in the code or by a test parameter (with the -same name), no validation on the integrity of the image will be -performed. - -Parameter reference -~~~~~~~~~~~~~~~~~~~ - -To understand how Avocado parameters are accessed by tests, and how -they can be passed to tests, please refer to:: - - https://avocado-framework.readthedocs.io/en/latest/guides/writer/chapters/writing.html#accessing-test-parameters - -Parameter values can be easily seen in the log files, and will look -like the following: - -.. code:: - - PARAMS (key=qemu_bin, path=*, default=./qemu-system-x86_64) => './qemu-system-x86_64 - -Test -^^^^ - -arch -'''' - -The architecture that will influence the selection of a QEMU binary -(when one is not explicitly given). - -Tests are also free to use this parameter value, for their own needs. -A test may, for instance, use the same value when selecting the -architecture of a kernel or disk image to boot a VM with. - -This parameter has a direct relation with the ``arch`` attribute. If -not given, it will default to None. - -cpu -''' - -The cpu model that will be set to all QEMUMachine instances created -by the test. - -machine -''''''' - -The machine type that will be set to all QEMUMachine instances created -by the test. - -qemu_bin -'''''''' - -The exact QEMU binary to be used on QEMUMachine. - -LinuxTest -^^^^^^^^^ - -Besides the parameters present on the ``avocado_qemu.Test`` base -class, the ``avocado_qemu.LinuxTest`` adds the following parameters: - -distro -'''''' - -The name of the Linux distribution used as the guest image for the -test. The name should match the **Provider** column on the list -of images supported by the avocado.utils.vmimage library: - -https://avocado-framework.readthedocs.io/en/latest/guides/writer/libs/vmimage.html#supported-images - -distro_version -'''''''''''''' - -The version of the Linux distribution as the guest image for the -test. The name should match the **Version** column on the list -of images supported by the avocado.utils.vmimage library: - -https://avocado-framework.readthedocs.io/en/latest/guides/writer/libs/vmimage.html#supported-images - -distro_checksum -''''''''''''''' - -The sha256 hash of the guest image file used for the test. - -If this value is not set in the code or by this parameter no -validation on the integrity of the image will be performed. - -Skipping tests -~~~~~~~~~~~~~~ - -The Avocado framework provides Python decorators which allow for easily skip -tests running under certain conditions. For example, on the lack of a binary -on the test system or when the running environment is a CI system. For further -information about those decorators, please refer to:: - - https://avocado-framework.readthedocs.io/en/latest/guides/writer/chapters/writing.html#skipping-tests - -While the conditions for skipping tests are often specifics of each one, there -are recurring scenarios identified by the QEMU developers and the use of -environment variables became a kind of standard way to enable/disable tests. - -Here is a list of the most used variables: - -AVOCADO_ALLOW_LARGE_STORAGE -^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Tests which are going to fetch or produce assets considered *large* are not -going to run unless that ``AVOCADO_ALLOW_LARGE_STORAGE=1`` is exported on -the environment. - -The definition of *large* is a bit arbitrary here, but it usually means an -asset which occupies at least 1GB of size on disk when uncompressed. - -SPEED -^^^^^ -Tests which have a long runtime will not be run unless ``SPEED=slow`` is -exported on the environment. - -The definition of *long* is a bit arbitrary here, and it depends on the -usefulness of the test too. A unique test is worth spending more time on, -small variations on existing tests perhaps less so. As a rough guide, -a test or set of similar tests which take more than 100 seconds to -complete. - -AVOCADO_ALLOW_UNTRUSTED_CODE -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -There are tests which will boot a kernel image or firmware that can be -considered not safe to run on the developer's workstation, thus they are -skipped by default. The definition of *not safe* is also arbitrary but -usually it means a blob which either its source or build process aren't -public available. - -You should export ``AVOCADO_ALLOW_UNTRUSTED_CODE=1`` on the environment in -order to allow tests which make use of those kind of assets. - -AVOCADO_TIMEOUT_EXPECTED -^^^^^^^^^^^^^^^^^^^^^^^^ -The Avocado framework has a timeout mechanism which interrupts tests to avoid the -test suite of getting stuck. The timeout value can be set via test parameter or -property defined in the test class, for further details:: - - https://avocado-framework.readthedocs.io/en/latest/guides/writer/chapters/writing.html#setting-a-test-timeout - -Even though the timeout can be set by the test developer, there are some tests -that may not have a well-defined limit of time to finish under certain -conditions. For example, tests that take longer to execute when QEMU is -compiled with debug flags. Therefore, the ``AVOCADO_TIMEOUT_EXPECTED`` variable -has been used to determine whether those tests should run or not. - -QEMU_TEST_FLAKY_TESTS -^^^^^^^^^^^^^^^^^^^^^ -Some tests are not working reliably and thus are disabled by default. -This includes tests that don't run reliably on GitLab's CI which -usually expose real issues that are rarely seen on developer machines -due to the constraints of the CI environment. If you encounter a -similar situation then raise a bug and then mark the test as shown on -the code snippet below: - -.. code:: - - # See https://gitlab.com/qemu-project/qemu/-/issues/nnnn - @skipUnless(os.getenv('QEMU_TEST_FLAKY_TESTS'), 'Test is unstable on GitLab') - def test(self): - do_something() - -You can also add ``:avocado: tags=flaky`` to the test meta-data so -only the flaky tests can be run as a group: - -.. code:: - - env QEMU_TEST_FLAKY_TESTS=1 ./pyvenv/bin/avocado \ - run tests/avocado -filter-by-tags=flaky - -Tests should not live in this state forever and should either be fixed -or eventually removed. - - -Uninstalling Avocado -~~~~~~~~~~~~~~~~~~~~ - -If you've followed the manual installation instructions above, you can -easily uninstall Avocado. Start by listing the packages you have -installed:: - - pip list --user - -And remove any package you want with:: - - pip uninstall <package_name> +See :ref:`checkavocado-ref` for more details. -If you've used ``make check-avocado``, the Python virtual environment where -Avocado is installed will be cleaned up as part of ``make check-clean``. .. _checktcg-ref: diff --git a/docs/devel/qgraph.rst b/docs/devel/testing/qgraph.rst index 43342d9d65..43342d9d65 100644 --- a/docs/devel/qgraph.rst +++ b/docs/devel/testing/qgraph.rst diff --git a/docs/devel/qtest.rst b/docs/devel/testing/qtest.rst index c5b8546b3e..c5b8546b3e 100644 --- a/docs/devel/qtest.rst +++ b/docs/devel/testing/qtest.rst diff --git a/docs/system/arm/emulation.rst b/docs/system/arm/emulation.rst index 3ab6e72667..35f52a54b1 100644 --- a/docs/system/arm/emulation.rst +++ b/docs/system/arm/emulation.rst @@ -45,6 +45,7 @@ the following architecture extensions: - FEAT_DotProd (Advanced SIMD dot product instructions) - FEAT_DoubleFault (Double Fault Extension) - FEAT_E0PD (Preventing EL0 access to halves of address maps) +- FEAT_EBF16 (AArch64 Extended BFloat16 instructions) - FEAT_ECV (Enhanced Counter Virtualization) - FEAT_EL0 (Support for execution at EL0) - FEAT_EL1 (Support for execution at EL1) diff --git a/hw/arm/boot.c b/hw/arm/boot.c index d480a7da02..5301d8d318 100644 --- a/hw/arm/boot.c +++ b/hw/arm/boot.c @@ -799,14 +799,18 @@ static ssize_t arm_load_elf(struct arm_boot_info *info, uint64_t *pentry, } elf_header; int data_swab = 0; bool big_endian; - ssize_t ret = -1; + ssize_t ret; Error *err = NULL; load_elf_hdr(info->kernel_filename, &elf_header, &elf_is64, &err); if (err) { + /* + * If the file is not an ELF file we silently return. + * The caller will fall back to try other formats. + */ error_free(err); - return ret; + return -1; } if (elf_is64) { @@ -839,6 +843,8 @@ static ssize_t arm_load_elf(struct arm_boot_info *info, uint64_t *pentry, 1, data_swab, as); if (ret <= 0) { /* The header loaded but the image didn't */ + error_report("Couldn't load elf '%s': %s", + info->kernel_filename, load_elf_strerror(ret)); exit(1); } diff --git a/hw/arm/sbsa-ref.c b/hw/arm/sbsa-ref.c index ae37a92301..e3195d5449 100644 --- a/hw/arm/sbsa-ref.c +++ b/hw/arm/sbsa-ref.c @@ -164,23 +164,20 @@ static uint64_t sbsa_ref_cpu_mp_affinity(SBSAMachineState *sms, int idx) static void sbsa_fdt_add_gic_node(SBSAMachineState *sms) { - char *nodename; + const char *intc_nodename = "/intc"; + const char *its_nodename = "/intc/its"; - nodename = g_strdup_printf("/intc"); - qemu_fdt_add_subnode(sms->fdt, nodename); - qemu_fdt_setprop_sized_cells(sms->fdt, nodename, "reg", + qemu_fdt_add_subnode(sms->fdt, intc_nodename); + qemu_fdt_setprop_sized_cells(sms->fdt, intc_nodename, "reg", 2, sbsa_ref_memmap[SBSA_GIC_DIST].base, 2, sbsa_ref_memmap[SBSA_GIC_DIST].size, 2, sbsa_ref_memmap[SBSA_GIC_REDIST].base, 2, sbsa_ref_memmap[SBSA_GIC_REDIST].size); - nodename = g_strdup_printf("/intc/its"); - qemu_fdt_add_subnode(sms->fdt, nodename); - qemu_fdt_setprop_sized_cells(sms->fdt, nodename, "reg", + qemu_fdt_add_subnode(sms->fdt, its_nodename); + qemu_fdt_setprop_sized_cells(sms->fdt, its_nodename, "reg", 2, sbsa_ref_memmap[SBSA_GIC_ITS].base, 2, sbsa_ref_memmap[SBSA_GIC_ITS].size); - - g_free(nodename); } /* @@ -621,6 +618,7 @@ static void create_smmu(const SBSAMachineState *sms, PCIBus *bus) dev = qdev_new(TYPE_ARM_SMMUV3); + object_property_set_str(OBJECT(dev), "stage", "nested", &error_abort); object_property_set_link(OBJECT(dev), "primary-bus", OBJECT(bus), &error_abort); sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c index 3971976389..4c49b5a885 100644 --- a/hw/arm/smmuv3.c +++ b/hw/arm/smmuv3.c @@ -1981,6 +1981,7 @@ static Property smmuv3_properties[] = { * Stages of translation advertised. * "1": Stage 1 * "2": Stage 2 + * "nested": Both stage 1 and stage 2 * Defaults to stage 1 */ DEFINE_PROP_STRING("stage", SMMUv3State, stage), diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 687fe0bb8b..7934b23651 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -1408,6 +1408,7 @@ static void create_pcie_irq_map(const MachineState *ms, 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]; @@ -1424,6 +1425,9 @@ static void create_smmu(const VirtMachineState *vms, dev = qdev_new(TYPE_ARM_SMMUV3); + if (!vmc->no_nested_smmu) { + object_property_set_str(OBJECT(dev), "stage", "nested", &error_fatal); + } object_property_set_link(OBJECT(dev), "primary-bus", OBJECT(bus), &error_abort); sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); @@ -3301,10 +3305,21 @@ static void machvirt_machine_init(void) } type_init(machvirt_machine_init); +static void virt_machine_9_2_options(MachineClass *mc) +{ +} +DEFINE_VIRT_MACHINE_AS_LATEST(9, 2) + static void virt_machine_9_1_options(MachineClass *mc) { + VirtMachineClass *vmc = VIRT_MACHINE_CLASS(OBJECT_CLASS(mc)); + + virt_machine_9_2_options(mc); + compat_props_add(mc->compat_props, hw_compat_9_1, hw_compat_9_1_len); + /* 9.1 and earlier have only a stage-1 SMMU, not a nested s1+2 one */ + vmc->no_nested_smmu = true; } -DEFINE_VIRT_MACHINE_AS_LATEST(9, 1) +DEFINE_VIRT_MACHINE(9, 1) static void virt_machine_9_0_options(MachineClass *mc) { diff --git a/hw/arm/xilinx_zynq.c b/hw/arm/xilinx_zynq.c index 3c56b9abe1..37c234f5ab 100644 --- a/hw/arm/xilinx_zynq.c +++ b/hw/arm/xilinx_zynq.c @@ -219,14 +219,6 @@ static void zynq_init(MachineState *machine) for (n = 0; n < smp_cpus; n++) { Object *cpuobj = object_new(machine->cpu_type); - /* - * By default A9 CPUs have EL3 enabled. This board does not currently - * support EL3 so the CPU EL3 property is disabled before realization. - */ - if (object_property_find(cpuobj, "has_el3")) { - object_property_set_bool(cpuobj, "has_el3", false, &error_fatal); - } - object_property_set_int(cpuobj, "midr", ZYNQ_BOARD_MIDR, &error_fatal); object_property_set_int(cpuobj, "reset-cbar", MPCORE_PERIPHBASE, diff --git a/hw/core/machine.c b/hw/core/machine.c index 27dcda0248..adaba17eba 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -34,6 +34,9 @@ #include "hw/virtio/virtio-iommu.h" #include "audio/audio.h" +GlobalProperty hw_compat_9_1[] = {}; +const size_t hw_compat_9_1_len = G_N_ELEMENTS(hw_compat_9_1); + GlobalProperty hw_compat_9_0[] = { {"arm-cpu", "backcompat-cntfrq", "true" }, { "scsi-hd", "migrate-emulated-scsi-request", "false" }, diff --git a/hw/core/platform-bus.c b/hw/core/platform-bus.c index b8487b26b6..dc58bf505a 100644 --- a/hw/core/platform-bus.c +++ b/hw/core/platform-bus.c @@ -145,9 +145,12 @@ static void platform_bus_map_mmio(PlatformBusDevice *pbus, SysBusDevice *sbdev, * the target device's memory region */ for (off = 0; off < pbus->mmio_size; off += alignment) { - if (!memory_region_find(&pbus->mmio, off, size).mr) { + MemoryRegion *mr = memory_region_find(&pbus->mmio, off, size).mr; + if (!mr) { found_region = true; break; + } else { + memory_region_unref(mr); } } diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 7779c88a91..ba0ff51183 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -79,6 +79,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_9_1[] = {}; +const size_t pc_compat_9_1_len = G_N_ELEMENTS(pc_compat_9_1); + GlobalProperty pc_compat_9_0[] = { { TYPE_X86_CPU, "x-amd-topoext-features-only", "false" }, { TYPE_X86_CPU, "x-l1-cache-per-thread", "false" }, diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 347afa4c37..2bf6865d40 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -474,13 +474,24 @@ static void pc_i440fx_machine_options(MachineClass *m) "Use a different south bridge than PIIX3"); } -static void pc_i440fx_machine_9_1_options(MachineClass *m) +static void pc_i440fx_machine_9_2_options(MachineClass *m) { pc_i440fx_machine_options(m); m->alias = "pc"; m->is_default = true; } +DEFINE_I440FX_MACHINE(9, 2); + +static void pc_i440fx_machine_9_1_options(MachineClass *m) +{ + pc_i440fx_machine_9_2_options(m); + m->alias = NULL; + m->is_default = false; + compat_props_add(m->compat_props, hw_compat_9_1, hw_compat_9_1_len); + compat_props_add(m->compat_props, pc_compat_9_1, pc_compat_9_1_len); +} + DEFINE_I440FX_MACHINE(9, 1); static void pc_i440fx_machine_9_0_options(MachineClass *m) @@ -488,8 +499,6 @@ static void pc_i440fx_machine_9_0_options(MachineClass *m) PCMachineClass *pcmc = PC_MACHINE_CLASS(m); pc_i440fx_machine_9_1_options(m); - m->alias = NULL; - m->is_default = false; m->smbios_memory_device_size = 16 * GiB; compat_props_add(m->compat_props, hw_compat_9_0, hw_compat_9_0_len); diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index f2d8edfa84..8319b6d45e 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -356,19 +356,28 @@ static void pc_q35_machine_options(MachineClass *m) pc_q35_compat_defaults, pc_q35_compat_defaults_len); } -static void pc_q35_machine_9_1_options(MachineClass *m) +static void pc_q35_machine_9_2_options(MachineClass *m) { pc_q35_machine_options(m); m->alias = "q35"; } +DEFINE_Q35_MACHINE(9, 2); + +static void pc_q35_machine_9_1_options(MachineClass *m) +{ + pc_q35_machine_9_2_options(m); + m->alias = NULL; + compat_props_add(m->compat_props, hw_compat_9_1, hw_compat_9_1_len); + compat_props_add(m->compat_props, pc_compat_9_1, pc_compat_9_1_len); +} + DEFINE_Q35_MACHINE(9, 1); static void pc_q35_machine_9_0_options(MachineClass *m) { PCMachineClass *pcmc = PC_MACHINE_CLASS(m); pc_q35_machine_9_1_options(m); - m->alias = NULL; m->smbios_memory_device_size = 16 * GiB; compat_props_add(m->compat_props, hw_compat_9_0, hw_compat_9_0_len); compat_props_add(m->compat_props, pc_compat_9_0, pc_compat_9_0_len); diff --git a/hw/m68k/virt.c b/hw/m68k/virt.c index cda199af8f..ea5c4a5a57 100644 --- a/hw/m68k/virt.c +++ b/hw/m68k/virt.c @@ -366,10 +366,17 @@ type_init(virt_machine_register_types) #define DEFINE_VIRT_MACHINE(major, minor) \ DEFINE_VIRT_MACHINE_IMPL(false, major, minor) +static void virt_machine_9_2_options(MachineClass *mc) +{ +} +DEFINE_VIRT_MACHINE_AS_LATEST(9, 2) + static void virt_machine_9_1_options(MachineClass *mc) { + virt_machine_9_2_options(mc); + compat_props_add(mc->compat_props, hw_compat_9_1, hw_compat_9_1_len); } -DEFINE_VIRT_MACHINE_AS_LATEST(9, 1) +DEFINE_VIRT_MACHINE(9, 1) static void virt_machine_9_0_options(MachineClass *mc) { diff --git a/hw/misc/xlnx-versal-cfu.c b/hw/misc/xlnx-versal-cfu.c index 6bb82e51c1..2284b407ea 100644 --- a/hw/misc/xlnx-versal-cfu.c +++ b/hw/misc/xlnx-versal-cfu.c @@ -397,6 +397,13 @@ static void cfu_fdro_init(Object *obj) fifo32_create(&s->fdro_data, 8 * KiB / sizeof(uint32_t)); } +static void cfu_fdro_finalize(Object *obj) +{ + XlnxVersalCFUFDRO *s = XLNX_VERSAL_CFU_FDRO(obj); + + fifo32_destroy(&s->fdro_data); +} + static void cfu_fdro_reset_enter(Object *obj, ResetType type) { XlnxVersalCFUFDRO *s = XLNX_VERSAL_CFU_FDRO(obj); @@ -539,6 +546,7 @@ static const TypeInfo cfu_fdro_info = { .instance_size = sizeof(XlnxVersalCFUFDRO), .class_init = cfu_fdro_class_init, .instance_init = cfu_fdro_init, + .instance_finalize = cfu_fdro_finalize, .interfaces = (InterfaceInfo[]) { { TYPE_XLNX_CFI_IF }, { } diff --git a/hw/misc/xlnx-versal-trng.c b/hw/misc/xlnx-versal-trng.c index 51eb760041..86905479b8 100644 --- a/hw/misc/xlnx-versal-trng.c +++ b/hw/misc/xlnx-versal-trng.c @@ -608,9 +608,8 @@ static void trng_init(Object *obj) { XlnxVersalTRng *s = XLNX_VERSAL_TRNG(obj); SysBusDevice *sbd = SYS_BUS_DEVICE(obj); - RegisterInfoArray *reg_array; - reg_array = + s->reg_array = register_init_block32(DEVICE(obj), trng_regs_info, ARRAY_SIZE(trng_regs_info), s->regs_info, s->regs, @@ -618,16 +617,17 @@ static void trng_init(Object *obj) XLNX_VERSAL_TRNG_ERR_DEBUG, R_MAX * 4); - sysbus_init_mmio(sbd, ®_array->mem); + sysbus_init_mmio(sbd, &s->reg_array->mem); sysbus_init_irq(sbd, &s->irq); s->prng = g_rand_new(); } -static void trng_unrealize(DeviceState *dev) +static void trng_finalize(Object *obj) { - XlnxVersalTRng *s = XLNX_VERSAL_TRNG(dev); + XlnxVersalTRng *s = XLNX_VERSAL_TRNG(obj); + register_finalize_block(s->reg_array); g_rand_free(s->prng); s->prng = NULL; } @@ -689,7 +689,6 @@ static void trng_class_init(ObjectClass *klass, void *data) ResettableClass *rc = RESETTABLE_CLASS(klass); dc->vmsd = &vmstate_trng; - dc->unrealize = trng_unrealize; rc->phases.hold = trng_reset_hold; /* Clone uint64 property with set allowed after realized */ @@ -706,6 +705,7 @@ static const TypeInfo trng_info = { .instance_size = sizeof(XlnxVersalTRng), .class_init = trng_class_init, .instance_init = trng_init, + .instance_finalize = trng_finalize, }; static void trng_register_types(void) diff --git a/hw/nvram/xlnx-bbram.c b/hw/nvram/xlnx-bbram.c index 09575a77d7..1bc58e90ad 100644 --- a/hw/nvram/xlnx-bbram.c +++ b/hw/nvram/xlnx-bbram.c @@ -456,9 +456,8 @@ static void bbram_ctrl_init(Object *obj) { XlnxBBRam *s = XLNX_BBRAM(obj); SysBusDevice *sbd = SYS_BUS_DEVICE(obj); - RegisterInfoArray *reg_array; - reg_array = + s->reg_array = register_init_block32(DEVICE(obj), bbram_ctrl_regs_info, ARRAY_SIZE(bbram_ctrl_regs_info), s->regs_info, s->regs, @@ -466,10 +465,17 @@ static void bbram_ctrl_init(Object *obj) XLNX_BBRAM_ERR_DEBUG, R_MAX * 4); - sysbus_init_mmio(sbd, ®_array->mem); + sysbus_init_mmio(sbd, &s->reg_array->mem); sysbus_init_irq(sbd, &s->irq_bbram); } +static void bbram_ctrl_finalize(Object *obj) +{ + XlnxBBRam *s = XLNX_BBRAM(obj); + + register_finalize_block(s->reg_array); +} + static void bbram_prop_set_drive(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { @@ -537,6 +543,7 @@ static const TypeInfo bbram_ctrl_info = { .instance_size = sizeof(XlnxBBRam), .class_init = bbram_ctrl_class_init, .instance_init = bbram_ctrl_init, + .instance_finalize = bbram_ctrl_finalize, }; static void bbram_ctrl_register_types(void) diff --git a/hw/nvram/xlnx-versal-efuse-ctrl.c b/hw/nvram/xlnx-versal-efuse-ctrl.c index def6fe3302..8252a5cabe 100644 --- a/hw/nvram/xlnx-versal-efuse-ctrl.c +++ b/hw/nvram/xlnx-versal-efuse-ctrl.c @@ -712,9 +712,8 @@ static void efuse_ctrl_init(Object *obj) { XlnxVersalEFuseCtrl *s = XLNX_VERSAL_EFUSE_CTRL(obj); SysBusDevice *sbd = SYS_BUS_DEVICE(obj); - RegisterInfoArray *reg_array; - reg_array = + s->reg_array = register_init_block32(DEVICE(obj), efuse_ctrl_regs_info, ARRAY_SIZE(efuse_ctrl_regs_info), s->regs_info, s->regs, @@ -722,7 +721,7 @@ static void efuse_ctrl_init(Object *obj) XLNX_VERSAL_EFUSE_CTRL_ERR_DEBUG, R_MAX * 4); - sysbus_init_mmio(sbd, ®_array->mem); + sysbus_init_mmio(sbd, &s->reg_array->mem); sysbus_init_irq(sbd, &s->irq_efuse_imr); } @@ -730,6 +729,7 @@ static void efuse_ctrl_finalize(Object *obj) { XlnxVersalEFuseCtrl *s = XLNX_VERSAL_EFUSE_CTRL(obj); + register_finalize_block(s->reg_array); g_free(s->extra_pg0_lock_spec); } diff --git a/hw/nvram/xlnx-zynqmp-efuse.c b/hw/nvram/xlnx-zynqmp-efuse.c index 2d465f0fc6..4e2d1b9d1e 100644 --- a/hw/nvram/xlnx-zynqmp-efuse.c +++ b/hw/nvram/xlnx-zynqmp-efuse.c @@ -803,9 +803,8 @@ static void zynqmp_efuse_init(Object *obj) { XlnxZynqMPEFuse *s = XLNX_ZYNQMP_EFUSE(obj); SysBusDevice *sbd = SYS_BUS_DEVICE(obj); - RegisterInfoArray *reg_array; - reg_array = + s->reg_array = register_init_block32(DEVICE(obj), zynqmp_efuse_regs_info, ARRAY_SIZE(zynqmp_efuse_regs_info), s->regs_info, s->regs, @@ -813,10 +812,17 @@ static void zynqmp_efuse_init(Object *obj) ZYNQMP_EFUSE_ERR_DEBUG, R_MAX * 4); - sysbus_init_mmio(sbd, ®_array->mem); + sysbus_init_mmio(sbd, &s->reg_array->mem); sysbus_init_irq(sbd, &s->irq); } +static void zynqmp_efuse_finalize(Object *obj) +{ + XlnxZynqMPEFuse *s = XLNX_ZYNQMP_EFUSE(obj); + + register_finalize_block(s->reg_array); +} + static const VMStateDescription vmstate_efuse = { .name = TYPE_XLNX_ZYNQMP_EFUSE, .version_id = 1, @@ -853,6 +859,7 @@ static const TypeInfo efuse_info = { .instance_size = sizeof(XlnxZynqMPEFuse), .class_init = zynqmp_efuse_class_init, .instance_init = zynqmp_efuse_init, + .instance_finalize = zynqmp_efuse_finalize, }; static void efuse_register_types(void) diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 370d7c35d3..8aa3ce7449 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -4838,14 +4838,25 @@ static void spapr_machine_latest_class_options(MachineClass *mc) DEFINE_SPAPR_MACHINE_IMPL(false, major, minor, _, tag) /* + * pseries-9.2 + */ +static void spapr_machine_9_2_class_options(MachineClass *mc) +{ + /* Defaults for the latest behaviour inherited from the base class */ +} + +DEFINE_SPAPR_MACHINE_AS_LATEST(9, 2); + +/* * pseries-9.1 */ static void spapr_machine_9_1_class_options(MachineClass *mc) { - /* Defaults for the latest behaviour inherited from the base class */ + spapr_machine_9_2_class_options(mc); + compat_props_add(mc->compat_props, hw_compat_9_1, hw_compat_9_1_len); } -DEFINE_SPAPR_MACHINE_AS_LATEST(9, 1); +DEFINE_SPAPR_MACHINE(9, 1); /* * pseries-9.0 diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index c483ff8064..18240a0fd8 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -871,14 +871,26 @@ static const TypeInfo ccw_machine_info = { DEFINE_CCW_MACHINE_IMPL(false, major, minor) +static void ccw_machine_9_2_instance_options(MachineState *machine) +{ +} + +static void ccw_machine_9_2_class_options(MachineClass *mc) +{ +} +DEFINE_CCW_MACHINE_AS_LATEST(9, 2); + static void ccw_machine_9_1_instance_options(MachineState *machine) { + ccw_machine_9_2_instance_options(machine); } static void ccw_machine_9_1_class_options(MachineClass *mc) { + ccw_machine_9_2_class_options(mc); + compat_props_add(mc->compat_props, hw_compat_9_1, hw_compat_9_1_len); } -DEFINE_CCW_MACHINE_AS_LATEST(9, 1); +DEFINE_CCW_MACHINE(9, 1); static void ccw_machine_9_0_instance_options(MachineState *machine) { diff --git a/include/hw/arm/virt.h b/include/hw/arm/virt.h index a4d937ed45..aca4f8061b 100644 --- a/include/hw/arm/virt.h +++ b/include/hw/arm/virt.h @@ -134,6 +134,7 @@ struct VirtMachineClass { bool no_cpu_topology; bool no_tcg_lpa2; bool no_ns_el2_virt_timer_irq; + bool no_nested_smmu; }; struct VirtMachineState { diff --git a/include/hw/boards.h b/include/hw/boards.h index 48ff6d8b93..9a492770cb 100644 --- a/include/hw/boards.h +++ b/include/hw/boards.h @@ -732,6 +732,9 @@ struct MachineState { } \ type_init(machine_initfn##_register_types) +extern GlobalProperty hw_compat_9_1[]; +extern const size_t hw_compat_9_1_len; + extern GlobalProperty hw_compat_9_0[]; extern const size_t hw_compat_9_0_len; diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index 4e55d7ef6e..14ee06287d 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -215,6 +215,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_9_1[]; +extern const size_t pc_compat_9_1_len; + extern GlobalProperty pc_compat_9_0[]; extern const size_t pc_compat_9_0_len; diff --git a/include/hw/misc/xlnx-versal-trng.h b/include/hw/misc/xlnx-versal-trng.h index 0bcef8a613..d96f8f9eff 100644 --- a/include/hw/misc/xlnx-versal-trng.h +++ b/include/hw/misc/xlnx-versal-trng.h @@ -50,6 +50,7 @@ typedef struct XlnxVersalTRng { uint64_t forced_prng_count; uint64_t tst_seed[2]; + RegisterInfoArray *reg_array; uint32_t regs[RMAX_XLNX_VERSAL_TRNG]; RegisterInfo regs_info[RMAX_XLNX_VERSAL_TRNG]; } XlnxVersalTRng; diff --git a/include/hw/nvram/xlnx-bbram.h b/include/hw/nvram/xlnx-bbram.h index 6fc13f8cc1..bce8e89d90 100644 --- a/include/hw/nvram/xlnx-bbram.h +++ b/include/hw/nvram/xlnx-bbram.h @@ -47,6 +47,7 @@ struct XlnxBBRam { bool bbram8_wo; bool blk_ro; + RegisterInfoArray *reg_array; uint32_t regs[RMAX_XLNX_BBRAM]; RegisterInfo regs_info[RMAX_XLNX_BBRAM]; }; diff --git a/include/hw/nvram/xlnx-versal-efuse.h b/include/hw/nvram/xlnx-versal-efuse.h index 86e2261b9a..afa4f4f996 100644 --- a/include/hw/nvram/xlnx-versal-efuse.h +++ b/include/hw/nvram/xlnx-versal-efuse.h @@ -44,6 +44,7 @@ struct XlnxVersalEFuseCtrl { void *extra_pg0_lock_spec; /* Opaque property */ uint32_t extra_pg0_lock_n16; + RegisterInfoArray *reg_array; uint32_t regs[XLNX_VERSAL_EFUSE_CTRL_R_MAX]; RegisterInfo regs_info[XLNX_VERSAL_EFUSE_CTRL_R_MAX]; }; diff --git a/include/hw/nvram/xlnx-zynqmp-efuse.h b/include/hw/nvram/xlnx-zynqmp-efuse.h index f5beacc2e6..7fb12df3fb 100644 --- a/include/hw/nvram/xlnx-zynqmp-efuse.h +++ b/include/hw/nvram/xlnx-zynqmp-efuse.h @@ -37,6 +37,7 @@ struct XlnxZynqMPEFuse { qemu_irq irq; XlnxEFuse *efuse; + RegisterInfoArray *reg_array; uint32_t regs[XLNX_ZYNQMP_EFUSE_R_MAX]; RegisterInfo regs_info[XLNX_ZYNQMP_EFUSE_R_MAX]; }; diff --git a/migration/file.c b/migration/file.c index 6451a21c86..7f11e26f5c 100644 --- a/migration/file.c +++ b/migration/file.c @@ -196,12 +196,13 @@ void file_start_incoming_migration(FileMigrationArgs *file_args, Error **errp) } int file_write_ramblock_iov(QIOChannel *ioc, const struct iovec *iov, - int niov, RAMBlock *block, Error **errp) + int niov, MultiFDPages_t *pages, Error **errp) { ssize_t ret = 0; int i, slice_idx, slice_num; uintptr_t base, next, offset; size_t len; + RAMBlock *block = pages->block; slice_idx = 0; slice_num = 1; diff --git a/migration/file.h b/migration/file.h index 9f71e87f74..1a1115f7f1 100644 --- a/migration/file.h +++ b/migration/file.h @@ -21,6 +21,6 @@ int file_parse_offset(char *filespec, uint64_t *offsetp, Error **errp); void file_cleanup_outgoing_migration(void); bool file_send_channel_create(gpointer opaque, Error **errp); int file_write_ramblock_iov(QIOChannel *ioc, const struct iovec *iov, - int niov, RAMBlock *block, Error **errp); + int niov, MultiFDPages_t *pages, Error **errp); int multifd_file_recv_data(MultiFDRecvParams *p, Error **errp); #endif diff --git a/migration/meson.build b/migration/meson.build index 5ce2acb41e..77f3abf08e 100644 --- a/migration/meson.build +++ b/migration/meson.build @@ -21,6 +21,7 @@ system_ss.add(files( 'migration-hmp-cmds.c', 'migration.c', 'multifd.c', + 'multifd-nocomp.c', 'multifd-zlib.c', 'multifd-zero-page.c', 'options.c', diff --git a/migration/multifd-nocomp.c b/migration/multifd-nocomp.c new file mode 100644 index 0000000000..07c63f4a72 --- /dev/null +++ b/migration/multifd-nocomp.c @@ -0,0 +1,389 @@ +/* + * Multifd RAM migration without compression + * + * Copyright (c) 2019-2020 Red Hat Inc + * + * Authors: + * Juan Quintela <quintela@redhat.com> + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "exec/ramblock.h" +#include "exec/target_page.h" +#include "file.h" +#include "multifd.h" +#include "options.h" +#include "qapi/error.h" +#include "qemu/error-report.h" +#include "trace.h" + +static MultiFDSendData *multifd_ram_send; + +size_t multifd_ram_payload_size(void) +{ + uint32_t n = multifd_ram_page_count(); + + /* + * We keep an array of page offsets at the end of MultiFDPages_t, + * add space for it in the allocation. + */ + return sizeof(MultiFDPages_t) + n * sizeof(ram_addr_t); +} + +void multifd_ram_save_setup(void) +{ + multifd_ram_send = multifd_send_data_alloc(); +} + +void multifd_ram_save_cleanup(void) +{ + g_free(multifd_ram_send); + multifd_ram_send = NULL; +} + +static void multifd_set_file_bitmap(MultiFDSendParams *p) +{ + MultiFDPages_t *pages = &p->data->u.ram; + + assert(pages->block); + + for (int i = 0; i < pages->normal_num; i++) { + ramblock_set_file_bmap_atomic(pages->block, pages->offset[i], true); + } + + for (int i = pages->normal_num; i < pages->num; i++) { + ramblock_set_file_bmap_atomic(pages->block, pages->offset[i], false); + } +} + +static int multifd_nocomp_send_setup(MultiFDSendParams *p, Error **errp) +{ + uint32_t page_count = multifd_ram_page_count(); + + if (migrate_zero_copy_send()) { + p->write_flags |= QIO_CHANNEL_WRITE_FLAG_ZERO_COPY; + } + + if (!migrate_mapped_ram()) { + /* We need one extra place for the packet header */ + p->iov = g_new0(struct iovec, page_count + 1); + } else { + p->iov = g_new0(struct iovec, page_count); + } + + return 0; +} + +static void multifd_nocomp_send_cleanup(MultiFDSendParams *p, Error **errp) +{ + g_free(p->iov); + p->iov = NULL; + return; +} + +static void multifd_send_prepare_iovs(MultiFDSendParams *p) +{ + MultiFDPages_t *pages = &p->data->u.ram; + uint32_t page_size = multifd_ram_page_size(); + + for (int i = 0; i < pages->normal_num; i++) { + p->iov[p->iovs_num].iov_base = pages->block->host + pages->offset[i]; + p->iov[p->iovs_num].iov_len = page_size; + p->iovs_num++; + } + + p->next_packet_size = pages->normal_num * page_size; +} + +static int multifd_nocomp_send_prepare(MultiFDSendParams *p, Error **errp) +{ + bool use_zero_copy_send = migrate_zero_copy_send(); + int ret; + + multifd_send_zero_page_detect(p); + + if (migrate_mapped_ram()) { + multifd_send_prepare_iovs(p); + multifd_set_file_bitmap(p); + + return 0; + } + + if (!use_zero_copy_send) { + /* + * Only !zerocopy needs the header in IOV; zerocopy will + * send it separately. + */ + multifd_send_prepare_header(p); + } + + multifd_send_prepare_iovs(p); + p->flags |= MULTIFD_FLAG_NOCOMP; + + multifd_send_fill_packet(p); + + if (use_zero_copy_send) { + /* Send header first, without zerocopy */ + ret = qio_channel_write_all(p->c, (void *)p->packet, + p->packet_len, errp); + if (ret != 0) { + return -1; + } + } + + return 0; +} + +static int multifd_nocomp_recv_setup(MultiFDRecvParams *p, Error **errp) +{ + p->iov = g_new0(struct iovec, multifd_ram_page_count()); + return 0; +} + +static void multifd_nocomp_recv_cleanup(MultiFDRecvParams *p) +{ + g_free(p->iov); + p->iov = NULL; +} + +static int multifd_nocomp_recv(MultiFDRecvParams *p, Error **errp) +{ + uint32_t flags; + + if (migrate_mapped_ram()) { + return multifd_file_recv_data(p, errp); + } + + flags = p->flags & MULTIFD_FLAG_COMPRESSION_MASK; + + if (flags != MULTIFD_FLAG_NOCOMP) { + error_setg(errp, "multifd %u: flags received %x flags expected %x", + p->id, flags, MULTIFD_FLAG_NOCOMP); + return -1; + } + + multifd_recv_zero_page_process(p); + + if (!p->normal_num) { + return 0; + } + + for (int i = 0; i < p->normal_num; i++) { + p->iov[i].iov_base = p->host + p->normal[i]; + p->iov[i].iov_len = multifd_ram_page_size(); + ramblock_recv_bitmap_set_offset(p->block, p->normal[i]); + } + return qio_channel_readv_all(p->c, p->iov, p->normal_num, errp); +} + +static void multifd_pages_reset(MultiFDPages_t *pages) +{ + /* + * We don't need to touch offset[] array, because it will be + * overwritten later when reused. + */ + pages->num = 0; + pages->normal_num = 0; + pages->block = NULL; +} + +void multifd_ram_fill_packet(MultiFDSendParams *p) +{ + MultiFDPacket_t *packet = p->packet; + MultiFDPages_t *pages = &p->data->u.ram; + uint32_t zero_num = pages->num - pages->normal_num; + + packet->pages_alloc = cpu_to_be32(multifd_ram_page_count()); + packet->normal_pages = cpu_to_be32(pages->normal_num); + packet->zero_pages = cpu_to_be32(zero_num); + + if (pages->block) { + strncpy(packet->ramblock, pages->block->idstr, 256); + } + + for (int i = 0; i < pages->num; i++) { + /* there are architectures where ram_addr_t is 32 bit */ + uint64_t temp = pages->offset[i]; + + packet->offset[i] = cpu_to_be64(temp); + } + + trace_multifd_send_ram_fill(p->id, pages->normal_num, + zero_num); +} + +int multifd_ram_unfill_packet(MultiFDRecvParams *p, Error **errp) +{ + MultiFDPacket_t *packet = p->packet; + uint32_t page_count = multifd_ram_page_count(); + uint32_t page_size = multifd_ram_page_size(); + uint32_t pages_per_packet = be32_to_cpu(packet->pages_alloc); + int i; + + if (pages_per_packet > page_count) { + error_setg(errp, "multifd: received packet with %u pages, expected %u", + pages_per_packet, page_count); + return -1; + } + + p->normal_num = be32_to_cpu(packet->normal_pages); + if (p->normal_num > pages_per_packet) { + error_setg(errp, "multifd: received packet with %u non-zero pages, " + "which exceeds maximum expected pages %u", + p->normal_num, pages_per_packet); + return -1; + } + + p->zero_num = be32_to_cpu(packet->zero_pages); + if (p->zero_num > pages_per_packet - p->normal_num) { + error_setg(errp, + "multifd: received packet with %u zero pages, expected maximum %u", + p->zero_num, pages_per_packet - p->normal_num); + return -1; + } + + if (p->normal_num == 0 && p->zero_num == 0) { + return 0; + } + + /* make sure that ramblock is 0 terminated */ + packet->ramblock[255] = 0; + p->block = qemu_ram_block_by_name(packet->ramblock); + if (!p->block) { + error_setg(errp, "multifd: unknown ram block %s", + packet->ramblock); + return -1; + } + + p->host = p->block->host; + for (i = 0; i < p->normal_num; i++) { + uint64_t offset = be64_to_cpu(packet->offset[i]); + + if (offset > (p->block->used_length - page_size)) { + error_setg(errp, "multifd: offset too long %" PRIu64 + " (max " RAM_ADDR_FMT ")", + offset, p->block->used_length); + return -1; + } + p->normal[i] = offset; + } + + for (i = 0; i < p->zero_num; i++) { + uint64_t offset = be64_to_cpu(packet->offset[p->normal_num + i]); + + if (offset > (p->block->used_length - page_size)) { + error_setg(errp, "multifd: offset too long %" PRIu64 + " (max " RAM_ADDR_FMT ")", + offset, p->block->used_length); + return -1; + } + p->zero[i] = offset; + } + + return 0; +} + +static inline bool multifd_queue_empty(MultiFDPages_t *pages) +{ + return pages->num == 0; +} + +static inline bool multifd_queue_full(MultiFDPages_t *pages) +{ + return pages->num == multifd_ram_page_count(); +} + +static inline void multifd_enqueue(MultiFDPages_t *pages, ram_addr_t offset) +{ + pages->offset[pages->num++] = offset; +} + +/* Returns true if enqueue successful, false otherwise */ +bool multifd_queue_page(RAMBlock *block, ram_addr_t offset) +{ + MultiFDPages_t *pages; + +retry: + pages = &multifd_ram_send->u.ram; + + if (multifd_payload_empty(multifd_ram_send)) { + multifd_pages_reset(pages); + multifd_set_payload_type(multifd_ram_send, MULTIFD_PAYLOAD_RAM); + } + + /* If the queue is empty, we can already enqueue now */ + if (multifd_queue_empty(pages)) { + pages->block = block; + multifd_enqueue(pages, offset); + return true; + } + + /* + * Not empty, meanwhile we need a flush. It can because of either: + * + * (1) The page is not on the same ramblock of previous ones, or, + * (2) The queue is full. + * + * After flush, always retry. + */ + if (pages->block != block || multifd_queue_full(pages)) { + if (!multifd_send(&multifd_ram_send)) { + return false; + } + goto retry; + } + + /* Not empty, and we still have space, do it! */ + multifd_enqueue(pages, offset); + return true; +} + +int multifd_ram_flush_and_sync(void) +{ + if (!migrate_multifd()) { + return 0; + } + + if (!multifd_payload_empty(multifd_ram_send)) { + if (!multifd_send(&multifd_ram_send)) { + error_report("%s: multifd_send fail", __func__); + return -1; + } + } + + return multifd_send_sync_main(); +} + +bool multifd_send_prepare_common(MultiFDSendParams *p) +{ + MultiFDPages_t *pages = &p->data->u.ram; + multifd_send_zero_page_detect(p); + + if (!pages->normal_num) { + p->next_packet_size = 0; + return false; + } + + multifd_send_prepare_header(p); + + return true; +} + +static const MultiFDMethods multifd_nocomp_ops = { + .send_setup = multifd_nocomp_send_setup, + .send_cleanup = multifd_nocomp_send_cleanup, + .send_prepare = multifd_nocomp_send_prepare, + .recv_setup = multifd_nocomp_recv_setup, + .recv_cleanup = multifd_nocomp_recv_cleanup, + .recv = multifd_nocomp_recv +}; + +static void multifd_nocomp_register(void) +{ + multifd_register_ops(MULTIFD_COMPRESSION_NONE, &multifd_nocomp_ops); +} + +migration_init(multifd_nocomp_register); diff --git a/migration/multifd-qpl.c b/migration/multifd-qpl.c index 9265098ee7..b0f1e2ba46 100644 --- a/migration/multifd-qpl.c +++ b/migration/multifd-qpl.c @@ -220,21 +220,13 @@ static void multifd_qpl_deinit(QplData *qpl) } } -/** - * multifd_qpl_send_setup: set up send side - * - * Set up the channel with QPL compression. - * - * Returns 0 on success or -1 on error - * - * @p: Params for the channel being used - * @errp: pointer to an error - */ static int multifd_qpl_send_setup(MultiFDSendParams *p, Error **errp) { QplData *qpl; + uint32_t page_size = multifd_ram_page_size(); + uint32_t page_count = multifd_ram_page_count(); - qpl = multifd_qpl_init(p->page_count, p->page_size, errp); + qpl = multifd_qpl_init(page_count, page_size, errp); if (!qpl) { return -1; } @@ -245,18 +237,10 @@ static int multifd_qpl_send_setup(MultiFDSendParams *p, Error **errp) * additional two IOVs are used to store packet header and compressed data * length */ - p->iov = g_new0(struct iovec, p->page_count + 2); + p->iov = g_new0(struct iovec, page_count + 2); return 0; } -/** - * multifd_qpl_send_cleanup: clean up send side - * - * Close the channel and free memory. - * - * @p: Params for the channel being used - * @errp: pointer to an error - */ static void multifd_qpl_send_cleanup(MultiFDSendParams *p, Error **errp) { multifd_qpl_deinit(p->compress_data); @@ -404,13 +388,14 @@ retry: static void multifd_qpl_compress_pages_slow_path(MultiFDSendParams *p) { QplData *qpl = p->compress_data; + MultiFDPages_t *pages = &p->data->u.ram; uint32_t size = p->page_size; qpl_job *job = qpl->sw_job; uint8_t *zbuf = qpl->zbuf; uint8_t *buf; - for (int i = 0; i < p->pages->normal_num; i++) { - buf = p->pages->block->host + p->pages->offset[i]; + for (int i = 0; i < pages->normal_num; i++) { + buf = pages->block->host + pages->offset[i]; multifd_qpl_prepare_comp_job(job, buf, zbuf, size); if (qpl_execute_job(job) == QPL_STS_OK) { multifd_qpl_fill_packet(i, p, zbuf, job->total_out); @@ -434,7 +419,7 @@ static void multifd_qpl_compress_pages_slow_path(MultiFDSendParams *p) static void multifd_qpl_compress_pages(MultiFDSendParams *p) { QplData *qpl = p->compress_data; - MultiFDPages_t *pages = p->pages; + MultiFDPages_t *pages = &p->data->u.ram; uint32_t size = p->page_size; QplHwJob *hw_job; uint8_t *buf; @@ -484,20 +469,10 @@ static void multifd_qpl_compress_pages(MultiFDSendParams *p) } } -/** - * multifd_qpl_send_prepare: prepare data to be able to send - * - * Create a compressed buffer with all the pages that we are going to - * send. - * - * Returns 0 on success or -1 on error - * - * @p: Params for the channel being used - * @errp: pointer to an error - */ static int multifd_qpl_send_prepare(MultiFDSendParams *p, Error **errp) { QplData *qpl = p->compress_data; + MultiFDPages_t *pages = &p->data->u.ram; uint32_t len = 0; if (!multifd_send_prepare_common(p)) { @@ -505,7 +480,7 @@ static int multifd_qpl_send_prepare(MultiFDSendParams *p, Error **errp) } /* The first IOV is used to store the compressed page lengths */ - len = p->pages->normal_num * sizeof(uint32_t); + len = pages->normal_num * sizeof(uint32_t); multifd_qpl_fill_iov(p, (uint8_t *) qpl->zlen, len); if (qpl->hw_avail) { multifd_qpl_compress_pages(p); @@ -519,21 +494,13 @@ out: return 0; } -/** - * multifd_qpl_recv_setup: set up receive side - * - * Create the compressed channel and buffer. - * - * Returns 0 on success or -1 on error - * - * @p: Params for the channel being used - * @errp: pointer to an error - */ static int multifd_qpl_recv_setup(MultiFDRecvParams *p, Error **errp) { QplData *qpl; + uint32_t page_size = multifd_ram_page_size(); + uint32_t page_count = multifd_ram_page_count(); - qpl = multifd_qpl_init(p->page_count, p->page_size, errp); + qpl = multifd_qpl_init(page_count, page_size, errp); if (!qpl) { return -1; } @@ -541,13 +508,6 @@ static int multifd_qpl_recv_setup(MultiFDRecvParams *p, Error **errp) return 0; } -/** - * multifd_qpl_recv_cleanup: set up receive side - * - * Close the channel and free memory. - * - * @p: Params for the channel being used - */ static void multifd_qpl_recv_cleanup(MultiFDRecvParams *p) { multifd_qpl_deinit(p->compress_data); @@ -688,17 +648,6 @@ static int multifd_qpl_decompress_pages(MultiFDRecvParams *p, Error **errp) } return 0; } -/** - * multifd_qpl_recv: read the data from the channel into actual pages - * - * Read the compressed buffer, and uncompress it into the actual - * pages. - * - * Returns 0 on success or -1 on error - * - * @p: Params for the channel being used - * @errp: pointer to an error - */ static int multifd_qpl_recv(MultiFDRecvParams *p, Error **errp) { QplData *qpl = p->compress_data; @@ -745,7 +694,7 @@ static int multifd_qpl_recv(MultiFDRecvParams *p, Error **errp) return multifd_qpl_decompress_pages_slow_path(p, errp); } -static MultiFDMethods multifd_qpl_ops = { +static const MultiFDMethods multifd_qpl_ops = { .send_setup = multifd_qpl_send_setup, .send_cleanup = multifd_qpl_send_cleanup, .send_prepare = multifd_qpl_send_prepare, diff --git a/migration/multifd-uadk.c b/migration/multifd-uadk.c index d12353fb21..6e6a290ae9 100644 --- a/migration/multifd-uadk.c +++ b/migration/multifd-uadk.c @@ -103,19 +103,13 @@ static void multifd_uadk_uninit_sess(struct wd_data *wd) g_free(wd); } -/** - * multifd_uadk_send_setup: setup send side - * - * Returns 0 for success or -1 for error - * - * @p: Params for the channel that we are using - * @errp: pointer to an error - */ static int multifd_uadk_send_setup(MultiFDSendParams *p, Error **errp) { struct wd_data *wd; + uint32_t page_size = multifd_ram_page_size(); + uint32_t page_count = multifd_ram_page_count(); - wd = multifd_uadk_init_sess(p->page_count, p->page_size, true, errp); + wd = multifd_uadk_init_sess(page_count, page_size, true, errp); if (!wd) { return -1; } @@ -128,24 +122,18 @@ static int multifd_uadk_send_setup(MultiFDSendParams *p, Error **errp) * length */ - p->iov = g_new0(struct iovec, p->page_count + 2); + p->iov = g_new0(struct iovec, page_count + 2); return 0; } -/** - * multifd_uadk_send_cleanup: cleanup send side - * - * Close the channel and return memory. - * - * @p: Params for the channel that we are using - * @errp: pointer to an error - */ static void multifd_uadk_send_cleanup(MultiFDSendParams *p, Error **errp) { struct wd_data *wd = p->compress_data; multifd_uadk_uninit_sess(wd); p->compress_data = NULL; + g_free(p->iov); + p->iov = NULL; } static inline void prepare_next_iov(MultiFDSendParams *p, void *base, @@ -157,37 +145,28 @@ static inline void prepare_next_iov(MultiFDSendParams *p, void *base, p->iovs_num++; } -/** - * multifd_uadk_send_prepare: prepare data to be able to send - * - * Create a compressed buffer with all the pages that we are going to - * send. - * - * Returns 0 for success or -1 for error - * - * @p: Params for the channel that we are using - * @errp: pointer to an error - */ static int multifd_uadk_send_prepare(MultiFDSendParams *p, Error **errp) { struct wd_data *uadk_data = p->compress_data; uint32_t hdr_size; + uint32_t page_size = multifd_ram_page_size(); uint8_t *buf = uadk_data->buf; int ret = 0; + MultiFDPages_t *pages = &p->data->u.ram; if (!multifd_send_prepare_common(p)) { goto out; } - hdr_size = p->pages->normal_num * sizeof(uint32_t); + hdr_size = pages->normal_num * sizeof(uint32_t); /* prepare the header that stores the lengths of all compressed data */ prepare_next_iov(p, uadk_data->buf_hdr, hdr_size); - for (int i = 0; i < p->pages->normal_num; i++) { + for (int i = 0; i < pages->normal_num; i++) { struct wd_comp_req creq = { .op_type = WD_DIR_COMPRESS, - .src = p->pages->block->host + p->pages->offset[i], - .src_len = p->page_size, + .src = pages->block->host + pages->offset[i], + .src_len = page_size, .dst = buf, /* Set dst_len to double the src in case compressed out >= page_size */ .dst_len = p->page_size * 2, @@ -200,7 +179,7 @@ static int multifd_uadk_send_prepare(MultiFDSendParams *p, Error **errp) p->id, ret, creq.status); return -1; } - if (creq.dst_len < p->page_size) { + if (creq.dst_len < page_size) { uadk_data->buf_hdr[i] = cpu_to_be32(creq.dst_len); prepare_next_iov(p, buf, creq.dst_len); buf += creq.dst_len; @@ -212,11 +191,11 @@ static int multifd_uadk_send_prepare(MultiFDSendParams *p, Error **errp) * than page_size as well because at the receive end we can skip the * decompression. But it is tricky to find the right number here. */ - if (!uadk_data->handle || creq.dst_len >= p->page_size) { - uadk_data->buf_hdr[i] = cpu_to_be32(p->page_size); - prepare_next_iov(p, p->pages->block->host + p->pages->offset[i], - p->page_size); - buf += p->page_size; + if (!uadk_data->handle || creq.dst_len >= page_size) { + uadk_data->buf_hdr[i] = cpu_to_be32(page_size); + prepare_next_iov(p, pages->block->host + pages->offset[i], + page_size); + buf += page_size; } } out: @@ -225,21 +204,13 @@ out: return 0; } -/** - * multifd_uadk_recv_setup: setup receive side - * - * Create the compressed channel and buffer. - * - * Returns 0 for success or -1 for error - * - * @p: Params for the channel that we are using - * @errp: pointer to an error - */ static int multifd_uadk_recv_setup(MultiFDRecvParams *p, Error **errp) { struct wd_data *wd; + uint32_t page_size = multifd_ram_page_size(); + uint32_t page_count = multifd_ram_page_count(); - wd = multifd_uadk_init_sess(p->page_count, p->page_size, false, errp); + wd = multifd_uadk_init_sess(page_count, page_size, false, errp); if (!wd) { return -1; } @@ -247,13 +218,6 @@ static int multifd_uadk_recv_setup(MultiFDRecvParams *p, Error **errp) return 0; } -/** - * multifd_uadk_recv_cleanup: cleanup receive side - * - * Close the channel and return memory. - * - * @p: Params for the channel that we are using - */ static void multifd_uadk_recv_cleanup(MultiFDRecvParams *p) { struct wd_data *wd = p->compress_data; @@ -262,17 +226,6 @@ static void multifd_uadk_recv_cleanup(MultiFDRecvParams *p) p->compress_data = NULL; } -/** - * multifd_uadk_recv: read the data from the channel into actual pages - * - * Read the compressed buffer, and uncompress it into the actual - * pages. - * - * Returns 0 for success or -1 for error - * - * @p: Params for the channel that we are using - * @errp: pointer to an error - */ static int multifd_uadk_recv(MultiFDRecvParams *p, Error **errp) { struct wd_data *uadk_data = p->compress_data; @@ -280,6 +233,7 @@ static int multifd_uadk_recv(MultiFDRecvParams *p, Error **errp) uint32_t flags = p->flags & MULTIFD_FLAG_COMPRESSION_MASK; uint32_t hdr_len = p->normal_num * sizeof(uint32_t); uint32_t data_len = 0; + uint32_t page_size = multifd_ram_page_size(); uint8_t *buf = uadk_data->buf; int ret = 0; @@ -306,7 +260,7 @@ static int multifd_uadk_recv(MultiFDRecvParams *p, Error **errp) for (int i = 0; i < p->normal_num; i++) { uadk_data->buf_hdr[i] = be32_to_cpu(uadk_data->buf_hdr[i]); data_len += uadk_data->buf_hdr[i]; - assert(uadk_data->buf_hdr[i] <= p->page_size); + assert(uadk_data->buf_hdr[i] <= page_size); } /* read compressed data */ @@ -322,12 +276,12 @@ static int multifd_uadk_recv(MultiFDRecvParams *p, Error **errp) .src = buf, .src_len = uadk_data->buf_hdr[i], .dst = p->host + p->normal[i], - .dst_len = p->page_size, + .dst_len = page_size, }; - if (uadk_data->buf_hdr[i] == p->page_size) { - memcpy(p->host + p->normal[i], buf, p->page_size); - buf += p->page_size; + if (uadk_data->buf_hdr[i] == page_size) { + memcpy(p->host + p->normal[i], buf, page_size); + buf += page_size; continue; } @@ -343,7 +297,7 @@ static int multifd_uadk_recv(MultiFDRecvParams *p, Error **errp) p->id, ret, creq.status); return -1; } - if (creq.dst_len != p->page_size) { + if (creq.dst_len != page_size) { error_setg(errp, "multifd %u: decompressed length error", p->id); return -1; } @@ -353,7 +307,7 @@ static int multifd_uadk_recv(MultiFDRecvParams *p, Error **errp) return 0; } -static MultiFDMethods multifd_uadk_ops = { +static const MultiFDMethods multifd_uadk_ops = { .send_setup = multifd_uadk_send_setup, .send_cleanup = multifd_uadk_send_cleanup, .send_prepare = multifd_uadk_send_prepare, diff --git a/migration/multifd-zero-page.c b/migration/multifd-zero-page.c index e1b8370f88..f1e988a959 100644 --- a/migration/multifd-zero-page.c +++ b/migration/multifd-zero-page.c @@ -14,6 +14,7 @@ #include "qemu/cutils.h" #include "exec/ramblock.h" #include "migration.h" +#include "migration-stats.h" #include "multifd.h" #include "options.h" #include "ram.h" @@ -46,14 +47,14 @@ static void swap_page_offset(ram_addr_t *pages_offset, int a, int b) */ void multifd_send_zero_page_detect(MultiFDSendParams *p) { - MultiFDPages_t *pages = p->pages; + MultiFDPages_t *pages = &p->data->u.ram; RAMBlock *rb = pages->block; int i = 0; int j = pages->num - 1; if (!multifd_zero_page_enabled()) { pages->normal_num = pages->num; - return; + goto out; } /* @@ -63,7 +64,7 @@ void multifd_send_zero_page_detect(MultiFDSendParams *p) while (i <= j) { uint64_t offset = pages->offset[i]; - if (!buffer_is_zero(rb->host + offset, p->page_size)) { + if (!buffer_is_zero(rb->host + offset, multifd_ram_page_size())) { i++; continue; } @@ -74,6 +75,10 @@ void multifd_send_zero_page_detect(MultiFDSendParams *p) } pages->normal_num = i; + +out: + stat64_add(&mig_stats.normal_pages, pages->normal_num); + stat64_add(&mig_stats.zero_pages, pages->num - pages->normal_num); } void multifd_recv_zero_page_process(MultiFDRecvParams *p) @@ -81,7 +86,7 @@ void multifd_recv_zero_page_process(MultiFDRecvParams *p) for (int i = 0; i < p->zero_num; i++) { void *page = p->host + p->zero[i]; if (ramblock_recv_bitmap_test_byte_offset(p->block, p->zero[i])) { - memset(page, 0, p->page_size); + memset(page, 0, multifd_ram_page_size()); } else { ramblock_recv_bitmap_set_offset(p->block, p->zero[i]); } diff --git a/migration/multifd-zlib.c b/migration/multifd-zlib.c index 2ced69487e..8cf8a26bb4 100644 --- a/migration/multifd-zlib.c +++ b/migration/multifd-zlib.c @@ -34,17 +34,7 @@ struct zlib_data { /* Multifd zlib compression */ -/** - * zlib_send_setup: setup send side - * - * Setup each channel with zlib compression. - * - * Returns 0 for success or -1 for error - * - * @p: Params for the channel that we are using - * @errp: pointer to an error - */ -static int zlib_send_setup(MultiFDSendParams *p, Error **errp) +static int multifd_zlib_send_setup(MultiFDSendParams *p, Error **errp) { struct zlib_data *z = g_new0(struct zlib_data, 1); z_stream *zs = &z->zs; @@ -86,15 +76,7 @@ err_free_z: return -1; } -/** - * zlib_send_cleanup: cleanup send side - * - * Close the channel and return memory. - * - * @p: Params for the channel that we are using - * @errp: pointer to an error - */ -static void zlib_send_cleanup(MultiFDSendParams *p, Error **errp) +static void multifd_zlib_send_cleanup(MultiFDSendParams *p, Error **errp) { struct zlib_data *z = p->compress_data; @@ -110,23 +92,13 @@ static void zlib_send_cleanup(MultiFDSendParams *p, Error **errp) p->iov = NULL; } -/** - * zlib_send_prepare: prepare date to be able to send - * - * Create a compressed buffer with all the pages that we are going to - * send. - * - * Returns 0 for success or -1 for error - * - * @p: Params for the channel that we are using - * @errp: pointer to an error - */ -static int zlib_send_prepare(MultiFDSendParams *p, Error **errp) +static int multifd_zlib_send_prepare(MultiFDSendParams *p, Error **errp) { - MultiFDPages_t *pages = p->pages; + MultiFDPages_t *pages = &p->data->u.ram; struct zlib_data *z = p->compress_data; z_stream *zs = &z->zs; uint32_t out_size = 0; + uint32_t page_size = multifd_ram_page_size(); int ret; uint32_t i; @@ -147,8 +119,8 @@ static int zlib_send_prepare(MultiFDSendParams *p, Error **errp) * with compression. zlib does not guarantee that this is safe, * therefore copy the page before calling deflate(). */ - memcpy(z->buf, p->pages->block->host + pages->offset[i], p->page_size); - zs->avail_in = p->page_size; + memcpy(z->buf, pages->block->host + pages->offset[i], page_size); + zs->avail_in = page_size; zs->next_in = z->buf; zs->avail_out = available; @@ -188,17 +160,7 @@ out: return 0; } -/** - * zlib_recv_setup: setup receive side - * - * Create the compressed channel and buffer. - * - * Returns 0 for success or -1 for error - * - * @p: Params for the channel that we are using - * @errp: pointer to an error - */ -static int zlib_recv_setup(MultiFDRecvParams *p, Error **errp) +static int multifd_zlib_recv_setup(MultiFDRecvParams *p, Error **errp) { struct zlib_data *z = g_new0(struct zlib_data, 1); z_stream *zs = &z->zs; @@ -224,14 +186,7 @@ static int zlib_recv_setup(MultiFDRecvParams *p, Error **errp) return 0; } -/** - * zlib_recv_cleanup: setup receive side - * - * For no compression this function does nothing. - * - * @p: Params for the channel that we are using - */ -static void zlib_recv_cleanup(MultiFDRecvParams *p) +static void multifd_zlib_recv_cleanup(MultiFDRecvParams *p) { struct zlib_data *z = p->compress_data; @@ -242,25 +197,15 @@ static void zlib_recv_cleanup(MultiFDRecvParams *p) p->compress_data = NULL; } -/** - * zlib_recv: read the data from the channel into actual pages - * - * Read the compressed buffer, and uncompress it into the actual - * pages. - * - * Returns 0 for success or -1 for error - * - * @p: Params for the channel that we are using - * @errp: pointer to an error - */ -static int zlib_recv(MultiFDRecvParams *p, Error **errp) +static int multifd_zlib_recv(MultiFDRecvParams *p, Error **errp) { struct zlib_data *z = p->compress_data; z_stream *zs = &z->zs; uint32_t in_size = p->next_packet_size; /* we measure the change of total_out */ uint32_t out_size = zs->total_out; - uint32_t expected_size = p->normal_num * p->page_size; + uint32_t page_size = multifd_ram_page_size(); + uint32_t expected_size = p->normal_num * page_size; uint32_t flags = p->flags & MULTIFD_FLAG_COMPRESSION_MASK; int ret; int i; @@ -296,7 +241,7 @@ static int zlib_recv(MultiFDRecvParams *p, Error **errp) flush = Z_SYNC_FLUSH; } - zs->avail_out = p->page_size; + zs->avail_out = page_size; zs->next_out = p->host + p->normal[i]; /* @@ -310,8 +255,8 @@ static int zlib_recv(MultiFDRecvParams *p, Error **errp) do { ret = inflate(zs, flush); } while (ret == Z_OK && zs->avail_in - && (zs->total_out - start) < p->page_size); - if (ret == Z_OK && (zs->total_out - start) < p->page_size) { + && (zs->total_out - start) < page_size); + if (ret == Z_OK && (zs->total_out - start) < page_size) { error_setg(errp, "multifd %u: inflate generated too few output", p->id); return -1; @@ -332,13 +277,13 @@ static int zlib_recv(MultiFDRecvParams *p, Error **errp) return 0; } -static MultiFDMethods multifd_zlib_ops = { - .send_setup = zlib_send_setup, - .send_cleanup = zlib_send_cleanup, - .send_prepare = zlib_send_prepare, - .recv_setup = zlib_recv_setup, - .recv_cleanup = zlib_recv_cleanup, - .recv = zlib_recv +static const MultiFDMethods multifd_zlib_ops = { + .send_setup = multifd_zlib_send_setup, + .send_cleanup = multifd_zlib_send_cleanup, + .send_prepare = multifd_zlib_send_prepare, + .recv_setup = multifd_zlib_recv_setup, + .recv_cleanup = multifd_zlib_recv_cleanup, + .recv = multifd_zlib_recv }; static void multifd_zlib_register(void) diff --git a/migration/multifd-zstd.c b/migration/multifd-zstd.c index ca17b7e310..53da33e048 100644 --- a/migration/multifd-zstd.c +++ b/migration/multifd-zstd.c @@ -37,17 +37,7 @@ struct zstd_data { /* Multifd zstd compression */ -/** - * zstd_send_setup: setup send side - * - * Setup each channel with zstd compression. - * - * Returns 0 for success or -1 for error - * - * @p: Params for the channel that we are using - * @errp: pointer to an error - */ -static int zstd_send_setup(MultiFDSendParams *p, Error **errp) +static int multifd_zstd_send_setup(MultiFDSendParams *p, Error **errp) { struct zstd_data *z = g_new0(struct zstd_data, 1); int res; @@ -83,15 +73,7 @@ static int zstd_send_setup(MultiFDSendParams *p, Error **errp) return 0; } -/** - * zstd_send_cleanup: cleanup send side - * - * Close the channel and return memory. - * - * @p: Params for the channel that we are using - * @errp: pointer to an error - */ -static void zstd_send_cleanup(MultiFDSendParams *p, Error **errp) +static void multifd_zstd_send_cleanup(MultiFDSendParams *p, Error **errp) { struct zstd_data *z = p->compress_data; @@ -106,20 +88,9 @@ static void zstd_send_cleanup(MultiFDSendParams *p, Error **errp) p->iov = NULL; } -/** - * zstd_send_prepare: prepare date to be able to send - * - * Create a compressed buffer with all the pages that we are going to - * send. - * - * Returns 0 for success or -1 for error - * - * @p: Params for the channel that we are using - * @errp: pointer to an error - */ -static int zstd_send_prepare(MultiFDSendParams *p, Error **errp) +static int multifd_zstd_send_prepare(MultiFDSendParams *p, Error **errp) { - MultiFDPages_t *pages = p->pages; + MultiFDPages_t *pages = &p->data->u.ram; struct zstd_data *z = p->compress_data; int ret; uint32_t i; @@ -138,8 +109,8 @@ static int zstd_send_prepare(MultiFDSendParams *p, Error **errp) if (i == pages->normal_num - 1) { flush = ZSTD_e_flush; } - z->in.src = p->pages->block->host + pages->offset[i]; - z->in.size = p->page_size; + z->in.src = pages->block->host + pages->offset[i]; + z->in.size = multifd_ram_page_size(); z->in.pos = 0; /* @@ -176,17 +147,7 @@ out: return 0; } -/** - * zstd_recv_setup: setup receive side - * - * Create the compressed channel and buffer. - * - * Returns 0 for success or -1 for error - * - * @p: Params for the channel that we are using - * @errp: pointer to an error - */ -static int zstd_recv_setup(MultiFDRecvParams *p, Error **errp) +static int multifd_zstd_recv_setup(MultiFDRecvParams *p, Error **errp) { struct zstd_data *z = g_new0(struct zstd_data, 1); int ret; @@ -220,14 +181,7 @@ static int zstd_recv_setup(MultiFDRecvParams *p, Error **errp) return 0; } -/** - * zstd_recv_cleanup: setup receive side - * - * For no compression this function does nothing. - * - * @p: Params for the channel that we are using - */ -static void zstd_recv_cleanup(MultiFDRecvParams *p) +static void multifd_zstd_recv_cleanup(MultiFDRecvParams *p) { struct zstd_data *z = p->compress_data; @@ -239,22 +193,12 @@ static void zstd_recv_cleanup(MultiFDRecvParams *p) p->compress_data = NULL; } -/** - * zstd_recv: read the data from the channel into actual pages - * - * Read the compressed buffer, and uncompress it into the actual - * pages. - * - * Returns 0 for success or -1 for error - * - * @p: Params for the channel that we are using - * @errp: pointer to an error - */ -static int zstd_recv(MultiFDRecvParams *p, Error **errp) +static int multifd_zstd_recv(MultiFDRecvParams *p, Error **errp) { uint32_t in_size = p->next_packet_size; uint32_t out_size = 0; - uint32_t expected_size = p->normal_num * p->page_size; + uint32_t page_size = multifd_ram_page_size(); + uint32_t expected_size = p->normal_num * page_size; uint32_t flags = p->flags & MULTIFD_FLAG_COMPRESSION_MASK; struct zstd_data *z = p->compress_data; int ret; @@ -286,7 +230,7 @@ static int zstd_recv(MultiFDRecvParams *p, Error **errp) for (i = 0; i < p->normal_num; i++) { ramblock_recv_bitmap_set_offset(p->block, p->normal[i]); z->out.dst = p->host + p->normal[i]; - z->out.size = p->page_size; + z->out.size = page_size; z->out.pos = 0; /* @@ -300,8 +244,8 @@ static int zstd_recv(MultiFDRecvParams *p, Error **errp) do { ret = ZSTD_decompressStream(z->zds, &z->out, &z->in); } while (ret > 0 && (z->in.size - z->in.pos > 0) - && (z->out.pos < p->page_size)); - if (ret > 0 && (z->out.pos < p->page_size)) { + && (z->out.pos < page_size)); + if (ret > 0 && (z->out.pos < page_size)) { error_setg(errp, "multifd %u: decompressStream buffer too small", p->id); return -1; @@ -321,13 +265,13 @@ static int zstd_recv(MultiFDRecvParams *p, Error **errp) return 0; } -static MultiFDMethods multifd_zstd_ops = { - .send_setup = zstd_send_setup, - .send_cleanup = zstd_send_cleanup, - .send_prepare = zstd_send_prepare, - .recv_setup = zstd_recv_setup, - .recv_cleanup = zstd_recv_cleanup, - .recv = zstd_recv +static const MultiFDMethods multifd_zstd_ops = { + .send_setup = multifd_zstd_send_setup, + .send_cleanup = multifd_zstd_send_cleanup, + .send_prepare = multifd_zstd_send_prepare, + .recv_setup = multifd_zstd_recv_setup, + .recv_cleanup = multifd_zstd_recv_cleanup, + .recv = multifd_zstd_recv }; static void multifd_zstd_register(void) diff --git a/migration/multifd.c b/migration/multifd.c index a6db05502a..9b200f4ad9 100644 --- a/migration/multifd.c +++ b/migration/multifd.c @@ -49,8 +49,6 @@ typedef struct { struct { MultiFDSendParams *params; - /* array of pages to sent */ - MultiFDPages_t *pages; /* * Global number of generated multifd packets. * @@ -78,7 +76,7 @@ struct { */ int exiting; /* multifd ops */ - MultiFDMethods *ops; + const MultiFDMethods *ops; } *multifd_send_state; struct { @@ -95,236 +93,50 @@ struct { uint64_t packet_num; int exiting; /* multifd ops */ - MultiFDMethods *ops; + const MultiFDMethods *ops; } *multifd_recv_state; -static bool multifd_use_packets(void) -{ - return !migrate_mapped_ram(); -} - -void multifd_send_channel_created(void) -{ - qemu_sem_post(&multifd_send_state->channels_created); -} - -static void multifd_set_file_bitmap(MultiFDSendParams *p) -{ - MultiFDPages_t *pages = p->pages; - - assert(pages->block); - - for (int i = 0; i < p->pages->normal_num; i++) { - ramblock_set_file_bmap_atomic(pages->block, pages->offset[i], true); - } - - for (int i = p->pages->normal_num; i < p->pages->num; i++) { - ramblock_set_file_bmap_atomic(pages->block, pages->offset[i], false); - } -} - -/* Multifd without compression */ - -/** - * nocomp_send_setup: setup send side - * - * @p: Params for the channel that we are using - * @errp: pointer to an error - */ -static int nocomp_send_setup(MultiFDSendParams *p, Error **errp) +MultiFDSendData *multifd_send_data_alloc(void) { - if (migrate_zero_copy_send()) { - p->write_flags |= QIO_CHANNEL_WRITE_FLAG_ZERO_COPY; - } - - if (multifd_use_packets()) { - /* We need one extra place for the packet header */ - p->iov = g_new0(struct iovec, p->page_count + 1); - } else { - p->iov = g_new0(struct iovec, p->page_count); - } + size_t max_payload_size, size_minus_payload; - return 0; -} - -/** - * nocomp_send_cleanup: cleanup send side - * - * For no compression this function does nothing. - * - * @p: Params for the channel that we are using - * @errp: pointer to an error - */ -static void nocomp_send_cleanup(MultiFDSendParams *p, Error **errp) -{ - g_free(p->iov); - p->iov = NULL; - return; -} - -static void multifd_send_prepare_iovs(MultiFDSendParams *p) -{ - MultiFDPages_t *pages = p->pages; - - for (int i = 0; i < pages->normal_num; i++) { - p->iov[p->iovs_num].iov_base = pages->block->host + pages->offset[i]; - p->iov[p->iovs_num].iov_len = p->page_size; - p->iovs_num++; - } - - p->next_packet_size = pages->normal_num * p->page_size; -} - -/** - * nocomp_send_prepare: prepare date to be able to send - * - * For no compression we just have to calculate the size of the - * packet. - * - * Returns 0 for success or -1 for error - * - * @p: Params for the channel that we are using - * @errp: pointer to an error - */ -static int nocomp_send_prepare(MultiFDSendParams *p, Error **errp) -{ - bool use_zero_copy_send = migrate_zero_copy_send(); - int ret; - - multifd_send_zero_page_detect(p); - - if (!multifd_use_packets()) { - multifd_send_prepare_iovs(p); - multifd_set_file_bitmap(p); - - return 0; - } - - if (!use_zero_copy_send) { - /* - * Only !zerocopy needs the header in IOV; zerocopy will - * send it separately. - */ - multifd_send_prepare_header(p); - } - - multifd_send_prepare_iovs(p); - p->flags |= MULTIFD_FLAG_NOCOMP; - - multifd_send_fill_packet(p); - - if (use_zero_copy_send) { - /* Send header first, without zerocopy */ - ret = qio_channel_write_all(p->c, (void *)p->packet, - p->packet_len, errp); - if (ret != 0) { - return -1; - } - } + /* + * MultiFDPages_t has a flexible array at the end, account for it + * when allocating MultiFDSendData. Use max() in case other types + * added to the union in the future are larger than + * (MultiFDPages_t + flex array). + */ + max_payload_size = MAX(multifd_ram_payload_size(), sizeof(MultiFDPayload)); - return 0; -} + /* + * Account for any holes the compiler might insert. We can't pack + * the structure because that misaligns the members and triggers + * Waddress-of-packed-member. + */ + size_minus_payload = sizeof(MultiFDSendData) - sizeof(MultiFDPayload); -/** - * nocomp_recv_setup: setup receive side - * - * For no compression this function does nothing. - * - * Returns 0 for success or -1 for error - * - * @p: Params for the channel that we are using - * @errp: pointer to an error - */ -static int nocomp_recv_setup(MultiFDRecvParams *p, Error **errp) -{ - p->iov = g_new0(struct iovec, p->page_count); - return 0; + return g_malloc0(size_minus_payload + max_payload_size); } -/** - * nocomp_recv_cleanup: setup receive side - * - * For no compression this function does nothing. - * - * @p: Params for the channel that we are using - */ -static void nocomp_recv_cleanup(MultiFDRecvParams *p) +static bool multifd_use_packets(void) { - g_free(p->iov); - p->iov = NULL; + return !migrate_mapped_ram(); } -/** - * nocomp_recv: read the data from the channel - * - * For no compression we just need to read things into the correct place. - * - * Returns 0 for success or -1 for error - * - * @p: Params for the channel that we are using - * @errp: pointer to an error - */ -static int nocomp_recv(MultiFDRecvParams *p, Error **errp) +void multifd_send_channel_created(void) { - uint32_t flags; - - if (!multifd_use_packets()) { - return multifd_file_recv_data(p, errp); - } - - flags = p->flags & MULTIFD_FLAG_COMPRESSION_MASK; - - if (flags != MULTIFD_FLAG_NOCOMP) { - error_setg(errp, "multifd %u: flags received %x flags expected %x", - p->id, flags, MULTIFD_FLAG_NOCOMP); - return -1; - } - - multifd_recv_zero_page_process(p); - - if (!p->normal_num) { - return 0; - } - - for (int i = 0; i < p->normal_num; i++) { - p->iov[i].iov_base = p->host + p->normal[i]; - p->iov[i].iov_len = p->page_size; - ramblock_recv_bitmap_set_offset(p->block, p->normal[i]); - } - return qio_channel_readv_all(p->c, p->iov, p->normal_num, errp); + qemu_sem_post(&multifd_send_state->channels_created); } -static MultiFDMethods multifd_nocomp_ops = { - .send_setup = nocomp_send_setup, - .send_cleanup = nocomp_send_cleanup, - .send_prepare = nocomp_send_prepare, - .recv_setup = nocomp_recv_setup, - .recv_cleanup = nocomp_recv_cleanup, - .recv = nocomp_recv -}; +static const MultiFDMethods *multifd_ops[MULTIFD_COMPRESSION__MAX] = {}; -static MultiFDMethods *multifd_ops[MULTIFD_COMPRESSION__MAX] = { - [MULTIFD_COMPRESSION_NONE] = &multifd_nocomp_ops, -}; - -void multifd_register_ops(int method, MultiFDMethods *ops) +void multifd_register_ops(int method, const MultiFDMethods *ops) { - assert(0 < method && method < MULTIFD_COMPRESSION__MAX); + assert(0 <= method && method < MULTIFD_COMPRESSION__MAX); + assert(!multifd_ops[method]); multifd_ops[method] = ops; } -/* Reset a MultiFDPages_t* object for the next use */ -static void multifd_pages_reset(MultiFDPages_t *pages) -{ - /* - * We don't need to touch offset[] array, because it will be - * overwritten later when reused. - */ - pages->num = 0; - pages->normal_num = 0; - pages->block = NULL; -} - static int multifd_send_initial_packet(MultiFDSendParams *p, Error **errp) { MultiFDInit_t msg = {}; @@ -389,160 +201,65 @@ static int multifd_recv_initial_packet(QIOChannel *c, Error **errp) return msg.id; } -static MultiFDPages_t *multifd_pages_init(uint32_t n) -{ - MultiFDPages_t *pages = g_new0(MultiFDPages_t, 1); - - pages->allocated = n; - pages->offset = g_new0(ram_addr_t, n); - - return pages; -} - -static void multifd_pages_clear(MultiFDPages_t *pages) -{ - multifd_pages_reset(pages); - pages->allocated = 0; - g_free(pages->offset); - pages->offset = NULL; - g_free(pages); -} - void multifd_send_fill_packet(MultiFDSendParams *p) { MultiFDPacket_t *packet = p->packet; - MultiFDPages_t *pages = p->pages; uint64_t packet_num; - uint32_t zero_num = pages->num - pages->normal_num; - int i; + bool sync_packet = p->flags & MULTIFD_FLAG_SYNC; + + memset(packet, 0, p->packet_len); + + packet->magic = cpu_to_be32(MULTIFD_MAGIC); + packet->version = cpu_to_be32(MULTIFD_VERSION); packet->flags = cpu_to_be32(p->flags); - packet->pages_alloc = cpu_to_be32(p->pages->allocated); - packet->normal_pages = cpu_to_be32(pages->normal_num); - packet->zero_pages = cpu_to_be32(zero_num); packet->next_packet_size = cpu_to_be32(p->next_packet_size); packet_num = qatomic_fetch_inc(&multifd_send_state->packet_num); packet->packet_num = cpu_to_be64(packet_num); - if (pages->block) { - strncpy(packet->ramblock, pages->block->idstr, 256); - } - - for (i = 0; i < pages->num; i++) { - /* there are architectures where ram_addr_t is 32 bit */ - uint64_t temp = pages->offset[i]; + p->packets_sent++; - packet->offset[i] = cpu_to_be64(temp); + if (!sync_packet) { + multifd_ram_fill_packet(p); } - p->packets_sent++; - p->total_normal_pages += pages->normal_num; - p->total_zero_pages += zero_num; - - trace_multifd_send(p->id, packet_num, pages->normal_num, zero_num, - p->flags, p->next_packet_size); + trace_multifd_send_fill(p->id, packet_num, + p->flags, p->next_packet_size); } static int multifd_recv_unfill_packet(MultiFDRecvParams *p, Error **errp) { - MultiFDPacket_t *packet = p->packet; - int i; + const MultiFDPacket_t *packet = p->packet; + uint32_t magic = be32_to_cpu(packet->magic); + uint32_t version = be32_to_cpu(packet->version); + int ret = 0; - packet->magic = be32_to_cpu(packet->magic); - if (packet->magic != MULTIFD_MAGIC) { - error_setg(errp, "multifd: received packet " - "magic %x and expected magic %x", - packet->magic, MULTIFD_MAGIC); + if (magic != MULTIFD_MAGIC) { + error_setg(errp, "multifd: received packet magic %x, expected %x", + magic, MULTIFD_MAGIC); return -1; } - packet->version = be32_to_cpu(packet->version); - if (packet->version != MULTIFD_VERSION) { - error_setg(errp, "multifd: received packet " - "version %u and expected version %u", - packet->version, MULTIFD_VERSION); + if (version != MULTIFD_VERSION) { + error_setg(errp, "multifd: received packet version %u, expected %u", + version, MULTIFD_VERSION); return -1; } p->flags = be32_to_cpu(packet->flags); - - packet->pages_alloc = be32_to_cpu(packet->pages_alloc); - /* - * If we received a packet that is 100 times bigger than expected - * just stop migration. It is a magic number. - */ - if (packet->pages_alloc > p->page_count) { - error_setg(errp, "multifd: received packet " - "with size %u and expected a size of %u", - packet->pages_alloc, p->page_count) ; - return -1; - } - - p->normal_num = be32_to_cpu(packet->normal_pages); - if (p->normal_num > packet->pages_alloc) { - error_setg(errp, "multifd: received packet " - "with %u normal pages and expected maximum pages are %u", - p->normal_num, packet->pages_alloc) ; - return -1; - } - - p->zero_num = be32_to_cpu(packet->zero_pages); - if (p->zero_num > packet->pages_alloc - p->normal_num) { - error_setg(errp, "multifd: received packet " - "with %u zero pages and expected maximum zero pages are %u", - p->zero_num, packet->pages_alloc - p->normal_num) ; - return -1; - } - p->next_packet_size = be32_to_cpu(packet->next_packet_size); p->packet_num = be64_to_cpu(packet->packet_num); p->packets_recved++; - p->total_normal_pages += p->normal_num; - p->total_zero_pages += p->zero_num; - - trace_multifd_recv(p->id, p->packet_num, p->normal_num, p->zero_num, - p->flags, p->next_packet_size); - - if (p->normal_num == 0 && p->zero_num == 0) { - return 0; - } - /* make sure that ramblock is 0 terminated */ - packet->ramblock[255] = 0; - p->block = qemu_ram_block_by_name(packet->ramblock); - if (!p->block) { - error_setg(errp, "multifd: unknown ram block %s", - packet->ramblock); - return -1; + if (!(p->flags & MULTIFD_FLAG_SYNC)) { + ret = multifd_ram_unfill_packet(p, errp); } - p->host = p->block->host; - for (i = 0; i < p->normal_num; i++) { - uint64_t offset = be64_to_cpu(packet->offset[i]); + trace_multifd_recv_unfill(p->id, p->packet_num, p->flags, + p->next_packet_size); - if (offset > (p->block->used_length - p->page_size)) { - error_setg(errp, "multifd: offset too long %" PRIu64 - " (max " RAM_ADDR_FMT ")", - offset, p->block->used_length); - return -1; - } - p->normal[i] = offset; - } - - for (i = 0; i < p->zero_num; i++) { - uint64_t offset = be64_to_cpu(packet->offset[p->normal_num + i]); - - if (offset > (p->block->used_length - p->page_size)) { - error_setg(errp, "multifd: offset too long %" PRIu64 - " (max " RAM_ADDR_FMT ")", - offset, p->block->used_length); - return -1; - } - p->zero[i] = offset; - } - - return 0; + return ret; } static bool multifd_send_should_exit(void) @@ -568,30 +285,25 @@ static void multifd_send_kick_main(MultiFDSendParams *p) } /* - * How we use multifd_send_state->pages and channel->pages? + * multifd_send() works by exchanging the MultiFDSendData object + * provided by the caller with an unused MultiFDSendData object from + * the next channel that is found to be idle. * - * We create a pages for each channel, and a main one. Each time that - * we need to send a batch of pages we interchange the ones between - * multifd_send_state and the channel that is sending it. There are - * two reasons for that: - * - to not have to do so many mallocs during migration - * - to make easier to know what to free at the end of migration + * The channel owns the data until it finishes transmitting and the + * caller owns the empty object until it fills it with data and calls + * this function again. No locking necessary. * - * This way we always know who is the owner of each "pages" struct, - * and we don't need any locking. It belongs to the migration thread - * or to the channel thread. Switching is safe because the migration - * thread is using the channel mutex when changing it, and the channel - * have to had finish with its own, otherwise pending_job can't be - * false. + * Switching is safe because both the migration thread and the channel + * thread have barriers in place to serialize access. * * Returns true if succeed, false otherwise. */ -static bool multifd_send_pages(void) +bool multifd_send(MultiFDSendData **send_data) { int i; static int next_channel; MultiFDSendParams *p = NULL; /* make happy gcc */ - MultiFDPages_t *pages = multifd_send_state->pages; + MultiFDSendData *tmp; if (multifd_send_should_exit()) { return false; @@ -626,66 +338,24 @@ static bool multifd_send_pages(void) * qatomic_store_release() in multifd_send_thread(). */ smp_mb_acquire(); - assert(!p->pages->num); - multifd_send_state->pages = p->pages; - p->pages = pages; - /* - * Making sure p->pages is setup before marking pending_job=true. Pairs - * with the qatomic_load_acquire() in multifd_send_thread(). - */ - qatomic_store_release(&p->pending_job, true); - qemu_sem_post(&p->sem); - return true; -} + assert(multifd_payload_empty(p->data)); -static inline bool multifd_queue_empty(MultiFDPages_t *pages) -{ - return pages->num == 0; -} - -static inline bool multifd_queue_full(MultiFDPages_t *pages) -{ - return pages->num == pages->allocated; -} - -static inline void multifd_enqueue(MultiFDPages_t *pages, ram_addr_t offset) -{ - pages->offset[pages->num++] = offset; -} - -/* Returns true if enqueue successful, false otherwise */ -bool multifd_queue_page(RAMBlock *block, ram_addr_t offset) -{ - MultiFDPages_t *pages; - -retry: - pages = multifd_send_state->pages; - - /* If the queue is empty, we can already enqueue now */ - if (multifd_queue_empty(pages)) { - pages->block = block; - multifd_enqueue(pages, offset); - return true; - } + /* + * Swap the pointers. The channel gets the client data for + * transferring and the client gets back an unused data slot. + */ + tmp = *send_data; + *send_data = p->data; + p->data = tmp; /* - * Not empty, meanwhile we need a flush. It can because of either: - * - * (1) The page is not on the same ramblock of previous ones, or, - * (2) The queue is full. - * - * After flush, always retry. + * Making sure p->data is setup before marking pending_job=true. Pairs + * with the qatomic_load_acquire() in multifd_send_thread(). */ - if (pages->block != block || multifd_queue_full(pages)) { - if (!multifd_send_pages()) { - return false; - } - goto retry; - } + qatomic_store_release(&p->pending_job, true); + qemu_sem_post(&p->sem); - /* Not empty, and we still have space, do it! */ - multifd_enqueue(pages, offset); return true; } @@ -790,12 +460,13 @@ static bool multifd_send_cleanup_channel(MultiFDSendParams *p, Error **errp) qemu_sem_destroy(&p->sem_sync); g_free(p->name); p->name = NULL; - multifd_pages_clear(p->pages); - p->pages = NULL; + g_free(p->data); + p->data = NULL; p->packet_len = 0; g_free(p->packet); p->packet = NULL; multifd_send_state->ops->send_cleanup(p, errp); + assert(!p->iov); return *errp == NULL; } @@ -808,8 +479,6 @@ static void multifd_send_cleanup_state(void) qemu_sem_destroy(&multifd_send_state->channels_ready); g_free(multifd_send_state->params); multifd_send_state->params = NULL; - multifd_pages_clear(multifd_send_state->pages); - multifd_send_state->pages = NULL; g_free(multifd_send_state); multifd_send_state = NULL; } @@ -859,16 +528,6 @@ int multifd_send_sync_main(void) int i; bool flush_zero_copy; - if (!migrate_multifd()) { - return 0; - } - if (multifd_send_state->pages->num) { - if (!multifd_send_pages()) { - error_report("%s: multifd_send_pages fail", __func__); - return -1; - } - } - flush_zero_copy = migrate_zero_copy_send(); for (i = 0; i < migrate_multifd_channels(); i++) { @@ -937,14 +596,12 @@ static void *multifd_send_thread(void *opaque) } /* - * Read pending_job flag before p->pages. Pairs with the - * qatomic_store_release() in multifd_send_pages(). + * Read pending_job flag before p->data. Pairs with the + * qatomic_store_release() in multifd_send(). */ if (qatomic_load_acquire(&p->pending_job)) { - MultiFDPages_t *pages = p->pages; - p->iovs_num = 0; - assert(pages->num); + assert(!multifd_payload_empty(p->data)); ret = multifd_send_state->ops->send_prepare(p, &local_err); if (ret != 0) { @@ -953,7 +610,7 @@ static void *multifd_send_thread(void *opaque) if (migrate_mapped_ram()) { ret = file_write_ramblock_iov(p->c, p->iov, p->iovs_num, - p->pages->block, &local_err); + &p->data->u.ram, &local_err); } else { ret = qio_channel_writev_full_all(p->c, p->iov, p->iovs_num, NULL, 0, p->write_flags, @@ -966,16 +623,14 @@ static void *multifd_send_thread(void *opaque) stat64_add(&mig_stats.multifd_bytes, p->next_packet_size + p->packet_len); - stat64_add(&mig_stats.normal_pages, pages->normal_num); - stat64_add(&mig_stats.zero_pages, pages->num - pages->normal_num); - multifd_pages_reset(p->pages); p->next_packet_size = 0; + multifd_set_payload_type(p->data, MULTIFD_PAYLOAD_NONE); /* - * Making sure p->pages is published before saying "we're + * Making sure p->data is published before saying "we're * free". Pairs with the smp_mb_acquire() in - * multifd_send_pages(). + * multifd_send(). */ qatomic_store_release(&p->pending_job, false); } else { @@ -1015,8 +670,7 @@ out: rcu_unregister_thread(); migration_threads_remove(thread); - trace_multifd_send_thread_end(p->id, p->packets_sent, p->total_normal_pages, - p->total_zero_pages); + trace_multifd_send_thread_end(p->id, p->packets_sent); return NULL; } @@ -1157,7 +811,7 @@ bool multifd_send_setup(void) { MigrationState *s = migrate_get_current(); int thread_count, ret = 0; - uint32_t page_count = MULTIFD_PACKET_SIZE / qemu_target_page_size(); + uint32_t page_count = multifd_ram_page_count(); bool use_packets = multifd_use_packets(); uint8_t i; @@ -1168,7 +822,6 @@ bool multifd_send_setup(void) thread_count = migrate_multifd_channels(); multifd_send_state = g_malloc0(sizeof(*multifd_send_state)); multifd_send_state->params = g_new0(MultiFDSendParams, thread_count); - multifd_send_state->pages = multifd_pages_init(page_count); qemu_sem_init(&multifd_send_state->channels_created, 0); qemu_sem_init(&multifd_send_state->channels_ready, 0); qatomic_set(&multifd_send_state->exiting, 0); @@ -1181,18 +834,14 @@ bool multifd_send_setup(void) qemu_sem_init(&p->sem, 0); qemu_sem_init(&p->sem_sync, 0); p->id = i; - p->pages = multifd_pages_init(page_count); + p->data = multifd_send_data_alloc(); if (use_packets) { p->packet_len = sizeof(MultiFDPacket_t) + sizeof(uint64_t) * page_count; p->packet = g_malloc0(p->packet_len); - p->packet->magic = cpu_to_be32(MULTIFD_MAGIC); - p->packet->version = cpu_to_be32(MULTIFD_VERSION); } p->name = g_strdup_printf("mig/src/send_%d", i); - p->page_size = qemu_target_page_size(); - p->page_count = page_count; p->write_flags = 0; if (!multifd_new_send_channel_create(p, &local_err)) { @@ -1223,6 +872,7 @@ bool multifd_send_setup(void) migrate_set_error(s, local_err); goto err; } + assert(p->iov); } return true; @@ -1501,7 +1151,9 @@ static void *multifd_recv_thread(void *opaque) flags = p->flags; /* recv methods don't know how to handle the SYNC flag */ p->flags &= ~MULTIFD_FLAG_SYNC; - has_data = p->normal_num || p->zero_num; + if (!(flags & MULTIFD_FLAG_SYNC)) { + has_data = p->normal_num || p->zero_num; + } qemu_mutex_unlock(&p->mutex); } else { /* @@ -1542,7 +1194,6 @@ static void *multifd_recv_thread(void *opaque) qemu_sem_wait(&p->sem_sync); } } else { - p->total_normal_pages += p->data->size / qemu_target_page_size(); p->data->size = 0; /* * Order data->size update before clearing @@ -1559,9 +1210,7 @@ static void *multifd_recv_thread(void *opaque) } rcu_unregister_thread(); - trace_multifd_recv_thread_end(p->id, p->packets_recved, - p->total_normal_pages, - p->total_zero_pages); + trace_multifd_recv_thread_end(p->id, p->packets_recved); return NULL; } @@ -1569,7 +1218,7 @@ static void *multifd_recv_thread(void *opaque) int multifd_recv_setup(Error **errp) { int thread_count; - uint32_t page_count = MULTIFD_PACKET_SIZE / qemu_target_page_size(); + uint32_t page_count = multifd_ram_page_count(); bool use_packets = multifd_use_packets(); uint8_t i; @@ -1613,8 +1262,6 @@ int multifd_recv_setup(Error **errp) p->name = g_strdup_printf("mig/dst/recv_%d", i); p->normal = g_new0(ram_addr_t, page_count); p->zero = g_new0(ram_addr_t, page_count); - p->page_count = page_count; - p->page_size = qemu_target_page_size(); } for (i = 0; i < thread_count; i++) { @@ -1687,17 +1334,3 @@ void multifd_recv_new_channel(QIOChannel *ioc, Error **errp) QEMU_THREAD_JOINABLE); qatomic_inc(&multifd_recv_state->count); } - -bool multifd_send_prepare_common(MultiFDSendParams *p) -{ - multifd_send_zero_page_detect(p); - - if (!p->pages->normal_num) { - p->next_packet_size = 0; - return false; - } - - multifd_send_prepare_header(p); - - return true; -} diff --git a/migration/multifd.h b/migration/multifd.h index 0ecd6f47d7..3bb96e9558 100644 --- a/migration/multifd.h +++ b/migration/multifd.h @@ -13,9 +13,11 @@ #ifndef QEMU_MIGRATION_MULTIFD_H #define QEMU_MIGRATION_MULTIFD_H +#include "exec/target_page.h" #include "ram.h" typedef struct MultiFDRecvData MultiFDRecvData; +typedef struct MultiFDSendData MultiFDSendData; bool multifd_send_setup(void); void multifd_send_shutdown(void); @@ -75,11 +77,9 @@ typedef struct { uint32_t num; /* number of normal pages */ uint32_t normal_num; - /* number of allocated pages */ - uint32_t allocated; - /* offset of each page */ - ram_addr_t *offset; RAMBlock *block; + /* offset of each page */ + ram_addr_t offset[]; } MultiFDPages_t; struct MultiFDRecvData { @@ -89,6 +89,31 @@ struct MultiFDRecvData { off_t file_offset; }; +typedef enum { + MULTIFD_PAYLOAD_NONE, + MULTIFD_PAYLOAD_RAM, +} MultiFDPayloadType; + +typedef union MultiFDPayload { + MultiFDPages_t ram; +} MultiFDPayload; + +struct MultiFDSendData { + MultiFDPayloadType type; + MultiFDPayload u; +}; + +static inline bool multifd_payload_empty(MultiFDSendData *data) +{ + return data->type == MULTIFD_PAYLOAD_NONE; +} + +static inline void multifd_set_payload_type(MultiFDSendData *data, + MultiFDPayloadType type) +{ + data->type = type; +} + typedef struct { /* Fields are only written at creating/deletion time */ /* No lock required for them, they are read only */ @@ -106,10 +131,6 @@ typedef struct { QIOChannel *c; /* packet allocated len */ uint32_t packet_len; - /* guest page size */ - uint32_t page_size; - /* number of pages in a full packet */ - uint32_t page_count; /* multifd flags for sending ram */ int write_flags; @@ -131,12 +152,7 @@ typedef struct { */ bool pending_job; bool pending_sync; - /* array of pages to sent. - * The owner of 'pages' depends of 'pending_job' value: - * pending_job == 0 -> migration_thread can use it. - * pending_job != 0 -> multifd_channel can use it. - */ - MultiFDPages_t *pages; + MultiFDSendData *data; /* thread local variables. No locking required */ @@ -146,10 +162,6 @@ typedef struct { uint32_t next_packet_size; /* packets sent through this channel */ uint64_t packets_sent; - /* non zero pages sent through this channel */ - uint64_t total_normal_pages; - /* zero pages sent through this channel */ - uint64_t total_zero_pages; /* buffers to send */ struct iovec *iov; /* number of iovs used */ @@ -173,10 +185,6 @@ typedef struct { QIOChannel *c; /* packet allocated len */ uint32_t packet_len; - /* guest page size */ - uint32_t page_size; - /* number of pages in a full packet */ - uint32_t page_count; /* syncs main thread and channels */ QemuSemaphore sem_sync; @@ -206,10 +214,6 @@ typedef struct { RAMBlock *block; /* ramblock host address */ uint8_t *host; - /* non zero pages recv through this channel */ - uint64_t total_normal_pages; - /* zero pages recv through this channel */ - uint64_t total_zero_pages; /* buffers to recv */ struct iovec *iov; /* Pages that are not zero */ @@ -225,21 +229,85 @@ typedef struct { } MultiFDRecvParams; typedef struct { - /* Setup for sending side */ + /* + * The send_setup, send_cleanup, send_prepare are only called on + * the QEMU instance at the migration source. + */ + + /* + * Setup for sending side. Called once per channel during channel + * setup phase. + * + * Must allocate p->iov. If packets are in use (default), one + * extra iovec must be allocated for the packet header. Any memory + * allocated in this hook must be released at send_cleanup. + * + * p->write_flags may be used for passing flags to the QIOChannel. + * + * p->compression_data may be used by compression methods to store + * compression data. + */ int (*send_setup)(MultiFDSendParams *p, Error **errp); - /* Cleanup for sending side */ + + /* + * Cleanup for sending side. Called once per channel during + * channel cleanup phase. + */ void (*send_cleanup)(MultiFDSendParams *p, Error **errp); - /* Prepare the send packet */ + + /* + * Prepare the send packet. Called as a result of multifd_send() + * on the client side, with p pointing to the MultiFDSendParams of + * a channel that is currently idle. + * + * Must populate p->iov with the data to be sent, increment + * p->iovs_num to match the amount of iovecs used and set + * p->next_packet_size with the amount of data currently present + * in p->iov. + * + * Must indicate whether this is a compression packet by setting + * p->flags. + * + * As a last step, if packets are in use (default), must prepare + * the packet by calling multifd_send_fill_packet(). + */ int (*send_prepare)(MultiFDSendParams *p, Error **errp); - /* Setup for receiving side */ + + /* + * The recv_setup, recv_cleanup, recv are only called on the QEMU + * instance at the migration destination. + */ + + /* + * Setup for receiving side. Called once per channel during + * channel setup phase. May be empty. + * + * May allocate data structures for the receiving of data. May use + * p->iov. Compression methods may use p->compress_data. + */ int (*recv_setup)(MultiFDRecvParams *p, Error **errp); - /* Cleanup for receiving side */ + + /* + * Cleanup for receiving side. Called once per channel during + * channel cleanup phase. May be empty. + */ void (*recv_cleanup)(MultiFDRecvParams *p); - /* Read all data */ + + /* + * Data receive method. Called as a result of multifd_recv() on + * the client side, with p pointing to the MultiFDRecvParams of a + * channel that is currently idle. Only called if there is data + * available to receive. + * + * Must validate p->flags according to what was set at + * send_prepare. + * + * Must read the data from the QIOChannel p->c. + */ int (*recv)(MultiFDRecvParams *p, Error **errp); } MultiFDMethods; -void multifd_register_ops(int method, MultiFDMethods *ops); +void multifd_register_ops(int method, const MultiFDMethods *ops); void multifd_send_fill_packet(MultiFDSendParams *p); bool multifd_send_prepare_common(MultiFDSendParams *p); void multifd_send_zero_page_detect(MultiFDSendParams *p); @@ -253,5 +321,23 @@ static inline void multifd_send_prepare_header(MultiFDSendParams *p) } void multifd_channel_connect(MultiFDSendParams *p, QIOChannel *ioc); +bool multifd_send(MultiFDSendData **send_data); +MultiFDSendData *multifd_send_data_alloc(void); + +static inline uint32_t multifd_ram_page_size(void) +{ + return qemu_target_page_size(); +} + +static inline uint32_t multifd_ram_page_count(void) +{ + return MULTIFD_PACKET_SIZE / qemu_target_page_size(); +} +void multifd_ram_save_setup(void); +void multifd_ram_save_cleanup(void); +int multifd_ram_flush_and_sync(void); +size_t multifd_ram_payload_size(void); +void multifd_ram_fill_packet(MultiFDSendParams *p); +int multifd_ram_unfill_packet(MultiFDRecvParams *p, Error **errp); #endif diff --git a/migration/ram.c b/migration/ram.c index edec1a2d07..67ca3d5d51 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -1326,7 +1326,7 @@ static int find_dirty_block(RAMState *rs, PageSearchStatus *pss) (!migrate_multifd_flush_after_each_section() || migrate_mapped_ram())) { QEMUFile *f = rs->pss[RAM_CHANNEL_PRECOPY].pss_channel; - int ret = multifd_send_sync_main(); + int ret = multifd_ram_flush_and_sync(); if (ret < 0) { return ret; } @@ -2387,6 +2387,7 @@ static void ram_save_cleanup(void *opaque) ram_bitmaps_destroy(); xbzrle_cleanup(); + multifd_ram_save_cleanup(); ram_state_cleanup(rsp); g_free(migration_ops); migration_ops = NULL; @@ -3058,13 +3059,14 @@ static int ram_save_setup(QEMUFile *f, void *opaque, Error **errp) migration_ops = g_malloc0(sizeof(MigrationOps)); if (migrate_multifd()) { + multifd_ram_save_setup(); migration_ops->ram_save_target_page = ram_save_target_page_multifd; } else { migration_ops->ram_save_target_page = ram_save_target_page_legacy; } bql_unlock(); - ret = multifd_send_sync_main(); + ret = multifd_ram_flush_and_sync(); bql_lock(); if (ret < 0) { error_setg(errp, "%s: multifd synchronization failed", __func__); @@ -3211,7 +3213,7 @@ out: && migration_is_setup_or_active()) { if (migrate_multifd() && migrate_multifd_flush_after_each_section() && !migrate_mapped_ram()) { - ret = multifd_send_sync_main(); + ret = multifd_ram_flush_and_sync(); if (ret < 0) { return ret; } @@ -3283,7 +3285,7 @@ static int ram_save_complete(QEMUFile *f, void *opaque) } } - ret = multifd_send_sync_main(); + ret = multifd_ram_flush_and_sync(); if (ret < 0) { return ret; } diff --git a/migration/savevm.c b/migration/savevm.c index 6bb404b9c8..d500eae979 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -2578,8 +2578,7 @@ static bool check_section_footer(QEMUFile *f, SaveStateEntry *se) } static int -qemu_loadvm_section_start_full(QEMUFile *f, MigrationIncomingState *mis, - uint8_t type) +qemu_loadvm_section_start_full(QEMUFile *f, uint8_t type) { bool trace_downtime = (type == QEMU_VM_SECTION_FULL); uint32_t instance_id, version_id, section_id; @@ -2657,8 +2656,7 @@ qemu_loadvm_section_start_full(QEMUFile *f, MigrationIncomingState *mis, } static int -qemu_loadvm_section_part_end(QEMUFile *f, MigrationIncomingState *mis, - uint8_t type) +qemu_loadvm_section_part_end(QEMUFile *f, uint8_t type) { bool trace_downtime = (type == QEMU_VM_SECTION_END); int64_t start_ts, end_ts; @@ -2893,14 +2891,14 @@ retry: switch (section_type) { case QEMU_VM_SECTION_START: case QEMU_VM_SECTION_FULL: - ret = qemu_loadvm_section_start_full(f, mis, section_type); + ret = qemu_loadvm_section_start_full(f, section_type); if (ret < 0) { goto out; } break; case QEMU_VM_SECTION_PART: case QEMU_VM_SECTION_END: - ret = qemu_loadvm_section_part_end(f, mis, section_type); + ret = qemu_loadvm_section_part_end(f, section_type); if (ret < 0) { goto out; } diff --git a/migration/trace-events b/migration/trace-events index 0b7c3324fb..c65902f042 100644 --- a/migration/trace-events +++ b/migration/trace-events @@ -128,21 +128,22 @@ postcopy_preempt_reset_channel(void) "" # multifd.c multifd_new_send_channel_async(uint8_t id) "channel %u" multifd_new_send_channel_async_error(uint8_t id, void *err) "channel=%u err=%p" -multifd_recv(uint8_t id, uint64_t packet_num, uint32_t normal, uint32_t zero, uint32_t flags, uint32_t next_packet_size) "channel %u packet_num %" PRIu64 " normal pages %u zero pages %u flags 0x%x next packet size %u" +multifd_recv_unfill(uint8_t id, uint64_t packet_num, uint32_t flags, uint32_t next_packet_size) "channel %u packet_num %" PRIu64 " flags 0x%x next packet size %u" multifd_recv_new_channel(uint8_t id) "channel %u" multifd_recv_sync_main(long packet_num) "packet num %ld" multifd_recv_sync_main_signal(uint8_t id) "channel %u" multifd_recv_sync_main_wait(uint8_t id) "iter %u" multifd_recv_terminate_threads(bool error) "error %d" -multifd_recv_thread_end(uint8_t id, uint64_t packets, uint64_t normal_pages, uint64_t zero_pages) "channel %u packets %" PRIu64 " normal pages %" PRIu64 " zero pages %" PRIu64 +multifd_recv_thread_end(uint8_t id, uint64_t packets) "channel %u packets %" PRIu64 multifd_recv_thread_start(uint8_t id) "%u" -multifd_send(uint8_t id, uint64_t packet_num, uint32_t normal_pages, uint32_t zero_pages, uint32_t flags, uint32_t next_packet_size) "channel %u packet_num %" PRIu64 " normal pages %u zero pages %u flags 0x%x next packet size %u" +multifd_send_fill(uint8_t id, uint64_t packet_num, uint32_t flags, uint32_t next_packet_size) "channel %u packet_num %" PRIu64 " flags 0x%x next packet size %u" +multifd_send_ram_fill(uint8_t id, uint32_t normal, uint32_t zero) "channel %u normal pages %u zero pages %u" multifd_send_error(uint8_t id) "channel %u" multifd_send_sync_main(long packet_num) "packet num %ld" multifd_send_sync_main_signal(uint8_t id) "channel %u" multifd_send_sync_main_wait(uint8_t id) "channel %u" multifd_send_terminate_threads(void) "" -multifd_send_thread_end(uint8_t id, uint64_t packets, uint64_t normal_pages, uint64_t zero_pages) "channel %u packets %" PRIu64 " normal pages %" PRIu64 " zero pages %" PRIu64 +multifd_send_thread_end(uint8_t id, uint64_t packets) "channel %u packets %" PRIu64 multifd_send_thread_start(uint8_t id) "%u" multifd_tls_outgoing_handshake_start(void *ioc, void *tioc, const char *hostname) "ioc=%p tioc=%p hostname=%s" multifd_tls_outgoing_handshake_error(void *ioc, const char *err) "ioc=%p err=%s" diff --git a/python/wheels/pycotap-1.3.1-py3-none-any.whl b/python/wheels/pycotap-1.3.1-py3-none-any.whl new file mode 100644 index 0000000000..9c2c7d2593 --- /dev/null +++ b/python/wheels/pycotap-1.3.1-py3-none-any.whl Binary files differdiff --git a/pythondeps.toml b/pythondeps.toml index f6e590fdd8..0a7f69b196 100644 --- a/pythondeps.toml +++ b/pythondeps.toml @@ -20,6 +20,7 @@ [meson] # The install key should match the version in python/wheels/ meson = { accepted = ">=1.1.0", installed = "1.2.3", canary = "meson" } +pycotap = { accepted = ">=1.1.0", installed = "1.3.1" } [docs] # Please keep the installed versions in sync with docs/requirements.txt @@ -30,5 +31,5 @@ sphinx_rtd_theme = { accepted = ">=0.5", installed = "1.1.1" } # Note that qemu.git/python/ is always implicitly installed. # Prefer an LTS version when updating the accepted versions of # avocado-framework, for example right now the limit is 92.x. -avocado-framework = { accepted = "(>=88.1, <93.0)", installed = "88.1", canary = "avocado" } +avocado-framework = { accepted = "(>=103.0, <104.0)", installed = "103.0", canary = "avocado" } pycdlib = { accepted = ">=1.11.0" } diff --git a/target/arm/cpu-features.h b/target/arm/cpu-features.h index c59ca104fe..cfb82c23ca 100644 --- a/target/arm/cpu-features.h +++ b/target/arm/cpu-features.h @@ -556,6 +556,11 @@ static inline bool isar_feature_aa64_bf16(const ARMISARegisters *id) return FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, BF16) != 0; } +static inline bool isar_feature_aa64_ebf16(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, BF16) > 1; +} + static inline bool isar_feature_aa64_rcpc_8_3(const ARMISARegisters *id) { return FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, LRCPC) != 0; diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 9a3fd59562..f065756c5c 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -1707,6 +1707,7 @@ void vfp_set_fpscr(CPUARMState *env, uint32_t val); #define FPCR_OFE (1 << 10) /* Overflow exception trap enable */ #define FPCR_UFE (1 << 11) /* Underflow exception trap enable */ #define FPCR_IXE (1 << 12) /* Inexact exception trap enable */ +#define FPCR_EBF (1 << 13) /* Extended BFloat16 behaviors */ #define FPCR_IDE (1 << 15) /* Input Denormal exception trap enable */ #define FPCR_LEN_MASK (7 << 16) /* LEN, A-profile only */ #define FPCR_FZ16 (1 << 19) /* ARMv8.2+, FP16 flush-to-zero */ diff --git a/target/arm/helper.h b/target/arm/helper.h index 970d059dec..b463be38c5 100644 --- a/target/arm/helper.h +++ b/target/arm/helper.h @@ -1027,13 +1027,13 @@ DEF_HELPER_FLAGS_5(gvec_ummla_b, TCG_CALL_NO_RWG, DEF_HELPER_FLAGS_5(gvec_usmmla_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_bfdot, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_bfdot_idx, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(gvec_bfdot, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_6(gvec_bfdot_idx, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_5(gvec_bfmmla, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(gvec_bfmmla, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, env, i32) DEF_HELPER_FLAGS_6(gvec_bfmlal, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, ptr, i32) diff --git a/target/arm/tcg/cpu64.c b/target/arm/tcg/cpu64.c index fe232eb306..79258a7c92 100644 --- a/target/arm/tcg/cpu64.c +++ b/target/arm/tcg/cpu64.c @@ -1160,7 +1160,7 @@ void aarch64_max_tcg_initfn(Object *obj) t = FIELD_DP64(t, ID_AA64ISAR1, FRINTTS, 1); /* FEAT_FRINTTS */ t = FIELD_DP64(t, ID_AA64ISAR1, SB, 1); /* FEAT_SB */ t = FIELD_DP64(t, ID_AA64ISAR1, SPECRES, 1); /* FEAT_SPECRES */ - t = FIELD_DP64(t, ID_AA64ISAR1, BF16, 1); /* FEAT_BF16 */ + t = FIELD_DP64(t, ID_AA64ISAR1, BF16, 2); /* FEAT_BF16, FEAT_EBF16 */ t = FIELD_DP64(t, ID_AA64ISAR1, DGH, 1); /* FEAT_DGH */ t = FIELD_DP64(t, ID_AA64ISAR1, I8MM, 1); /* FEAT_I8MM */ cpu->isar.id_aa64isar1 = t; @@ -1244,7 +1244,7 @@ void aarch64_max_tcg_initfn(Object *obj) t = FIELD_DP64(t, ID_AA64ZFR0, SVEVER, 1); t = FIELD_DP64(t, ID_AA64ZFR0, AES, 2); /* FEAT_SVE_PMULL128 */ t = FIELD_DP64(t, ID_AA64ZFR0, BITPERM, 1); /* FEAT_SVE_BitPerm */ - t = FIELD_DP64(t, ID_AA64ZFR0, BFLOAT16, 1); /* FEAT_BF16 */ + t = FIELD_DP64(t, ID_AA64ZFR0, BFLOAT16, 2); /* FEAT_BF16, FEAT_EBF16 */ t = FIELD_DP64(t, ID_AA64ZFR0, SHA3, 1); /* FEAT_SVE_SHA3 */ t = FIELD_DP64(t, ID_AA64ZFR0, SM4, 1); /* FEAT_SVE_SM4 */ t = FIELD_DP64(t, ID_AA64ZFR0, I8MM, 1); /* FEAT_I8MM */ diff --git a/target/arm/tcg/helper-sme.h b/target/arm/tcg/helper-sme.h index d22bf9d21b..59ecaa1548 100644 --- a/target/arm/tcg/helper-sme.h +++ b/target/arm/tcg/helper-sme.h @@ -126,8 +126,8 @@ DEF_HELPER_FLAGS_7(sme_fmopa_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_7(sme_fmopa_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_6(sme_bfmopa, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_7(sme_bfmopa, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, env, i32) DEF_HELPER_FLAGS_6(sme_smopa_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_6(sme_umopa_s, TCG_CALL_NO_RWG, diff --git a/target/arm/tcg/sme_helper.c b/target/arm/tcg/sme_helper.c index 02106809ce..8cf12654e5 100644 --- a/target/arm/tcg/sme_helper.c +++ b/target/arm/tcg/sme_helper.c @@ -1079,38 +1079,68 @@ void HELPER(sme_fmopa_h)(void *vza, void *vzn, void *vzm, void *vpn, } } -void HELPER(sme_bfmopa)(void *vza, void *vzn, void *vzm, void *vpn, - void *vpm, uint32_t desc) +void HELPER(sme_bfmopa)(void *vza, void *vzn, void *vzm, + void *vpn, void *vpm, CPUARMState *env, uint32_t desc) { intptr_t row, col, oprsz = simd_maxsz(desc); uint32_t neg = simd_data(desc) * 0x80008000u; uint16_t *pn = vpn, *pm = vpm; + float_status fpst, fpst_odd; - for (row = 0; row < oprsz; ) { - uint16_t prow = pn[H2(row >> 4)]; - do { - void *vza_row = vza + tile_vslice_offset(row); - uint32_t n = *(uint32_t *)(vzn + H1_4(row)); + if (is_ebf(env, &fpst, &fpst_odd)) { + for (row = 0; row < oprsz; ) { + uint16_t prow = pn[H2(row >> 4)]; + do { + void *vza_row = vza + tile_vslice_offset(row); + uint32_t n = *(uint32_t *)(vzn + H1_4(row)); - n = f16mop_adj_pair(n, prow, neg); + n = f16mop_adj_pair(n, prow, neg); - for (col = 0; col < oprsz; ) { - uint16_t pcol = pm[H2(col >> 4)]; - do { - if (prow & pcol & 0b0101) { - uint32_t *a = vza_row + H1_4(col); - uint32_t m = *(uint32_t *)(vzm + H1_4(col)); + for (col = 0; col < oprsz; ) { + uint16_t pcol = pm[H2(col >> 4)]; + do { + if (prow & pcol & 0b0101) { + uint32_t *a = vza_row + H1_4(col); + uint32_t m = *(uint32_t *)(vzm + H1_4(col)); - m = f16mop_adj_pair(m, pcol, 0); - *a = bfdotadd(*a, n, m); - } - col += 4; - pcol >>= 4; - } while (col & 15); - } - row += 4; - prow >>= 4; - } while (row & 15); + m = f16mop_adj_pair(m, pcol, 0); + *a = bfdotadd_ebf(*a, n, m, &fpst, &fpst_odd); + } + col += 4; + pcol >>= 4; + } while (col & 15); + } + row += 4; + prow >>= 4; + } while (row & 15); + } + } else { + for (row = 0; row < oprsz; ) { + uint16_t prow = pn[H2(row >> 4)]; + do { + void *vza_row = vza + tile_vslice_offset(row); + uint32_t n = *(uint32_t *)(vzn + H1_4(row)); + + n = f16mop_adj_pair(n, prow, neg); + + for (col = 0; col < oprsz; ) { + uint16_t pcol = pm[H2(col >> 4)]; + do { + if (prow & pcol & 0b0101) { + uint32_t *a = vza_row + H1_4(col); + uint32_t m = *(uint32_t *)(vzm + H1_4(col)); + + m = f16mop_adj_pair(m, pcol, 0); + *a = bfdotadd(*a, n, m, &fpst); + } + col += 4; + pcol >>= 4; + } while (col & 15); + } + row += 4; + prow >>= 4; + } while (row & 15); + } } } diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 4684e7eb6e..6d5f12e8f5 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -736,6 +736,22 @@ static void gen_gvec_op4_ool(DisasContext *s, bool is_q, int rd, int rn, } /* + * Expand a 4-operand operation using an out-of-line helper that takes + * a pointer to the CPU env. + */ +static void gen_gvec_op4_env(DisasContext *s, bool is_q, int rd, int rn, + int rm, int ra, int data, + gen_helper_gvec_4_ptr *fn) +{ + tcg_gen_gvec_4_ptr(vec_full_reg_offset(s, rd), + vec_full_reg_offset(s, rn), + vec_full_reg_offset(s, rm), + vec_full_reg_offset(s, ra), + tcg_env, + is_q ? 16 : 8, vec_full_reg_size(s), data, fn); +} + +/* * Expand a 4-operand + fpstatus pointer + simd data value operation using * an out-of-line helper. */ @@ -5608,11 +5624,20 @@ static bool do_dot_vector(DisasContext *s, arg_qrrr_e *a, return true; } +static bool do_dot_vector_env(DisasContext *s, arg_qrrr_e *a, + gen_helper_gvec_4_ptr *fn) +{ + if (fp_access_check(s)) { + gen_gvec_op4_env(s, a->q, a->rd, a->rn, a->rm, a->rd, 0, fn); + } + return true; +} + TRANS_FEAT(SDOT_v, aa64_dp, do_dot_vector, a, gen_helper_gvec_sdot_b) TRANS_FEAT(UDOT_v, aa64_dp, do_dot_vector, a, gen_helper_gvec_udot_b) TRANS_FEAT(USDOT_v, aa64_i8mm, do_dot_vector, a, gen_helper_gvec_usdot_b) -TRANS_FEAT(BFDOT_v, aa64_bf16, do_dot_vector, a, gen_helper_gvec_bfdot) -TRANS_FEAT(BFMMLA, aa64_bf16, do_dot_vector, a, gen_helper_gvec_bfmmla) +TRANS_FEAT(BFDOT_v, aa64_bf16, do_dot_vector_env, a, gen_helper_gvec_bfdot) +TRANS_FEAT(BFMMLA, aa64_bf16, do_dot_vector_env, a, gen_helper_gvec_bfmmla) TRANS_FEAT(SMMLA, aa64_i8mm, do_dot_vector, a, gen_helper_gvec_smmla_b) TRANS_FEAT(UMMLA, aa64_i8mm, do_dot_vector, a, gen_helper_gvec_ummla_b) TRANS_FEAT(USMMLA, aa64_i8mm, do_dot_vector, a, gen_helper_gvec_usmmla_b) @@ -6385,13 +6410,22 @@ static bool do_dot_vector_idx(DisasContext *s, arg_qrrx_e *a, return true; } +static bool do_dot_vector_idx_env(DisasContext *s, arg_qrrx_e *a, + gen_helper_gvec_4_ptr *fn) +{ + if (fp_access_check(s)) { + gen_gvec_op4_env(s, a->q, a->rd, a->rn, a->rm, a->rd, a->idx, fn); + } + return true; +} + TRANS_FEAT(SDOT_vi, aa64_dp, do_dot_vector_idx, a, gen_helper_gvec_sdot_idx_b) TRANS_FEAT(UDOT_vi, aa64_dp, do_dot_vector_idx, a, gen_helper_gvec_udot_idx_b) TRANS_FEAT(SUDOT_vi, aa64_i8mm, do_dot_vector_idx, a, gen_helper_gvec_sudot_idx_b) TRANS_FEAT(USDOT_vi, aa64_i8mm, do_dot_vector_idx, a, gen_helper_gvec_usdot_idx_b) -TRANS_FEAT(BFDOT_vi, aa64_bf16, do_dot_vector_idx, a, +TRANS_FEAT(BFDOT_vi, aa64_bf16, do_dot_vector_idx_env, a, gen_helper_gvec_bfdot_idx) static bool trans_BFMLAL_vi(DisasContext *s, arg_qrrx_e *a) diff --git a/target/arm/tcg/translate-neon.c b/target/arm/tcg/translate-neon.c index 915c9e56db..13cd31aad4 100644 --- a/target/arm/tcg/translate-neon.c +++ b/target/arm/tcg/translate-neon.c @@ -148,6 +148,37 @@ static bool do_neon_ddda(DisasContext *s, int q, int vd, int vn, int vm, return true; } +static bool do_neon_ddda_env(DisasContext *s, int q, int vd, int vn, int vm, + int data, gen_helper_gvec_4_ptr *fn_gvec) +{ + /* UNDEF accesses to D16-D31 if they don't exist. */ + if (((vd | vn | vm) & 0x10) && !dc_isar_feature(aa32_simd_r32, s)) { + return false; + } + + /* + * UNDEF accesses to odd registers for each bit of Q. + * Q will be 0b111 for all Q-reg instructions, otherwise + * when we have mixed Q- and D-reg inputs. + */ + if (((vd & 1) * 4 | (vn & 1) * 2 | (vm & 1)) & q) { + return false; + } + + if (!vfp_access_check(s)) { + return true; + } + + int opr_sz = q ? 16 : 8; + tcg_gen_gvec_4_ptr(vfp_reg_offset(1, vd), + vfp_reg_offset(1, vn), + vfp_reg_offset(1, vm), + vfp_reg_offset(1, vd), + tcg_env, + opr_sz, opr_sz, data, fn_gvec); + return true; +} + static bool do_neon_ddda_fpst(DisasContext *s, int q, int vd, int vn, int vm, int data, ARMFPStatusFlavour fp_flavour, gen_helper_gvec_4_ptr *fn_gvec_ptr) @@ -266,8 +297,8 @@ static bool trans_VDOT_b16(DisasContext *s, arg_VDOT_b16 *a) if (!dc_isar_feature(aa32_bf16, s)) { return false; } - return do_neon_ddda(s, a->q * 7, a->vd, a->vn, a->vm, 0, - gen_helper_gvec_bfdot); + return do_neon_ddda_env(s, a->q * 7, a->vd, a->vn, a->vm, 0, + gen_helper_gvec_bfdot); } static bool trans_VFML(DisasContext *s, arg_VFML *a) @@ -360,8 +391,8 @@ static bool trans_VDOT_b16_scal(DisasContext *s, arg_VDOT_b16_scal *a) if (!dc_isar_feature(aa32_bf16, s)) { return false; } - return do_neon_ddda(s, a->q * 6, a->vd, a->vn, a->vm, a->index, - gen_helper_gvec_bfdot_idx); + return do_neon_ddda_env(s, a->q * 6, a->vd, a->vn, a->vm, a->index, + gen_helper_gvec_bfdot_idx); } static bool trans_VFML_scalar(DisasContext *s, arg_VFML_scalar *a) @@ -3699,8 +3730,8 @@ static bool trans_VMMLA_b16(DisasContext *s, arg_VMMLA_b16 *a) if (!dc_isar_feature(aa32_bf16, s)) { return false; } - return do_neon_ddda(s, 7, a->vd, a->vn, a->vm, 0, - gen_helper_gvec_bfmmla); + return do_neon_ddda_env(s, 7, a->vd, a->vn, a->vm, 0, + gen_helper_gvec_bfmmla); } static bool trans_VFMA_b16(DisasContext *s, arg_VFMA_b16 *a) diff --git a/target/arm/tcg/translate-sme.c b/target/arm/tcg/translate-sme.c index ae42ddef7b..01ece57016 100644 --- a/target/arm/tcg/translate-sme.c +++ b/target/arm/tcg/translate-sme.c @@ -362,8 +362,7 @@ TRANS_FEAT(FMOPA_s, aa64_sme, do_outprod_fpst, a, TRANS_FEAT(FMOPA_d, aa64_sme_f64f64, do_outprod_fpst, a, MO_64, FPST_FPCR, gen_helper_sme_fmopa_d) -/* TODO: FEAT_EBF16 */ -TRANS_FEAT(BFMOPA, aa64_sme, do_outprod, a, MO_32, gen_helper_sme_bfmopa) +TRANS_FEAT(BFMOPA, aa64_sme, do_outprod_env, a, MO_32, gen_helper_sme_bfmopa) TRANS_FEAT(SMOPA_s, aa64_sme, do_outprod, a, MO_32, gen_helper_sme_smopa_s) TRANS_FEAT(UMOPA_s, aa64_sme, do_outprod, a, MO_32, gen_helper_sme_umopa_s) diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c index a72c262096..9e2536dfe9 100644 --- a/target/arm/tcg/translate-sve.c +++ b/target/arm/tcg/translate-sve.c @@ -252,6 +252,25 @@ static bool gen_gvec_fpst_zzzz(DisasContext *s, gen_helper_gvec_4_ptr *fn, return ret; } +static bool gen_gvec_env_zzzz(DisasContext *s, gen_helper_gvec_4_ptr *fn, + int rd, int rn, int rm, int ra, + int data) +{ + return gen_gvec_ptr_zzzz(s, fn, rd, rn, rm, ra, data, tcg_env); +} + +static bool gen_gvec_env_arg_zzzz(DisasContext *s, gen_helper_gvec_4_ptr *fn, + arg_rrrr_esz *a, int data) +{ + return gen_gvec_env_zzzz(s, fn, a->rd, a->rn, a->rm, a->ra, data); +} + +static bool gen_gvec_env_arg_zzxz(DisasContext *s, gen_helper_gvec_4_ptr *fn, + arg_rrxr_esz *a) +{ + return gen_gvec_env_zzzz(s, fn, a->rd, a->rn, a->rm, a->ra, a->index); +} + /* Invoke an out-of-line helper on 4 Zregs, 1 Preg, plus fpst. */ static bool gen_gvec_fpst_zzzzp(DisasContext *s, gen_helper_gvec_5_ptr *fn, int rd, int rn, int rm, int ra, int pg, @@ -7113,12 +7132,12 @@ TRANS_FEAT_NONSTREAMING(USMMLA, aa64_sve_i8mm, gen_gvec_ool_arg_zzzz, TRANS_FEAT_NONSTREAMING(UMMLA, aa64_sve_i8mm, gen_gvec_ool_arg_zzzz, gen_helper_gvec_ummla_b, a, 0) -TRANS_FEAT(BFDOT_zzzz, aa64_sve_bf16, gen_gvec_ool_arg_zzzz, +TRANS_FEAT(BFDOT_zzzz, aa64_sve_bf16, gen_gvec_env_arg_zzzz, gen_helper_gvec_bfdot, a, 0) -TRANS_FEAT(BFDOT_zzxz, aa64_sve_bf16, gen_gvec_ool_arg_zzxz, +TRANS_FEAT(BFDOT_zzxz, aa64_sve_bf16, gen_gvec_env_arg_zzxz, gen_helper_gvec_bfdot_idx, a) -TRANS_FEAT_NONSTREAMING(BFMMLA, aa64_sve_bf16, gen_gvec_ool_arg_zzzz, +TRANS_FEAT_NONSTREAMING(BFMMLA, aa64_sve_bf16, gen_gvec_env_arg_zzzz, gen_helper_gvec_bfmmla, a, 0) static bool do_BFMLAL_zzzw(DisasContext *s, arg_rrrr_esz *a, bool sel) diff --git a/target/arm/tcg/translate-vfp.c b/target/arm/tcg/translate-vfp.c index cd5b848357..b6fa28a7bf 100644 --- a/target/arm/tcg/translate-vfp.c +++ b/target/arm/tcg/translate-vfp.c @@ -2190,8 +2190,8 @@ static bool do_vfm_sp(DisasContext *s, arg_VFMA_sp *a, bool neg_n, bool neg_d) static bool do_vfm_dp(DisasContext *s, arg_VFMA_dp *a, bool neg_n, bool neg_d) { /* - * VFNMA : fd = muladd(-fd, fn, fm) - * VFNMS : fd = muladd(-fd, -fn, fm) + * VFNMA : fd = muladd(-fd, -fn, fm) + * VFNMS : fd = muladd(-fd, fn, fm) * VFMA : fd = muladd( fd, fn, fm) * VFMS : fd = muladd( fd, -fn, fm) * @@ -2262,8 +2262,8 @@ static bool do_vfm_dp(DisasContext *s, arg_VFMA_dp *a, bool neg_n, bool neg_d) #define MAKE_VFM_TRANS_FNS(PREC) \ MAKE_ONE_VFM_TRANS_FN(VFMA, PREC, false, false) \ MAKE_ONE_VFM_TRANS_FN(VFMS, PREC, true, false) \ - MAKE_ONE_VFM_TRANS_FN(VFNMA, PREC, false, true) \ - MAKE_ONE_VFM_TRANS_FN(VFNMS, PREC, true, true) + MAKE_ONE_VFM_TRANS_FN(VFNMS, PREC, false, true) \ + MAKE_ONE_VFM_TRANS_FN(VFNMA, PREC, true, true) MAKE_VFM_TRANS_FNS(hp) MAKE_VFM_TRANS_FNS(sp) diff --git a/target/arm/tcg/vec_helper.c b/target/arm/tcg/vec_helper.c index 98604d170f..22ddb96881 100644 --- a/target/arm/tcg/vec_helper.c +++ b/target/arm/tcg/vec_helper.c @@ -2790,44 +2790,115 @@ DO_MMLA_B(gvec_usmmla_b, do_usmmla_b) * BFloat16 Dot Product */ -float32 bfdotadd(float32 sum, uint32_t e1, uint32_t e2) +bool is_ebf(CPUARMState *env, float_status *statusp, float_status *oddstatusp) { - /* FPCR is ignored for BFDOT and BFMMLA. */ - float_status bf_status = { + /* + * For BFDOT, BFMMLA, etc, the behaviour depends on FPCR.EBF. + * For EBF = 0, we ignore the FPCR bits which determine rounding + * mode and denormal-flushing, and we do unfused multiplies and + * additions with intermediate rounding of all products and sums. + * For EBF = 1, we honour FPCR rounding mode and denormal-flushing bits, + * and we perform a fused two-way sum-of-products without intermediate + * rounding of the products. + * In either case, we don't set fp exception flags. + * + * EBF is AArch64 only, so even if it's set in the FPCR it has + * no effect on AArch32 instructions. + */ + bool ebf = is_a64(env) && env->vfp.fpcr & FPCR_EBF; + *statusp = (float_status){ .tininess_before_rounding = float_tininess_before_rounding, .float_rounding_mode = float_round_to_odd_inf, .flush_to_zero = true, .flush_inputs_to_zero = true, .default_nan_mode = true, }; + + if (ebf) { + float_status *fpst = &env->vfp.fp_status; + set_flush_to_zero(get_flush_to_zero(fpst), statusp); + set_flush_inputs_to_zero(get_flush_inputs_to_zero(fpst), statusp); + set_float_rounding_mode(get_float_rounding_mode(fpst), statusp); + + /* EBF=1 needs to do a step with round-to-odd semantics */ + *oddstatusp = *statusp; + set_float_rounding_mode(float_round_to_odd, oddstatusp); + } + + return ebf; +} + +float32 bfdotadd(float32 sum, uint32_t e1, uint32_t e2, float_status *fpst) +{ float32 t1, t2; /* * Extract each BFloat16 from the element pair, and shift * them such that they become float32. */ - t1 = float32_mul(e1 << 16, e2 << 16, &bf_status); - t2 = float32_mul(e1 & 0xffff0000u, e2 & 0xffff0000u, &bf_status); - t1 = float32_add(t1, t2, &bf_status); - t1 = float32_add(sum, t1, &bf_status); + t1 = float32_mul(e1 << 16, e2 << 16, fpst); + t2 = float32_mul(e1 & 0xffff0000u, e2 & 0xffff0000u, fpst); + t1 = float32_add(t1, t2, fpst); + t1 = float32_add(sum, t1, fpst); return t1; } -void HELPER(gvec_bfdot)(void *vd, void *vn, void *vm, void *va, uint32_t desc) +float32 bfdotadd_ebf(float32 sum, uint32_t e1, uint32_t e2, + float_status *fpst, float_status *fpst_odd) +{ + /* + * Compare f16_dotadd() in sme_helper.c, but here we have + * bfloat16 inputs. In particular that means that we do not + * want the FPCR.FZ16 flush semantics, so we use the normal + * float_status for the input handling here. + */ + float64 e1r = float32_to_float64(e1 << 16, fpst); + float64 e1c = float32_to_float64(e1 & 0xffff0000u, fpst); + float64 e2r = float32_to_float64(e2 << 16, fpst); + float64 e2c = float32_to_float64(e2 & 0xffff0000u, fpst); + float64 t64; + float32 t32; + + /* + * The ARM pseudocode function FPDot performs both multiplies + * and the add with a single rounding operation. Emulate this + * by performing the first multiply in round-to-odd, then doing + * the second multiply as fused multiply-add, and rounding to + * float32 all in one step. + */ + t64 = float64_mul(e1r, e2r, fpst_odd); + t64 = float64r32_muladd(e1c, e2c, t64, 0, fpst); + + /* This conversion is exact, because we've already rounded. */ + t32 = float64_to_float32(t64, fpst); + + /* The final accumulation step is not fused. */ + return float32_add(sum, t32, fpst); +} + +void HELPER(gvec_bfdot)(void *vd, void *vn, void *vm, void *va, + CPUARMState *env, uint32_t desc) { intptr_t i, opr_sz = simd_oprsz(desc); float32 *d = vd, *a = va; uint32_t *n = vn, *m = vm; + float_status fpst, fpst_odd; - for (i = 0; i < opr_sz / 4; ++i) { - d[i] = bfdotadd(a[i], n[i], m[i]); + if (is_ebf(env, &fpst, &fpst_odd)) { + for (i = 0; i < opr_sz / 4; ++i) { + d[i] = bfdotadd_ebf(a[i], n[i], m[i], &fpst, &fpst_odd); + } + } else { + for (i = 0; i < opr_sz / 4; ++i) { + d[i] = bfdotadd(a[i], n[i], m[i], &fpst); + } } clear_tail(d, opr_sz, simd_maxsz(desc)); } void HELPER(gvec_bfdot_idx)(void *vd, void *vn, void *vm, - void *va, uint32_t desc) + void *va, CPUARMState *env, uint32_t desc) { intptr_t i, j, opr_sz = simd_oprsz(desc); intptr_t index = simd_data(desc); @@ -2835,53 +2906,100 @@ void HELPER(gvec_bfdot_idx)(void *vd, void *vn, void *vm, intptr_t eltspersegment = MIN(16 / 4, elements); float32 *d = vd, *a = va; uint32_t *n = vn, *m = vm; + float_status fpst, fpst_odd; - for (i = 0; i < elements; i += eltspersegment) { - uint32_t m_idx = m[i + H4(index)]; + if (is_ebf(env, &fpst, &fpst_odd)) { + for (i = 0; i < elements; i += eltspersegment) { + uint32_t m_idx = m[i + H4(index)]; - for (j = i; j < i + eltspersegment; j++) { - d[j] = bfdotadd(a[j], n[j], m_idx); + for (j = i; j < i + eltspersegment; j++) { + d[j] = bfdotadd_ebf(a[j], n[j], m_idx, &fpst, &fpst_odd); + } + } + } else { + for (i = 0; i < elements; i += eltspersegment) { + uint32_t m_idx = m[i + H4(index)]; + + for (j = i; j < i + eltspersegment; j++) { + d[j] = bfdotadd(a[j], n[j], m_idx, &fpst); + } } } clear_tail(d, opr_sz, simd_maxsz(desc)); } -void HELPER(gvec_bfmmla)(void *vd, void *vn, void *vm, void *va, uint32_t desc) +void HELPER(gvec_bfmmla)(void *vd, void *vn, void *vm, void *va, + CPUARMState *env, uint32_t desc) { intptr_t s, opr_sz = simd_oprsz(desc); float32 *d = vd, *a = va; uint32_t *n = vn, *m = vm; + float_status fpst, fpst_odd; - for (s = 0; s < opr_sz / 4; s += 4) { - float32 sum00, sum01, sum10, sum11; + if (is_ebf(env, &fpst, &fpst_odd)) { + for (s = 0; s < opr_sz / 4; s += 4) { + float32 sum00, sum01, sum10, sum11; - /* - * Process the entire segment at once, writing back the - * results only after we've consumed all of the inputs. - * - * Key to indices by column: - * i j i k j k - */ - sum00 = a[s + H4(0 + 0)]; - sum00 = bfdotadd(sum00, n[s + H4(0 + 0)], m[s + H4(0 + 0)]); - sum00 = bfdotadd(sum00, n[s + H4(0 + 1)], m[s + H4(0 + 1)]); - - sum01 = a[s + H4(0 + 1)]; - sum01 = bfdotadd(sum01, n[s + H4(0 + 0)], m[s + H4(2 + 0)]); - sum01 = bfdotadd(sum01, n[s + H4(0 + 1)], m[s + H4(2 + 1)]); - - sum10 = a[s + H4(2 + 0)]; - sum10 = bfdotadd(sum10, n[s + H4(2 + 0)], m[s + H4(0 + 0)]); - sum10 = bfdotadd(sum10, n[s + H4(2 + 1)], m[s + H4(0 + 1)]); - - sum11 = a[s + H4(2 + 1)]; - sum11 = bfdotadd(sum11, n[s + H4(2 + 0)], m[s + H4(2 + 0)]); - sum11 = bfdotadd(sum11, n[s + H4(2 + 1)], m[s + H4(2 + 1)]); - - d[s + H4(0 + 0)] = sum00; - d[s + H4(0 + 1)] = sum01; - d[s + H4(2 + 0)] = sum10; - d[s + H4(2 + 1)] = sum11; + /* + * Process the entire segment at once, writing back the + * results only after we've consumed all of the inputs. + * + * Key to indices by column: + * i j i k j k + */ + sum00 = a[s + H4(0 + 0)]; + sum00 = bfdotadd_ebf(sum00, n[s + H4(0 + 0)], m[s + H4(0 + 0)], &fpst, &fpst_odd); + sum00 = bfdotadd_ebf(sum00, n[s + H4(0 + 1)], m[s + H4(0 + 1)], &fpst, &fpst_odd); + + sum01 = a[s + H4(0 + 1)]; + sum01 = bfdotadd_ebf(sum01, n[s + H4(0 + 0)], m[s + H4(2 + 0)], &fpst, &fpst_odd); + sum01 = bfdotadd_ebf(sum01, n[s + H4(0 + 1)], m[s + H4(2 + 1)], &fpst, &fpst_odd); + + sum10 = a[s + H4(2 + 0)]; + sum10 = bfdotadd_ebf(sum10, n[s + H4(2 + 0)], m[s + H4(0 + 0)], &fpst, &fpst_odd); + sum10 = bfdotadd_ebf(sum10, n[s + H4(2 + 1)], m[s + H4(0 + 1)], &fpst, &fpst_odd); + + sum11 = a[s + H4(2 + 1)]; + sum11 = bfdotadd_ebf(sum11, n[s + H4(2 + 0)], m[s + H4(2 + 0)], &fpst, &fpst_odd); + sum11 = bfdotadd_ebf(sum11, n[s + H4(2 + 1)], m[s + H4(2 + 1)], &fpst, &fpst_odd); + + d[s + H4(0 + 0)] = sum00; + d[s + H4(0 + 1)] = sum01; + d[s + H4(2 + 0)] = sum10; + d[s + H4(2 + 1)] = sum11; + } + } else { + for (s = 0; s < opr_sz / 4; s += 4) { + float32 sum00, sum01, sum10, sum11; + + /* + * Process the entire segment at once, writing back the + * results only after we've consumed all of the inputs. + * + * Key to indices by column: + * i j i k j k + */ + sum00 = a[s + H4(0 + 0)]; + sum00 = bfdotadd(sum00, n[s + H4(0 + 0)], m[s + H4(0 + 0)], &fpst); + sum00 = bfdotadd(sum00, n[s + H4(0 + 1)], m[s + H4(0 + 1)], &fpst); + + sum01 = a[s + H4(0 + 1)]; + sum01 = bfdotadd(sum01, n[s + H4(0 + 0)], m[s + H4(2 + 0)], &fpst); + sum01 = bfdotadd(sum01, n[s + H4(0 + 1)], m[s + H4(2 + 1)], &fpst); + + sum10 = a[s + H4(2 + 0)]; + sum10 = bfdotadd(sum10, n[s + H4(2 + 0)], m[s + H4(0 + 0)], &fpst); + sum10 = bfdotadd(sum10, n[s + H4(2 + 1)], m[s + H4(0 + 1)], &fpst); + + sum11 = a[s + H4(2 + 1)]; + sum11 = bfdotadd(sum11, n[s + H4(2 + 0)], m[s + H4(2 + 0)], &fpst); + sum11 = bfdotadd(sum11, n[s + H4(2 + 1)], m[s + H4(2 + 1)], &fpst); + + d[s + H4(0 + 0)] = sum00; + d[s + H4(0 + 1)] = sum01; + d[s + H4(2 + 0)] = sum10; + d[s + H4(2 + 1)] = sum11; + } } clear_tail(d, opr_sz, simd_maxsz(desc)); } diff --git a/target/arm/tcg/vec_internal.h b/target/arm/tcg/vec_internal.h index 3ca1b94ccf..094f5c169c 100644 --- a/target/arm/tcg/vec_internal.h +++ b/target/arm/tcg/vec_internal.h @@ -223,13 +223,46 @@ int64_t do_sqrdmlah_d(int64_t, int64_t, int64_t, bool, bool); * bfdotadd: * @sum: addend * @e1, @e2: multiplicand vectors + * @fpst: floating-point status to use * * BFloat16 2-way dot product of @e1 & @e2, accumulating with @sum. * The @e1 and @e2 operands correspond to the 32-bit source vector * slots and contain two Bfloat16 values each. * - * Corresponds to the ARM pseudocode function BFDotAdd. + * Corresponds to the ARM pseudocode function BFDotAdd, specialized + * for the FPCR.EBF == 0 case. */ -float32 bfdotadd(float32 sum, uint32_t e1, uint32_t e2); +float32 bfdotadd(float32 sum, uint32_t e1, uint32_t e2, float_status *fpst); +/** + * bfdotadd_ebf: + * @sum: addend + * @e1, @e2: multiplicand vectors + * @fpst: floating-point status to use + * @fpst_odd: floating-point status to use for round-to-odd operations + * + * BFloat16 2-way dot product of @e1 & @e2, accumulating with @sum. + * The @e1 and @e2 operands correspond to the 32-bit source vector + * slots and contain two Bfloat16 values each. + * + * Corresponds to the ARM pseudocode function BFDotAdd, specialized + * for the FPCR.EBF == 1 case. + */ +float32 bfdotadd_ebf(float32 sum, uint32_t e1, uint32_t e2, + float_status *fpst, float_status *fpst_odd); + +/** + * is_ebf: + * @env: CPU state + * @statusp: pointer to floating point status to fill in + * @oddstatusp: pointer to floating point status to fill in for round-to-odd + * + * Determine whether a BFDotAdd operation should use FPCR.EBF = 0 + * or FPCR.EBF = 1 semantics. On return, has initialized *statusp + * and *oddstatusp to suitable float_status arguments to use with either + * bfdotadd() or bfdotadd_ebf(). + * Returns true for EBF = 1, false for EBF = 0. (The caller should use this + * to decide whether to call bfdotadd() or bfdotadd_ebf().) + */ +bool is_ebf(CPUARMState *env, float_status *statusp, float_status *oddstatusp); #endif /* TARGET_ARM_VEC_INTERNAL_H */ diff --git a/target/arm/tcg/vfp.decode b/target/arm/tcg/vfp.decode index 5405e80197..2dd87a2708 100644 --- a/target/arm/tcg/vfp.decode +++ b/target/arm/tcg/vfp.decode @@ -141,18 +141,18 @@ VDIV_dp ---- 1110 1.00 .... .... 1011 .0.0 .... @vfp_dnm_d VFMA_hp ---- 1110 1.10 .... .... 1001 .0. 0 .... @vfp_dnm_s VFMS_hp ---- 1110 1.10 .... .... 1001 .1. 0 .... @vfp_dnm_s -VFNMA_hp ---- 1110 1.01 .... .... 1001 .0. 0 .... @vfp_dnm_s -VFNMS_hp ---- 1110 1.01 .... .... 1001 .1. 0 .... @vfp_dnm_s +VFNMS_hp ---- 1110 1.01 .... .... 1001 .0. 0 .... @vfp_dnm_s +VFNMA_hp ---- 1110 1.01 .... .... 1001 .1. 0 .... @vfp_dnm_s VFMA_sp ---- 1110 1.10 .... .... 1010 .0. 0 .... @vfp_dnm_s VFMS_sp ---- 1110 1.10 .... .... 1010 .1. 0 .... @vfp_dnm_s -VFNMA_sp ---- 1110 1.01 .... .... 1010 .0. 0 .... @vfp_dnm_s -VFNMS_sp ---- 1110 1.01 .... .... 1010 .1. 0 .... @vfp_dnm_s +VFNMS_sp ---- 1110 1.01 .... .... 1010 .0. 0 .... @vfp_dnm_s +VFNMA_sp ---- 1110 1.01 .... .... 1010 .1. 0 .... @vfp_dnm_s VFMA_dp ---- 1110 1.10 .... .... 1011 .0.0 .... @vfp_dnm_d VFMS_dp ---- 1110 1.10 .... .... 1011 .1.0 .... @vfp_dnm_d -VFNMA_dp ---- 1110 1.01 .... .... 1011 .0.0 .... @vfp_dnm_d -VFNMS_dp ---- 1110 1.01 .... .... 1011 .1.0 .... @vfp_dnm_d +VFNMS_dp ---- 1110 1.01 .... .... 1011 .0.0 .... @vfp_dnm_d +VFNMA_dp ---- 1110 1.01 .... .... 1011 .1.0 .... @vfp_dnm_d VMOV_imm_hp ---- 1110 1.11 .... .... 1001 0000 .... \ vd=%vd_sp imm=%vmov_imm diff --git a/target/arm/vfp_helper.c b/target/arm/vfp_helper.c index b3698da8ca..203d37303b 100644 --- a/target/arm/vfp_helper.c +++ b/target/arm/vfp_helper.c @@ -254,6 +254,10 @@ static void vfp_set_fpcr_masked(CPUARMState *env, uint32_t val, uint32_t mask) val &= ~FPCR_FZ16; } + if (!cpu_isar_feature(aa64_ebf16, cpu)) { + val &= ~FPCR_EBF; + } + vfp_set_fpcr_to_host(env, val, mask); if (mask & (FPCR_LEN_MASK | FPCR_STRIDE_MASK)) { @@ -278,12 +282,12 @@ static void vfp_set_fpcr_masked(CPUARMState *env, uint32_t val, uint32_t mask) * We don't implement trapped exception handling, so the * trap enable bits, IDE|IXE|UFE|OFE|DZE|IOE are all RAZ/WI (not RES0!) * - * The FPCR bits we keep in vfp.fpcr are AHP, DN, FZ, RMode + * The FPCR bits we keep in vfp.fpcr are AHP, DN, FZ, RMode, EBF * and FZ16. Len, Stride and LTPSIZE we just handled. Store those bits * there, and zero any of the other FPCR bits and the RES0 and RAZ/WI * bits. */ - val &= FPCR_AHP | FPCR_DN | FPCR_FZ | FPCR_RMODE_MASK | FPCR_FZ16; + val &= FPCR_AHP | FPCR_DN | FPCR_FZ | FPCR_RMODE_MASK | FPCR_FZ16 | FPCR_EBF; env->vfp.fpcr &= ~mask; env->vfp.fpcr |= val; } diff --git a/target/ppc/machine.c b/target/ppc/machine.c index 731dd8df35..d433fd45fc 100644 --- a/target/ppc/machine.c +++ b/target/ppc/machine.c @@ -621,7 +621,7 @@ static bool tlbemb_needed(void *opaque) } static const VMStateDescription vmstate_tlbemb = { - .name = "cpu/tlb6xx", + .name = "cpu/tlbemb", .version_id = 1, .minimum_version_id = 1, .needed = tlbemb_needed, diff --git a/tests/Makefile.include b/tests/Makefile.include index 6618bfed70..010369bd3a 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -3,28 +3,30 @@ .PHONY: check-help check-help: @echo "Regression testing targets:" - @echo " $(MAKE) check Run block, qapi-schema, unit, softfloat, qtest and decodetree tests" - @echo " $(MAKE) bench Run speed tests" + @echo " $(MAKE) check Run block, qapi-schema, unit, softfloat, qtest and decodetree tests" + @echo " $(MAKE) bench Run speed tests" @echo @echo "Individual test suites:" - @echo " $(MAKE) check-qtest-TARGET Run qtest tests for given target" - @echo " $(MAKE) check-qtest Run qtest tests" - @echo " $(MAKE) check-unit Run qobject tests" - @echo " $(MAKE) check-qapi-schema Run QAPI schema tests" - @echo " $(MAKE) check-block Run block tests" + @echo " $(MAKE) check-qtest-TARGET Run qtest tests for given target" + @echo " $(MAKE) check-qtest Run qtest tests" + @echo " $(MAKE) check-functional Run python-based functional tests" + @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-block Run block tests" ifneq ($(filter $(all-check-targets), check-softfloat),) - @echo " $(MAKE) check-tcg Run TCG tests" - @echo " $(MAKE) check-softfloat Run FPU emulation tests" + @echo " $(MAKE) check-tcg Run TCG tests" + @echo " $(MAKE) check-softfloat Run FPU emulation tests" endif - @echo " $(MAKE) check-avocado Run avocado (integration) tests for currently configured targets" + @echo " $(MAKE) check-avocado Run avocado (integration) tests for currently configured targets" @echo - @echo " $(MAKE) check-report.junit.xml Generates an aggregated XML test report" - @echo " $(MAKE) check-venv Creates a Python venv for tests" - @echo " $(MAKE) check-clean Clean the tests and related data" + @echo " $(MAKE) check-report.junit.xml Generates an aggregated XML test report" + @echo " $(MAKE) check-venv Creates a Python venv for tests" + @echo " $(MAKE) check-clean Clean the tests and related data" @echo @echo "The following are useful for CI builds" - @echo " $(MAKE) check-build Build most test binaries" - @echo " $(MAKE) get-vm-images Downloads all images used by avocado tests, according to configured targets (~350 MB each, 1.5 GB max)" + @echo " $(MAKE) check-build Build most test binaries" + @echo " $(MAKE) get-vm-images Downloads all images used by avocado tests, according to configured targets (~350 MB each, 1.5 GB max)" @echo @echo @echo "The variable SPEED can be set to control the gtester speed setting." @@ -141,7 +143,7 @@ check-avocado: check-venv $(TESTS_RESULTS_DIR) get-vm-images --show=$(AVOCADO_SHOW) run --job-results-dir=$(TESTS_RESULTS_DIR) \ $(if $(AVOCADO_TAGS),, --filter-by-tags-include-empty \ --filter-by-tags-include-empty-key) \ - $(AVOCADO_CMDLINE_TAGS) \ + $(AVOCADO_CMDLINE_TAGS) --max-parallel-tasks=1 \ $(if $(GITLAB_CI),,--failfast) $(AVOCADO_TESTS), \ "AVOCADO", "tests/avocado") @@ -152,6 +154,16 @@ check-acceptance-deprecated-warning: check-acceptance: check-acceptance-deprecated-warning | check-avocado +FUNCTIONAL_TARGETS=$(patsubst %-softmmu,check-functional-%, $(filter %-softmmu,$(TARGETS))) +.PHONY: $(FUNCTIONAL_TARGETS) +$(FUNCTIONAL_TARGETS): + @$(MAKE) SPEED=thorough $(subst -functional,-func,$@) + +.PHONY: check-functional +check-functional: + @$(NINJA) precache-functional + @QEMU_TEST_NO_DOWNLOAD=1 $(MAKE) SPEED=thorough check-func check-func-quick + # Consolidated targets .PHONY: check check-clean get-vm-images diff --git a/tests/avocado/avocado_qemu/__init__.py b/tests/avocado/avocado_qemu/__init__.py index ef935614cf..0e4ecea7a0 100644 --- a/tests/avocado/avocado_qemu/__init__.py +++ b/tests/avocado/avocado_qemu/__init__.py @@ -384,23 +384,6 @@ class QemuSystemTest(QemuBaseTest): super().tearDown() -class QemuUserTest(QemuBaseTest): - """Facilitates user-mode emulation tests.""" - - def setUp(self): - self._ldpath = [] - super().setUp('qemu-') - - def add_ldpath(self, ldpath): - self._ldpath.append(os.path.abspath(ldpath)) - - def run(self, bin_path, args=[]): - qemu_args = " ".join(["-L %s" % ldpath for ldpath in self._ldpath]) - bin_args = " ".join(args) - return process.run("%s %s %s %s" % (self.qemu_bin, qemu_args, - bin_path, bin_args)) - - class LinuxSSHMixIn: """Contains utility methods for interacting with a guest via SSH.""" diff --git a/tests/avocado/avocado_qemu/linuxtest.py b/tests/avocado/avocado_qemu/linuxtest.py index e1dc838b1c..66fb9f1507 100644 --- a/tests/avocado/avocado_qemu/linuxtest.py +++ b/tests/avocado/avocado_qemu/linuxtest.py @@ -13,8 +13,8 @@ import shutil from avocado.utils import cloudinit, datadrainer, process, vmimage -from . import LinuxSSHMixIn -from . import QemuSystemTest +from avocado_qemu import LinuxSSHMixIn +from avocado_qemu import QemuSystemTest if os.path.islink(os.path.dirname(os.path.dirname(__file__))): # The link to the avocado tests dir in the source code directory diff --git a/tests/avocado/boot_linux_console.py b/tests/avocado/boot_linux_console.py index cffdd6b5a2..18c69d6acc 100644 --- a/tests/avocado/boot_linux_console.py +++ b/tests/avocado/boot_linux_console.py @@ -1304,26 +1304,6 @@ class BootLinuxConsole(LinuxKernelTest): self.vm.launch() self.wait_for_console_pattern('version UEFI Firmware v1.15') - def test_s390x_s390_ccw_virtio(self): - """ - :avocado: tags=arch:s390x - :avocado: tags=machine:s390-ccw-virtio - """ - kernel_url = ('https://archives.fedoraproject.org/pub/archive' - '/fedora-secondary/releases/29/Everything/s390x/os/images' - '/kernel.img') - kernel_hash = 'e8e8439103ef8053418ef062644ffd46a7919313' - kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) - - self.vm.set_console() - kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'console=sclp0' - self.vm.add_args('-nodefaults', - '-kernel', kernel_path, - '-append', kernel_command_line) - self.vm.launch() - console_pattern = 'Kernel command line: %s' % kernel_command_line - self.wait_for_console_pattern(console_pattern) - def test_alpha_clipper(self): """ :avocado: tags=arch:alpha diff --git a/tests/avocado/boot_xen.py b/tests/avocado/boot_xen.py index f29bc58b9e..490a127a3e 100644 --- a/tests/avocado/boot_xen.py +++ b/tests/avocado/boot_xen.py @@ -30,23 +30,22 @@ class BootXen(LinuxKernelTest): timeout = 90 XEN_COMMON_COMMAND_LINE = 'dom0_mem=128M loglvl=all guest_loglvl=all' - def fetch_guest_kernel(self): + def setUp(self): + super(BootXen, self).setUp() + # Using my own built kernel - which works kernel_url = ('https://fileserver.linaro.org/' 's/JSsewXGZ6mqxPr5/download?path=%2F&files=' 'linux-5.9.9-arm64-ajb') kernel_sha1 = '4f92bc4b9f88d5ab792fa7a43a68555d344e1b83' - kernel_path = self.fetch_asset(kernel_url, - asset_hash=kernel_sha1) - - return kernel_path + self.kernel_path = self.fetch_asset(kernel_url, + asset_hash=kernel_sha1) def launch_xen(self, xen_path): """ Launch Xen with a dom0 guest kernel """ self.log.info("launch with xen_path: %s", xen_path) - kernel_path = self.fetch_guest_kernel() self.vm.set_console() @@ -56,7 +55,7 @@ class BootXen(LinuxKernelTest): '-append', self.XEN_COMMON_COMMAND_LINE, '-device', 'guest-loader,addr=0x47000000,kernel=%s,bootargs=console=hvc0' - % (kernel_path)) + % (self.kernel_path)) self.vm.launch() diff --git a/tests/avocado/load_bflt.py b/tests/avocado/load_bflt.py deleted file mode 100644 index bb50cec1ee..0000000000 --- a/tests/avocado/load_bflt.py +++ /dev/null @@ -1,54 +0,0 @@ -# Test the bFLT loader format -# -# Copyright (C) 2019 Philippe Mathieu-Daudé <f4bug@amsat.org> -# -# SPDX-License-Identifier: GPL-2.0-or-later - -import os -import bz2 -import subprocess - -from avocado import skipUnless -from avocado_qemu import QemuUserTest -from avocado_qemu import has_cmd - - -class LoadBFLT(QemuUserTest): - - def extract_cpio(self, cpio_path): - """ - Extracts a cpio archive into the test workdir - - :param cpio_path: path to the cpio archive - """ - cwd = os.getcwd() - os.chdir(self.workdir) - with bz2.open(cpio_path, 'rb') as archive_cpio: - subprocess.run(['cpio', '-i'], input=archive_cpio.read(), - stderr=subprocess.DEVNULL) - os.chdir(cwd) - - @skipUnless(*has_cmd('cpio')) - @skipUnless(os.getenv('AVOCADO_ALLOW_UNTRUSTED_CODE'), 'untrusted code') - def test_stm32(self): - """ - :avocado: tags=arch:arm - :avocado: tags=linux_user - :avocado: tags=quick - """ - # See https://elinux.org/STM32#User_Space - rootfs_url = ('https://elinux.org/images/5/51/' - 'Stm32_mini_rootfs.cpio.bz2') - rootfs_hash = '9f065e6ba40cce7411ba757f924f30fcc57951e6' - rootfs_path_bz2 = self.fetch_asset(rootfs_url, asset_hash=rootfs_hash) - busybox_path = os.path.join(self.workdir, "/bin/busybox") - - self.extract_cpio(rootfs_path_bz2) - - res = self.run(busybox_path) - ver = 'BusyBox v1.24.0.git (2015-02-03 22:17:13 CET) multi-call binary.' - self.assertIn(ver, res.stdout_text) - - res = self.run(busybox_path, ['uname', '-a']) - unm = 'armv7l GNU/Linux' - self.assertIn(unm, res.stdout_text) diff --git a/tests/avocado/machine_arm_n8x0.py b/tests/avocado/machine_arm_n8x0.py index 12e9a6803b..12e9a6803b 100644..100755 --- a/tests/avocado/machine_arm_n8x0.py +++ b/tests/avocado/machine_arm_n8x0.py diff --git a/tests/avocado/machine_microblaze.py b/tests/avocado/machine_microblaze.py deleted file mode 100644 index 807709cd11..0000000000 --- a/tests/avocado/machine_microblaze.py +++ /dev/null @@ -1,61 +0,0 @@ -# Functional test that boots a microblaze Linux kernel and checks the console -# -# Copyright (c) 2018, 2021 Red Hat, Inc. -# -# This work is licensed under the terms of the GNU GPL, version 2 or -# later. See the COPYING file in the top-level directory. - -import time -from avocado_qemu import exec_command, exec_command_and_wait_for_pattern -from avocado_qemu import QemuSystemTest -from avocado_qemu import wait_for_console_pattern -from avocado.utils import archive - -class MicroblazeMachine(QemuSystemTest): - - timeout = 90 - - def test_microblaze_s3adsp1800(self): - """ - :avocado: tags=arch:microblaze - :avocado: tags=machine:petalogix-s3adsp1800 - """ - - tar_url = ('https://qemu-advcal.gitlab.io' - '/qac-best-of-multiarch/download/day17.tar.xz') - tar_hash = '08bf3e3bfb6b6c7ce1e54ab65d54e189f2caf13f' - file_path = self.fetch_asset(tar_url, asset_hash=tar_hash) - archive.extract(file_path, self.workdir) - self.vm.set_console() - self.vm.add_args('-kernel', self.workdir + '/day17/ballerina.bin') - self.vm.launch() - wait_for_console_pattern(self, 'This architecture does not have ' - 'kernel memory protection') - # Note: - # The kernel sometimes gets stuck after the "This architecture ..." - # message, that's why we don't test for a later string here. This - # needs some investigation by a microblaze wizard one day... - - def test_microblazeel_s3adsp1800(self): - """ - :avocado: tags=arch:microblazeel - :avocado: tags=machine:petalogix-s3adsp1800 - """ - - self.require_netdev('user') - tar_url = ('http://www.qemu-advent-calendar.org/2023/download/' - 'day13.tar.gz') - tar_hash = '6623d5fff5f84cfa8f34e286f32eff6a26546f44' - file_path = self.fetch_asset(tar_url, asset_hash=tar_hash) - archive.extract(file_path, self.workdir) - self.vm.set_console() - self.vm.add_args('-kernel', self.workdir + '/day13/xmaton.bin') - self.vm.add_args('-nic', 'user,tftp=' + self.workdir + '/day13/') - self.vm.launch() - wait_for_console_pattern(self, 'QEMU Advent Calendar 2023') - time.sleep(0.1) - exec_command(self, 'root') - time.sleep(0.1) - exec_command_and_wait_for_pattern(self, - 'tftp -g -r xmaton.png 10.0.2.2 ; md5sum xmaton.png', - '821cd3cab8efd16ad6ee5acc3642a8ea') diff --git a/tests/avocado/machine_mips_loongson3v.py b/tests/avocado/machine_mips_loongson3v.py deleted file mode 100644 index 5194cf18c9..0000000000 --- a/tests/avocado/machine_mips_loongson3v.py +++ /dev/null @@ -1,39 +0,0 @@ -# Functional tests for the Generic Loongson-3 Platform. -# -# Copyright (c) 2021 Jiaxun Yang <jiaxun.yang@flygoat.com> -# -# This work is licensed under the terms of the GNU GPL, version 2 or later. -# See the COPYING file in the top-level directory. -# -# SPDX-License-Identifier: GPL-2.0-or-later - -import os -import time - -from avocado import skipUnless -from avocado_qemu import QemuSystemTest -from avocado_qemu import wait_for_console_pattern - -class MipsLoongson3v(QemuSystemTest): - timeout = 60 - - @skipUnless(os.getenv('AVOCADO_ALLOW_UNTRUSTED_CODE'), 'untrusted code') - def test_pmon_serial_console(self): - """ - :avocado: tags=arch:mips64el - :avocado: tags=endian:little - :avocado: tags=machine:loongson3-virt - :avocado: tags=cpu:Loongson-3A1000 - :avocado: tags=device:liointc - :avocado: tags=device:goldfish_rtc - """ - - pmon_hash = '7c8b45dd81ccfc55ff28f5aa267a41c3' - pmon_path = self.fetch_asset('https://github.com/loongson-community/pmon/' - 'releases/download/20210112/pmon-3avirt.bin', - asset_hash=pmon_hash, algorithm='md5') - - self.vm.set_console() - self.vm.add_args('-bios', pmon_path) - self.vm.launch() - wait_for_console_pattern(self, 'CPU GODSON3 BogoMIPS:') diff --git a/tests/avocado/machine_sparc64_sun4u.py b/tests/avocado/machine_sparc64_sun4u.py deleted file mode 100644 index d333c0ae91..0000000000 --- a/tests/avocado/machine_sparc64_sun4u.py +++ /dev/null @@ -1,36 +0,0 @@ -# Functional test that boots a Linux kernel and checks the console -# -# Copyright (c) 2020 Red Hat, Inc. -# -# Author: -# Thomas Huth <thuth@redhat.com> -# -# This work is licensed under the terms of the GNU GPL, version 2 or -# later. See the COPYING file in the top-level directory. - -import os - -from avocado_qemu import wait_for_console_pattern -from avocado.utils import archive -from boot_linux_console import LinuxKernelTest - -class Sun4uMachine(LinuxKernelTest): - """Boots the Linux kernel and checks that the console is operational""" - - timeout = 90 - - def test_sparc64_sun4u(self): - """ - :avocado: tags=arch:sparc64 - :avocado: tags=machine:sun4u - """ - tar_url = ('https://qemu-advcal.gitlab.io' - '/qac-best-of-multiarch/download/day23.tar.xz') - tar_hash = '142db83cd974ffadc4f75c8a5cad5bcc5722c240' - file_path = self.fetch_asset(tar_url, asset_hash=tar_hash) - archive.extract(file_path, self.workdir) - self.vm.set_console() - self.vm.add_args('-kernel', self.workdir + '/day23/vmlinux', - '-append', self.KERNEL_COMMON_COMMAND_LINE) - self.vm.launch() - wait_for_console_pattern(self, 'Starting logging: OK') diff --git a/tests/avocado/ppc_amiga.py b/tests/avocado/ppc_amiga.py deleted file mode 100644 index b6f866f91d..0000000000 --- a/tests/avocado/ppc_amiga.py +++ /dev/null @@ -1,38 +0,0 @@ -# Test AmigaNG boards -# -# Copyright (c) 2023 BALATON Zoltan -# -# This work is licensed under the terms of the GNU GPL, version 2 or -# later. See the COPYING file in the top-level directory. - -from avocado.utils import archive -from avocado.utils import process -from avocado_qemu import QemuSystemTest -from avocado_qemu import wait_for_console_pattern - -class AmigaOneMachine(QemuSystemTest): - - timeout = 90 - - def test_ppc_amigaone(self): - """ - :avocado: tags=arch:ppc - :avocado: tags=machine:amigaone - :avocado: tags=device:articia - :avocado: tags=accel:tcg - """ - self.require_accelerator("tcg") - tar_name = 'A1Firmware_Floppy_05-Mar-2005.zip' - tar_url = ('https://www.hyperion-entertainment.com/index.php/' - 'downloads?view=download&format=raw&file=25') - tar_hash = 'c52e59bc73e31d8bcc3cc2106778f7ac84f6c755' - zip_file = self.fetch_asset(tar_name, locations=tar_url, - asset_hash=tar_hash) - archive.extract(zip_file, self.workdir) - cmd = f"tail -c 524288 {self.workdir}/floppy_edition/updater.image >{self.workdir}/u-boot-amigaone.bin" - process.run(cmd, shell=True) - - self.vm.set_console() - self.vm.add_args('-bios', self.workdir + '/u-boot-amigaone.bin') - self.vm.launch() - wait_for_console_pattern(self, 'FLASH:') diff --git a/tests/avocado/tesseract_utils.py b/tests/avocado/tesseract_utils.py deleted file mode 100644 index 476f528147..0000000000 --- a/tests/avocado/tesseract_utils.py +++ /dev/null @@ -1,46 +0,0 @@ -# ... -# -# Copyright (c) 2019 Philippe Mathieu-Daudé <f4bug@amsat.org> -# -# This work is licensed under the terms of the GNU GPL, version 2 or -# later. See the COPYING file in the top-level directory. - -import re -import logging - -from avocado.utils import process -from avocado.utils.path import find_command, CmdNotFoundError - -def tesseract_available(expected_version): - try: - find_command('tesseract') - except CmdNotFoundError: - return False - res = process.run('tesseract --version') - try: - version = res.stdout_text.split()[1] - except IndexError: - version = res.stderr_text.split()[1] - return int(version.split('.')[0]) >= expected_version - - match = re.match(r'tesseract\s(\d)', res) - if match is None: - return False - # now this is guaranteed to be a digit - return int(match.groups()[0]) >= expected_version - - -def tesseract_ocr(image_path, tesseract_args='', tesseract_version=3): - console_logger = logging.getLogger('tesseract') - console_logger.debug(image_path) - if tesseract_version == 4: - tesseract_args += ' --oem 1' - proc = process.run("tesseract {} {} stdout".format(tesseract_args, - image_path)) - lines = [] - for line in proc.stdout_text.split('\n'): - sline = line.strip() - if len(sline): - console_logger.debug(sline) - lines += [sline] - return lines diff --git a/tests/avocado/acpi-bits/bits-config/bits-cfg.txt b/tests/functional/acpi-bits/bits-config/bits-cfg.txt index 8010804453..8010804453 100644 --- a/tests/avocado/acpi-bits/bits-config/bits-cfg.txt +++ b/tests/functional/acpi-bits/bits-config/bits-cfg.txt diff --git a/tests/avocado/acpi-bits/bits-tests/smbios.py2 b/tests/functional/acpi-bits/bits-tests/smbios.py2 index 5868a7137a..5868a7137a 100644 --- a/tests/avocado/acpi-bits/bits-tests/smbios.py2 +++ b/tests/functional/acpi-bits/bits-tests/smbios.py2 diff --git a/tests/avocado/acpi-bits/bits-tests/smilatency.py2 b/tests/functional/acpi-bits/bits-tests/smilatency.py2 index 405af67e19..405af67e19 100644 --- a/tests/avocado/acpi-bits/bits-tests/smilatency.py2 +++ b/tests/functional/acpi-bits/bits-tests/smilatency.py2 diff --git a/tests/avocado/acpi-bits/bits-tests/testacpi.py2 b/tests/functional/acpi-bits/bits-tests/testacpi.py2 index 7bf9075c1b..7bf9075c1b 100644 --- a/tests/avocado/acpi-bits/bits-tests/testacpi.py2 +++ b/tests/functional/acpi-bits/bits-tests/testacpi.py2 diff --git a/tests/avocado/acpi-bits/bits-tests/testcpuid.py2 b/tests/functional/acpi-bits/bits-tests/testcpuid.py2 index 7adefbe355..7adefbe355 100644 --- a/tests/avocado/acpi-bits/bits-tests/testcpuid.py2 +++ b/tests/functional/acpi-bits/bits-tests/testcpuid.py2 diff --git a/tests/functional/meson.build b/tests/functional/meson.build new file mode 100644 index 0000000000..cda89c4b0c --- /dev/null +++ b/tests/functional/meson.build @@ -0,0 +1,205 @@ +# QEMU functional tests: +# Tests that are put in the 'quick' category are run by default during +# 'make check'. Everything that should not be run during 'make check' +# (e.g. tests that fetch assets from the internet) should be put into +# the 'thorough' category instead. + +# Most tests run too slow with TCI enabled, so skip the functional tests there +if get_option('tcg_interpreter') + subdir_done() +endif + +# Timeouts for individual tests that can be slow e.g. with debugging enabled +test_timeouts = { + 'aarch64_sbsaref' : 600, + 'aarch64_virt' : 360, + 'acpi_bits' : 240, + 'netdev_ethtool' : 180, + 'ppc_40p' : 240, + 'ppc64_hv' : 1000, + 'ppc64_powernv' : 120, + 'ppc64_pseries' : 120, + 's390x_ccw_virtio' : 180, +} + +tests_generic_system = [ + 'empty_cpu_model', + 'info_usernet', + 'version', +] + +tests_generic_linuxuser = [ +] + +tests_generic_bsduser = [ +] + +tests_aarch64_system_thorough = [ + 'aarch64_sbsaref', + 'aarch64_virt', +] + +tests_arm_system_thorough = [ + 'arm_canona1100', + 'arm_integratorcp', +] + +tests_arm_linuxuser_thorough = [ + 'arm_bflt', +] + +tests_avr_system_thorough = [ + 'avr_mega2560', +] + +tests_loongarch64_system_thorough = [ + 'loongarch64_virt', +] + +tests_m68k_system_thorough = [ + 'm68k_nextcube' +] + +tests_microblaze_system_thorough = [ + 'microblaze_s3adsp1800' +] + +tests_microblazeel_system_thorough = [ + 'microblazeel_s3adsp1800' +] + +tests_mips64el_system_quick = [ + 'mips64el_fuloong2e', +] + +tests_mips64el_system_thorough = [ + 'mips64el_loongson3v', +] + +tests_ppc_system_quick = [ + 'ppc_74xx', +] + +tests_ppc_system_thorough = [ + 'ppc_405', + 'ppc_40p', + 'ppc_amiga', + 'ppc_bamboo', + 'ppc_mpc8544ds', + 'ppc_virtex_ml507', +] + +tests_ppc64_system_thorough = [ + 'ppc64_hv', + 'ppc64_powernv', + 'ppc64_pseries', +] + +tests_rx_system_thorough = [ + 'rx_gdbsim', +] + +tests_s390x_system_thorough = [ + 's390x_ccw_virtio', + 's390x_topology', +] + +tests_sparc64_system_thorough = [ + 'sparc64_sun4u', +] + +tests_x86_64_system_quick = [ + 'cpu_queries', + 'mem_addr_space', + 'pc_cpu_hotplug_props', + 'virtio_version', + 'x86_cpu_model_versions', +] + +tests_x86_64_system_thorough = [ + 'acpi_bits', + 'linux_initrd', + 'netdev_ethtool', + 'virtio_gpu', +] + +precache_all = [] +foreach speed : ['quick', 'thorough'] + foreach dir : target_dirs + + target_base = dir.split('-')[0] + + if dir.endswith('-softmmu') + sysmode = 'system' + test_emulator = emulators['qemu-system-' + target_base] + elif dir.endswith('-linux-user') + sysmode = 'linuxuser' + test_emulator = emulators['qemu-' + target_base] + elif dir.endswith('-bsd-user') + sysmode = 'bsduser' + test_emulator = emulators['qemu-' + target_base] + else + continue + endif + + if speed == 'quick' + suites = ['func-quick', 'func-' + target_base] + target_tests = get_variable('tests_' + target_base + '_' + sysmode + '_quick', []) \ + + get_variable('tests_generic_' + sysmode) + else + suites = ['func-' + speed, 'func-' + target_base + '-' + speed, speed] + target_tests = get_variable('tests_' + target_base + '_' + sysmode + '_' + speed, []) + endif + + test_deps = roms + test_env = environment() + if have_tools + test_env.set('QEMU_TEST_QEMU_IMG', meson.global_build_root() / 'qemu-img') + test_deps += [qemu_img] + endif + test_env.set('QEMU_TEST_QEMU_BINARY', test_emulator.full_path()) + test_env.set('QEMU_BUILD_ROOT', meson.project_build_root()) + test_env.set('PYTHONPATH', meson.project_source_root() / 'python:' + + meson.current_source_dir()) + + foreach test : target_tests + testname = '@0@-@1@'.format(target_base, test) + testfile = 'test_' + test + '.py' + testpath = meson.current_source_dir() / testfile + teststamp = testname + '.tstamp' + test_precache_env = environment() + test_precache_env.set('QEMU_TEST_PRECACHE', meson.current_build_dir() / teststamp) + test_precache_env.set('PYTHONPATH', meson.project_source_root() / 'python:' + + meson.current_source_dir()) + precache = custom_target('func-precache-' + testname, + output: teststamp, + command: [python, testpath], + depend_files: files(testpath), + build_by_default: false, + env: test_precache_env) + precache_all += precache + + # Ideally we would add 'precache' to 'depends' here, such that + # 'build_by_default: false' lets the pre-caching automatically + # run immediately before the test runs. In practice this is + # broken in meson, with it running the pre-caching in the normal + # compile phase https://github.com/mesonbuild/meson/issues/2518 + # If the above bug ever gets fixed, when QEMU changes the min + # meson version, add the 'depends' and remove the custom + # 'run_target' logic below & in Makefile.include + test('func-' + testname, + python, + depends: [test_deps, test_emulator, emulator_modules], + env: test_env, + args: [testpath], + protocol: 'tap', + timeout: test_timeouts.get(test, 60), + priority: test_timeouts.get(test, 60), + suite: suites) + endforeach + endforeach +endforeach + +run_target('precache-functional', + depends: precache_all, + command: ['true']) diff --git a/tests/functional/qemu_test/__init__.py b/tests/functional/qemu_test/__init__.py new file mode 100644 index 0000000000..f33282efe8 --- /dev/null +++ b/tests/functional/qemu_test/__init__.py @@ -0,0 +1,14 @@ +# Test class and utilities for functional tests +# +# Copyright 2024 Red Hat, Inc. +# +# This work is licensed under the terms of the GNU GPL, version 2 or +# later. See the COPYING file in the top-level directory. + + +from .asset import Asset +from .config import BUILD_DIR +from .cmd import has_cmd, has_cmds, run_cmd, is_readable_executable_file, \ + interrupt_interactive_console_until_pattern, wait_for_console_pattern, \ + exec_command, exec_command_and_wait_for_pattern, get_qemu_img +from .testcase import QemuBaseTest, QemuUserTest, QemuSystemTest diff --git a/tests/functional/qemu_test/asset.py b/tests/functional/qemu_test/asset.py new file mode 100644 index 0000000000..d3be2aff82 --- /dev/null +++ b/tests/functional/qemu_test/asset.py @@ -0,0 +1,171 @@ +# Test utilities for fetching & caching assets +# +# Copyright 2024 Red Hat, Inc. +# +# This work is licensed under the terms of the GNU GPL, version 2 or +# later. See the COPYING file in the top-level directory. + +import hashlib +import logging +import os +import subprocess +import sys +import unittest +import urllib.request +from time import sleep +from pathlib import Path +from shutil import copyfileobj + + +# Instances of this class must be declared as class level variables +# starting with a name "ASSET_". This enables the pre-caching logic +# to easily find all referenced assets and download them prior to +# execution of the tests. +class Asset: + + def __init__(self, url, hashsum): + self.url = url + self.hash = hashsum + cache_dir_env = os.getenv('QEMU_TEST_CACHE_DIR') + if cache_dir_env: + self.cache_dir = Path(cache_dir_env, "download") + else: + self.cache_dir = Path(Path("~").expanduser(), + ".cache", "qemu", "download") + self.cache_file = Path(self.cache_dir, hashsum) + self.log = logging.getLogger('qemu-test') + + def __repr__(self): + return "Asset: url=%s hash=%s cache=%s" % ( + self.url, self.hash, self.cache_file) + + def _check(self, cache_file): + if self.hash is None: + return True + if len(self.hash) == 64: + sum_prog = 'sha256sum' + elif len(self.hash) == 128: + sum_prog = 'sha512sum' + else: + raise Exception("unknown hash type") + + checksum = subprocess.check_output( + [sum_prog, str(cache_file)]).split()[0] + return self.hash == checksum.decode("utf-8") + + def valid(self): + return self.cache_file.exists() and self._check(self.cache_file) + + def _wait_for_other_download(self, tmp_cache_file): + # Another thread already seems to download the asset, so wait until + # it is done, while also checking the size to see whether it is stuck + try: + current_size = tmp_cache_file.stat().st_size + new_size = current_size + except: + if os.path.exists(self.cache_file): + return True + raise + waittime = lastchange = 600 + while waittime > 0: + sleep(1) + waittime -= 1 + try: + new_size = tmp_cache_file.stat().st_size + except: + if os.path.exists(self.cache_file): + return True + raise + if new_size != current_size: + lastchange = waittime + current_size = new_size + elif lastchange - waittime > 90: + return False + + self.log.debug("Time out while waiting for %s!", tmp_cache_file) + raise + + def fetch(self): + if not self.cache_dir.exists(): + self.cache_dir.mkdir(parents=True, exist_ok=True) + + if self.valid(): + self.log.debug("Using cached asset %s for %s", + self.cache_file, self.url) + return str(self.cache_file) + + if os.environ.get("QEMU_TEST_NO_DOWNLOAD", False): + raise Exception("Asset cache is invalid and downloads disabled") + + self.log.info("Downloading %s to %s...", self.url, self.cache_file) + tmp_cache_file = self.cache_file.with_suffix(".download") + + for retries in range(3): + try: + with tmp_cache_file.open("xb") as dst: + with urllib.request.urlopen(self.url) as resp: + copyfileobj(resp, dst) + break + except FileExistsError: + self.log.debug("%s already exists, " + "waiting for other thread to finish...", + tmp_cache_file) + if self._wait_for_other_download(tmp_cache_file): + return str(self.cache_file) + self.log.debug("%s seems to be stale, " + "deleting and retrying download...", + tmp_cache_file) + tmp_cache_file.unlink() + continue + except Exception as e: + self.log.error("Unable to download %s: %s", self.url, e) + tmp_cache_file.unlink() + raise + + try: + # Set these just for informational purposes + os.setxattr(str(tmp_cache_file), "user.qemu-asset-url", + self.url.encode('utf8')) + os.setxattr(str(tmp_cache_file), "user.qemu-asset-hash", + self.hash.encode('utf8')) + except Exception as e: + self.log.debug("Unable to set xattr on %s: %s", tmp_cache_file, e) + pass + + if not self._check(tmp_cache_file): + tmp_cache_file.unlink() + raise Exception("Hash of %s does not match %s" % + (self.url, self.hash)) + tmp_cache_file.replace(self.cache_file) + + self.log.info("Cached %s at %s" % (self.url, self.cache_file)) + return str(self.cache_file) + + def precache_test(test): + log = logging.getLogger('qemu-test') + log.setLevel(logging.DEBUG) + handler = logging.StreamHandler(sys.stdout) + handler.setLevel(logging.DEBUG) + formatter = logging.Formatter( + '%(asctime)s - %(name)s - %(levelname)s - %(message)s') + handler.setFormatter(formatter) + log.addHandler(handler) + for name, asset in vars(test.__class__).items(): + if name.startswith("ASSET_") and type(asset) == Asset: + log.info("Attempting to cache '%s'" % asset) + asset.fetch() + log.removeHandler(handler) + + def precache_suite(suite): + for test in suite: + if isinstance(test, unittest.TestSuite): + Asset.precache_suite(test) + elif isinstance(test, unittest.TestCase): + Asset.precache_test(test) + + def precache_suites(path, cacheTstamp): + loader = unittest.loader.defaultTestLoader + tests = loader.loadTestsFromNames([path], None) + + with open(cacheTstamp, "w") as fh: + Asset.precache_suite(tests) diff --git a/tests/functional/qemu_test/cmd.py b/tests/functional/qemu_test/cmd.py new file mode 100644 index 0000000000..3acd617324 --- /dev/null +++ b/tests/functional/qemu_test/cmd.py @@ -0,0 +1,193 @@ +# Test class and utilities for functional tests +# +# Copyright 2018, 2024 Red Hat, Inc. +# +# Original Author (Avocado-based tests): +# Cleber Rosa <crosa@redhat.com> +# +# Adaption for standalone version: +# Thomas Huth <thuth@redhat.com> +# +# This work is licensed under the terms of the GNU GPL, version 2 or +# later. See the COPYING file in the top-level directory. + +import logging +import os +import os.path +import subprocess + +from .config import BUILD_DIR + + +def has_cmd(name, args=None): + """ + This function is for use in a @skipUnless decorator, e.g.: + + @skipUnless(*has_cmd('sudo -n', ('sudo', '-n', 'true'))) + def test_something_that_needs_sudo(self): + ... + """ + + if args is None: + args = ('which', name) + + try: + _, stderr, exitcode = run_cmd(args) + except Exception as e: + exitcode = -1 + stderr = str(e) + + if exitcode != 0: + cmd_line = ' '.join(args) + err = f'{name} required, but "{cmd_line}" failed: {stderr.strip()}' + return (False, err) + else: + return (True, '') + +def has_cmds(*cmds): + """ + This function is for use in a @skipUnless decorator and + allows checking for the availability of multiple commands, e.g.: + + @skipUnless(*has_cmds(('cmd1', ('cmd1', '--some-parameter')), + 'cmd2', 'cmd3')) + def test_something_that_needs_cmd1_and_cmd2(self): + ... + """ + + for cmd in cmds: + if isinstance(cmd, str): + cmd = (cmd,) + + ok, errstr = has_cmd(*cmd) + if not ok: + return (False, errstr) + + return (True, '') + +def run_cmd(args): + subp = subprocess.Popen(args, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + universal_newlines=True) + stdout, stderr = subp.communicate() + ret = subp.returncode + + return (stdout, stderr, ret) + +def is_readable_executable_file(path): + return os.path.isfile(path) and os.access(path, os.R_OK | os.X_OK) + +def _console_interaction(test, success_message, failure_message, + send_string, keep_sending=False, vm=None): + assert not keep_sending or send_string + if vm is None: + vm = test.vm + console = vm.console_file + console_logger = logging.getLogger('console') + while True: + if send_string: + vm.console_socket.sendall(send_string.encode()) + if not keep_sending: + send_string = None # send only once + + # Only consume console output if waiting for something + if success_message is None and failure_message is None: + if send_string is None: + break + continue + + try: + msg = console.readline().decode().strip() + except UnicodeDecodeError: + msg = None + if not msg: + continue + console_logger.debug(msg) + if success_message is None or success_message in msg: + break + if failure_message and failure_message in msg: + console.close() + fail = 'Failure message found in console: "%s". Expected: "%s"' % \ + (failure_message, success_message) + test.fail(fail) + +def interrupt_interactive_console_until_pattern(test, success_message, + failure_message=None, + interrupt_string='\r'): + """ + Keep sending a string to interrupt a console prompt, while logging the + console output. Typical use case is to break a boot loader prompt, such: + + Press a key within 5 seconds to interrupt boot process. + 5 + 4 + 3 + 2 + 1 + Booting default image... + + :param test: a test containing a VM that will have its console + read and probed for a success or failure message + :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 interrupt_string: a string to send to the console before trying + to read a new line + """ + _console_interaction(test, success_message, failure_message, + interrupt_string, True) + +def wait_for_console_pattern(test, success_message, failure_message=None, + vm=None): + """ + Waits for messages to appear on the console, while logging the content + + :param test: a test containing a VM that will have its console + read and probed for a success or failure message + :type test: :class:`qemu_test.QemuSystemTest` + :param success_message: if this message appears, test succeeds + :param failure_message: if this message appears, test fails + """ + _console_interaction(test, success_message, failure_message, None, vm=vm) + +def exec_command(test, command): + """ + Send a command to a console (appending CRLF characters), while logging + the content. + + :param test: a test containing a VM. + :type test: :class:`qemu_test.QemuSystemTest` + :param command: the command to send + :type command: str + """ + _console_interaction(test, None, None, command + '\r') + +def exec_command_and_wait_for_pattern(test, command, + success_message, failure_message=None): + """ + Send a command to a console (appending CRLF characters), then wait + for success_message to appear on the console, while logging the. + content. Mark the test as failed if failure_message is found instead. + + :param test: a test containing a VM that will have its console + read and probed for a success or failure message + :type test: :class:`qemu_test.QemuSystemTest` + :param command: the command to send + :param success_message: if this message appears, test succeeds + :param failure_message: if this message appears, test fails + """ + _console_interaction(test, success_message, failure_message, command + '\r') + +def get_qemu_img(test): + test.log.debug('Looking for and selecting a qemu-img binary') + + # If qemu-img has been built, use it, otherwise the system wide one + # will be used. + qemu_img = os.path.join(BUILD_DIR, 'qemu-img') + if os.path.exists(qemu_img): + return qemu_img + if has_cmd('qemu-img'): + return 'qemu-img' + test.skipTest('Could not find "qemu-img", which is required to ' + 'create temporary images') diff --git a/tests/functional/qemu_test/config.py b/tests/functional/qemu_test/config.py new file mode 100644 index 0000000000..edd75b7fd0 --- /dev/null +++ b/tests/functional/qemu_test/config.py @@ -0,0 +1,36 @@ +# Test class and utilities for functional tests +# +# Copyright 2018, 2024 Red Hat, Inc. +# +# Original Author (Avocado-based tests): +# Cleber Rosa <crosa@redhat.com> +# +# Adaption for standalone version: +# Thomas Huth <thuth@redhat.com> +# +# This work is licensed under the terms of the GNU GPL, version 2 or +# later. See the COPYING file in the top-level directory. + +import os +from pathlib import Path + + +def _source_dir(): + # Determine top-level directory of the QEMU sources + return Path(__file__).parent.parent.parent.parent + +def _build_dir(): + root = os.getenv('QEMU_BUILD_ROOT') + if root is not None: + return Path(root) + # Makefile.mtest only exists in build dir, so if it is available, use CWD + if os.path.exists('Makefile.mtest'): + return Path(os.getcwd()) + + root = os.path.join(_source_dir(), 'build') + if os.path.exists(root): + return Path(root) + + raise Exception("Cannot identify build dir, set QEMU_BUILD_ROOT") + +BUILD_DIR = _build_dir() diff --git a/tests/functional/qemu_test/tesseract.py b/tests/functional/qemu_test/tesseract.py new file mode 100644 index 0000000000..c4087b7c11 --- /dev/null +++ b/tests/functional/qemu_test/tesseract.py @@ -0,0 +1,35 @@ +# ... +# +# Copyright (c) 2019 Philippe Mathieu-Daudé <f4bug@amsat.org> +# +# This work is licensed under the terms of the GNU GPL, version 2 or +# later. See the COPYING file in the top-level directory. + +import re +import logging + +from . import has_cmd, run_cmd + +def tesseract_available(expected_version): + if not has_cmd('tesseract'): + return False + (stdout, stderr, ret) = run_cmd([ 'tesseract', '--version']) + if ret: + return False + version = stdout.split()[1] + return int(version.split('.')[0]) >= expected_version + +def tesseract_ocr(image_path, tesseract_args=''): + console_logger = logging.getLogger('console') + console_logger.debug(image_path) + (stdout, stderr, ret) = run_cmd(['tesseract', image_path, + 'stdout']) + if ret: + return None + lines = [] + for line in stdout.split('\n'): + sline = line.strip() + if len(sline): + console_logger.debug(sline) + lines += [sline] + return lines diff --git a/tests/functional/qemu_test/testcase.py b/tests/functional/qemu_test/testcase.py new file mode 100644 index 0000000000..aa0146265a --- /dev/null +++ b/tests/functional/qemu_test/testcase.py @@ -0,0 +1,202 @@ +# Test class and utilities for functional tests +# +# Copyright 2018, 2024 Red Hat, Inc. +# +# Original Author (Avocado-based tests): +# Cleber Rosa <crosa@redhat.com> +# +# Adaption for standalone version: +# Thomas Huth <thuth@redhat.com> +# +# This work is licensed under the terms of the GNU GPL, version 2 or +# later. See the COPYING file in the top-level directory. + +import logging +import os +import subprocess +import pycotap +import sys +import unittest +import uuid + +from qemu.machine import QEMUMachine +from qemu.utils import kvm_available, tcg_available + +from .asset import Asset +from .cmd import run_cmd +from .config import BUILD_DIR + + +class QemuBaseTest(unittest.TestCase): + + qemu_bin = os.getenv('QEMU_TEST_QEMU_BINARY') + arch = None + + workdir = None + log = None + logdir = None + + def setUp(self, bin_prefix): + self.assertIsNotNone(self.qemu_bin, 'QEMU_TEST_QEMU_BINARY must be set') + self.arch = self.qemu_bin.split('-')[-1] + + self.workdir = os.path.join(BUILD_DIR, 'tests/functional', self.arch, + self.id()) + os.makedirs(self.workdir, exist_ok=True) + + self.logdir = self.workdir + self.log = logging.getLogger('qemu-test') + self.log.setLevel(logging.DEBUG) + self._log_fh = logging.FileHandler(os.path.join(self.logdir, + 'base.log'), mode='w') + self._log_fh.setLevel(logging.DEBUG) + fileFormatter = logging.Formatter( + '%(asctime)s - %(levelname)s: %(message)s') + self._log_fh.setFormatter(fileFormatter) + self.log.addHandler(self._log_fh) + + def tearDown(self): + self.log.removeHandler(self._log_fh) + + def main(): + path = os.path.basename(sys.argv[0])[:-3] + + cache = os.environ.get("QEMU_TEST_PRECACHE", None) + if cache is not None: + Asset.precache_suites(path, cache) + return + + tr = pycotap.TAPTestRunner(message_log = pycotap.LogMode.LogToError, + test_output_log = pycotap.LogMode.LogToError) + unittest.main(module = None, testRunner = tr, argv=["__dummy__", path]) + + +class QemuUserTest(QemuBaseTest): + + def setUp(self): + super().setUp('qemu-') + self._ldpath = [] + + def add_ldpath(self, ldpath): + self._ldpath.append(os.path.abspath(ldpath)) + + def run_cmd(self, bin_path, args=[]): + return subprocess.run([self.qemu_bin] + + ["-L %s" % ldpath for ldpath in self._ldpath] + + [bin_path] + + args, + text=True, capture_output=True) + +class QemuSystemTest(QemuBaseTest): + """Facilitates system emulation tests.""" + + cpu = None + machine = None + _machinehelp = None + + def setUp(self): + self._vms = {} + + super().setUp('qemu-system-') + + console_log = logging.getLogger('console') + console_log.setLevel(logging.DEBUG) + self._console_log_fh = logging.FileHandler(os.path.join(self.workdir, + 'console.log'), mode='w') + self._console_log_fh.setLevel(logging.DEBUG) + fileFormatter = logging.Formatter('%(asctime)s: %(message)s') + self._console_log_fh.setFormatter(fileFormatter) + console_log.addHandler(self._console_log_fh) + + def set_machine(self, machinename): + # TODO: We should use QMP to get the list of available machines + if not self._machinehelp: + self._machinehelp = run_cmd([self.qemu_bin, '-M', 'help'])[0]; + if self._machinehelp.find(machinename) < 0: + self.skipTest('no support for machine ' + machinename) + self.machine = machinename + + def require_accelerator(self, accelerator): + """ + Requires an accelerator to be available for the test to continue + + It takes into account the currently set qemu binary. + + If the check fails, the test is canceled. If the check itself + for the given accelerator is not available, the test is also + canceled. + + :param accelerator: name of the accelerator, such as "kvm" or "tcg" + :type accelerator: str + """ + checker = {'tcg': tcg_available, + 'kvm': kvm_available}.get(accelerator) + if checker is None: + self.skipTest("Don't know how to check for the presence " + "of accelerator %s" % accelerator) + if not checker(qemu_bin=self.qemu_bin): + self.skipTest("%s accelerator does not seem to be " + "available" % accelerator) + + def require_netdev(self, netdevname): + netdevhelp = run_cmd([self.qemu_bin, + '-M', 'none', '-netdev', 'help'])[0]; + if netdevhelp.find('\n' + netdevname + '\n') < 0: + self.skipTest('no support for " + netdevname + " networking') + + def require_device(self, devicename): + devhelp = run_cmd([self.qemu_bin, + '-M', 'none', '-device', 'help'])[0]; + if devhelp.find(devicename) < 0: + self.skipTest('no support for device ' + devicename) + + def _new_vm(self, name, *args): + vm = QEMUMachine(self.qemu_bin, base_temp_dir=self.workdir) + self.log.debug('QEMUMachine "%s" created', name) + self.log.debug('QEMUMachine "%s" temp_dir: %s', name, vm.temp_dir) + self.log.debug('QEMUMachine "%s" log_dir: %s', name, vm.log_dir) + if args: + vm.add_args(*args) + return vm + + @property + def vm(self): + return self.get_vm(name='default') + + def get_vm(self, *args, name=None): + if not name: + name = str(uuid.uuid4()) + if self._vms.get(name) is None: + self._vms[name] = self._new_vm(name, *args) + if self.cpu is not None: + self._vms[name].add_args('-cpu', self.cpu) + if self.machine is not None: + self._vms[name].set_machine(self.machine) + return self._vms[name] + + def set_vm_arg(self, arg, value): + """ + Set an argument to list of extra arguments to be given to the QEMU + binary. If the argument already exists then its value is replaced. + + :param arg: the QEMU argument, such as "-cpu" in "-cpu host" + :type arg: str + :param value: the argument value, such as "host" in "-cpu host" + :type value: str + """ + if not arg or not value: + return + if arg not in self.vm.args: + self.vm.args.extend([arg, value]) + else: + idx = self.vm.args.index(arg) + 1 + if idx < len(self.vm.args): + self.vm.args[idx] = value + else: + self.vm.args.append(value) + + def tearDown(self): + for vm in self._vms.values(): + vm.shutdown() + logging.getLogger('console').removeHandler(self._console_log_fh) + super().tearDown() diff --git a/tests/functional/qemu_test/utils.py b/tests/functional/qemu_test/utils.py new file mode 100644 index 0000000000..2a1cb60d38 --- /dev/null +++ b/tests/functional/qemu_test/utils.py @@ -0,0 +1,56 @@ +# Utilities for python-based QEMU tests +# +# Copyright 2024 Red Hat, Inc. +# +# Authors: +# Thomas Huth <thuth@redhat.com> +# +# This work is licensed under the terms of the GNU GPL, version 2 or +# later. See the COPYING file in the top-level directory. + +import gzip +import lzma +import os +import shutil +import subprocess +import tarfile + +def archive_extract(archive, dest_dir, member=None): + with tarfile.open(archive) as tf: + if hasattr(tarfile, 'data_filter'): + tf.extraction_filter = getattr(tarfile, 'data_filter', + (lambda member, path: member)) + if member: + tf.extract(member=member, path=dest_dir) + else: + tf.extractall(path=dest_dir) + +def gzip_uncompress(gz_path, output_path): + if os.path.exists(output_path): + return + with gzip.open(gz_path, 'rb') as gz_in: + try: + with open(output_path, 'wb') as raw_out: + shutil.copyfileobj(gz_in, raw_out) + except: + os.remove(output_path) + raise + +def lzma_uncompress(xz_path, output_path): + if os.path.exists(output_path): + return + with lzma.open(xz_path, 'rb') as lzma_in: + try: + with open(output_path, 'wb') as raw_out: + shutil.copyfileobj(lzma_in, raw_out) + except: + os.remove(output_path) + raise + +def cpio_extract(cpio_handle, output_path): + cwd = os.getcwd() + os.chdir(output_path) + subprocess.run(['cpio', '-i'], + input=cpio_handle.read(), + stderr=subprocess.DEVNULL) + os.chdir(cwd) diff --git a/tests/avocado/machine_aarch64_sbsaref.py b/tests/functional/test_aarch64_sbsaref.py index e920bbf08c..f31c2a60b6 100644..100755 --- a/tests/avocado/machine_aarch64_sbsaref.py +++ b/tests/functional/test_aarch64_sbsaref.py @@ -1,3 +1,5 @@ +#!/usr/bin/env python3 +# # Functional test that boots a Linux kernel and checks the console # # SPDX-FileCopyrightText: 2023-2024 Linaro Ltd. @@ -8,26 +10,31 @@ import os -from avocado import skipUnless -from avocado.utils import archive - -from avocado_qemu import QemuSystemTest -from avocado_qemu import wait_for_console_pattern -from avocado_qemu import interrupt_interactive_console_until_pattern +from qemu_test import QemuSystemTest, Asset +from qemu_test import wait_for_console_pattern +from qemu_test import interrupt_interactive_console_until_pattern +from qemu_test.utils import lzma_uncompress +from unittest import skipUnless class Aarch64SbsarefMachine(QemuSystemTest): """ - :avocado: tags=arch:aarch64 - :avocado: tags=machine:sbsa-ref - :avocado: tags=accel:tcg - As firmware runs at a higher privilege level than the hypervisor we can only run these tests under TCG emulation. """ timeout = 180 + ASSET_FLASH0 = Asset( + ('https://artifacts.codelinaro.org/artifactory/linaro-419-sbsa-ref/' + '20240619-148232/edk2/SBSA_FLASH0.fd.xz'), + '0c954842a590988f526984de22e21ae0ab9cb351a0c99a8a58e928f0c7359cf7') + + ASSET_FLASH1 = Asset( + ('https://artifacts.codelinaro.org/artifactory/linaro-419-sbsa-ref/' + '20240619-148232/edk2/SBSA_FLASH1.fd.xz'), + 'c6ec39374c4d79bb9e9cdeeb6db44732d90bb4a334cec92002b3f4b9cac4b5ee') + def fetch_firmware(self): """ Flash volumes generated using: @@ -44,47 +51,31 @@ class Aarch64SbsarefMachine(QemuSystemTest): """ # Secure BootRom (TF-A code) - fs0_xz_url = ( - "https://artifacts.codelinaro.org/artifactory/linaro-419-sbsa-ref/" - "20240619-148232/edk2/SBSA_FLASH0.fd.xz" - ) - fs0_xz_hash = "0c954842a590988f526984de22e21ae0ab9cb351a0c99a8a58e928f0c7359cf7" - tar_xz_path = self.fetch_asset(fs0_xz_url, asset_hash=fs0_xz_hash, - algorithm='sha256') - archive.extract(tar_xz_path, self.workdir) + fs0_xz_path = self.ASSET_FLASH0.fetch() fs0_path = os.path.join(self.workdir, "SBSA_FLASH0.fd") + lzma_uncompress(fs0_xz_path, fs0_path) # Non-secure rom (UEFI and EFI variables) - fs1_xz_url = ( - "https://artifacts.codelinaro.org/artifactory/linaro-419-sbsa-ref/" - "20240619-148232/edk2/SBSA_FLASH1.fd.xz" - ) - fs1_xz_hash = "c6ec39374c4d79bb9e9cdeeb6db44732d90bb4a334cec92002b3f4b9cac4b5ee" - tar_xz_path = self.fetch_asset(fs1_xz_url, asset_hash=fs1_xz_hash, - algorithm='sha256') - archive.extract(tar_xz_path, self.workdir) + fs1_xz_path = self.ASSET_FLASH1.fetch() fs1_path = os.path.join(self.workdir, "SBSA_FLASH1.fd") + lzma_uncompress(fs1_xz_path, fs1_path) for path in [fs0_path, fs1_path]: with open(path, "ab+") as fd: fd.truncate(256 << 20) # Expand volumes to 256MiB + self.set_machine('sbsa-ref') self.vm.set_console() self.vm.add_args( - "-drive", - f"if=pflash,file={fs0_path},format=raw", - "-drive", - f"if=pflash,file={fs1_path},format=raw", - "-machine", - "sbsa-ref", + "-drive", f"if=pflash,file={fs0_path},format=raw", + "-drive", f"if=pflash,file={fs1_path},format=raw", ) def test_sbsaref_edk2_firmware(self): - """ - :avocado: tags=cpu:cortex-a57 - """ self.fetch_firmware() + + self.vm.add_args('-cpu', 'cortex-a57') self.vm.launch() # TF-A boot sequence: @@ -110,87 +101,62 @@ class Aarch64SbsarefMachine(QemuSystemTest): wait_for_console_pattern(self, "UEFI firmware (version 1.0") interrupt_interactive_console_until_pattern(self, "QEMU SBSA-REF Machine") + + ASSET_ALPINE_ISO = Asset( + ('https://dl-cdn.alpinelinux.org/' + 'alpine/v3.17/releases/aarch64/alpine-standard-3.17.2-aarch64.iso'), + '5a36304ecf039292082d92b48152a9ec21009d3a62f459de623e19c4bd9dc027') + # This tests the whole boot chain from EFI to Userspace # We only boot a whole OS for the current top level CPU and GIC # Other test profiles should use more minimal boots def boot_alpine_linux(self, cpu): self.fetch_firmware() - iso_url = ( - "https://dl-cdn.alpinelinux.org/" - "alpine/v3.17/releases/aarch64/alpine-standard-3.17.2-aarch64.iso" - ) - - iso_hash = "5a36304ecf039292082d92b48152a9ec21009d3a62f459de623e19c4bd9dc027" - iso_path = self.fetch_asset(iso_url, algorithm="sha256", asset_hash=iso_hash) + iso_path = self.ASSET_ALPINE_ISO.fetch() self.vm.set_console() self.vm.add_args( - "-cpu", - cpu, - "-drive", - f"file={iso_path},format=raw", + "-cpu", cpu, + "-drive", f"file={iso_path},media=cdrom,format=raw", ) self.vm.launch() wait_for_console_pattern(self, "Welcome to Alpine Linux 3.17") def test_sbsaref_alpine_linux_cortex_a57(self): - """ - :avocado: tags=cpu:cortex-a57 - :avocado: tags=os:linux - """ self.boot_alpine_linux("cortex-a57") def test_sbsaref_alpine_linux_neoverse_n1(self): - """ - :avocado: tags=cpu:neoverse-n1 - :avocado: tags=os:linux - """ self.boot_alpine_linux("neoverse-n1") def test_sbsaref_alpine_linux_max_pauth_off(self): - """ - :avocado: tags=cpu:max - :avocado: tags=os:linux - """ self.boot_alpine_linux("max,pauth=off") def test_sbsaref_alpine_linux_max_pauth_impdef(self): - """ - :avocado: tags=cpu:max - :avocado: tags=os:linux - """ self.boot_alpine_linux("max,pauth-impdef=on") - @skipUnless(os.getenv('AVOCADO_TIMEOUT_EXPECTED'), 'Test might timeout') + @skipUnless(os.getenv('QEMU_TEST_TIMEOUT_EXPECTED'), 'Test might timeout') def test_sbsaref_alpine_linux_max(self): - """ - :avocado: tags=cpu:max - :avocado: tags=os:linux - """ self.boot_alpine_linux("max") + ASSET_OPENBSD_ISO = Asset( + ('https://cdn.openbsd.org/pub/OpenBSD/7.3/arm64/miniroot73.img'), + '7fc2c75401d6f01fbfa25f4953f72ad7d7c18650056d30755c44b9c129b707e5') + # This tests the whole boot chain from EFI to Userspace # We only boot a whole OS for the current top level CPU and GIC # Other test profiles should use more minimal boots def boot_openbsd73(self, cpu): self.fetch_firmware() - img_url = ( - "https://cdn.openbsd.org/pub/OpenBSD/7.3/arm64/miniroot73.img" - ) - - img_hash = "7fc2c75401d6f01fbfa25f4953f72ad7d7c18650056d30755c44b9c129b707e5" - img_path = self.fetch_asset(img_url, algorithm="sha256", asset_hash=img_hash) + img_path = self.ASSET_OPENBSD_ISO.fetch() self.vm.set_console() self.vm.add_args( - "-cpu", - cpu, - "-drive", - f"file={img_path},format=raw", + "-cpu", cpu, + "-drive", f"file={img_path},format=raw,snapshot=on", ) self.vm.launch() @@ -199,38 +165,22 @@ class Aarch64SbsarefMachine(QemuSystemTest): " 7.3 installation program.") def test_sbsaref_openbsd73_cortex_a57(self): - """ - :avocado: tags=cpu:cortex-a57 - :avocado: tags=os:openbsd - """ self.boot_openbsd73("cortex-a57") def test_sbsaref_openbsd73_neoverse_n1(self): - """ - :avocado: tags=cpu:neoverse-n1 - :avocado: tags=os:openbsd - """ self.boot_openbsd73("neoverse-n1") def test_sbsaref_openbsd73_max_pauth_off(self): - """ - :avocado: tags=cpu:max - :avocado: tags=os:openbsd - """ self.boot_openbsd73("max,pauth=off") - @skipUnless(os.getenv('AVOCADO_TIMEOUT_EXPECTED'), 'Test might timeout') + @skipUnless(os.getenv('QEMU_TEST_TIMEOUT_EXPECTED'), 'Test might timeout') def test_sbsaref_openbsd73_max_pauth_impdef(self): - """ - :avocado: tags=cpu:max - :avocado: tags=os:openbsd - """ self.boot_openbsd73("max,pauth-impdef=on") - @skipUnless(os.getenv('AVOCADO_TIMEOUT_EXPECTED'), 'Test might timeout') + @skipUnless(os.getenv('QEMU_TEST_TIMEOUT_EXPECTED'), 'Test might timeout') def test_sbsaref_openbsd73_max(self): - """ - :avocado: tags=cpu:max - :avocado: tags=os:openbsd - """ self.boot_openbsd73("max") + + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/avocado/machine_aarch64_virt.py b/tests/functional/test_aarch64_virt.py index a90dc6ff4b..c967da41b4 100644..100755 --- a/tests/avocado/machine_aarch64_virt.py +++ b/tests/functional/test_aarch64_virt.py @@ -1,3 +1,5 @@ +#!/usr/bin/env python3 +# # Functional test that boots a various Linux systems and checks the # console output. # @@ -12,12 +14,11 @@ import time import os import logging -from avocado_qemu import QemuSystemTest -from avocado_qemu import wait_for_console_pattern -from avocado_qemu import exec_command -from avocado_qemu import BUILD_DIR -from avocado.utils import process -from avocado.utils.path import find_command +from qemu_test import BUILD_DIR +from qemu_test import QemuSystemTest, Asset +from qemu_test import exec_command, wait_for_console_pattern +from qemu_test import get_qemu_img, run_cmd + class Aarch64VirtMachine(QemuSystemTest): KERNEL_COMMON_COMMAND_LINE = 'printk.time=0 ' @@ -28,23 +29,18 @@ class Aarch64VirtMachine(QemuSystemTest): failure_message='Kernel panic - not syncing', vm=vm) + ASSET_ALPINE_ISO = Asset( + ('https://dl-cdn.alpinelinux.org/' + 'alpine/v3.17/releases/aarch64/alpine-standard-3.17.2-aarch64.iso'), + '5a36304ecf039292082d92b48152a9ec21009d3a62f459de623e19c4bd9dc027') + # This tests the whole boot chain from EFI to Userspace # We only boot a whole OS for the current top level CPU and GIC # Other test profiles should use more minimal boots def test_alpine_virt_tcg_gic_max(self): - """ - :avocado: tags=arch:aarch64 - :avocado: tags=machine:virt - :avocado: tags=accel:tcg - """ - iso_url = ('https://dl-cdn.alpinelinux.org/' - 'alpine/v3.17/releases/aarch64/' - 'alpine-standard-3.17.2-aarch64.iso') - - # Alpine use sha256 so I recalculated this myself - iso_sha1 = '76284fcd7b41fe899b0c2375ceb8470803eea839' - iso_path = self.fetch_asset(iso_url, asset_hash=iso_sha1) + iso_path = self.ASSET_ALPINE_ISO.fetch() + self.set_machine('virt') self.vm.set_console() kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + 'console=ttyAMA0') @@ -60,7 +56,7 @@ class Aarch64VirtMachine(QemuSystemTest): self.vm.add_args("-smp", "2", "-m", "1024") self.vm.add_args('-bios', os.path.join(BUILD_DIR, 'pc-bios', 'edk2-aarch64-code.fd')) - self.vm.add_args("-drive", f"file={iso_path},format=raw") + self.vm.add_args("-drive", f"file={iso_path},media=cdrom,format=raw") self.vm.add_args('-device', 'virtio-rng-pci,rng=rng0') self.vm.add_args('-object', 'rng-random,id=rng0,filename=/dev/urandom') @@ -68,6 +64,11 @@ class Aarch64VirtMachine(QemuSystemTest): self.wait_for_console_pattern('Welcome to Alpine Linux 3.17') + ASSET_KERNEL = Asset( + ('https://fileserver.linaro.org/s/' + 'z6B2ARM7DQT3HWN/download'), + '12a54d4805cda6ab647cb7c7bbdb16fafb3df400e0d6f16445c1a0436100ef8d') + def common_aarch64_virt(self, machine): """ Common code to launch basic virt machine with kernel+initrd @@ -75,11 +76,9 @@ class Aarch64VirtMachine(QemuSystemTest): """ logger = logging.getLogger('aarch64_virt') - kernel_url = ('https://fileserver.linaro.org/s/' - 'z6B2ARM7DQT3HWN/download') - kernel_hash = 'ed11daab50c151dde0e1e9c9cb8b2d9bd3215347' - kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) + kernel_path = self.ASSET_KERNEL.fetch() + self.set_machine('virt') self.vm.set_console() kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + 'console=ttyAMA0') @@ -98,14 +97,8 @@ class Aarch64VirtMachine(QemuSystemTest): # Also add a scratch block device logger.info('creating scratch qcow2 image') image_path = os.path.join(self.workdir, 'scratch.qcow2') - qemu_img = os.path.join(BUILD_DIR, 'qemu-img') - if not os.path.exists(qemu_img): - qemu_img = find_command('qemu-img', False) - if qemu_img is False: - self.cancel('Could not find "qemu-img", which is required to ' - 'create the temporary qcow2 image') - cmd = '%s create -f qcow2 %s 8M' % (qemu_img, image_path) - process.run(cmd) + qemu_img = get_qemu_img(self) + run_cmd([qemu_img, 'create', '-f', 'qcow2', image_path, '8M']) # Add the device self.vm.add_args('-blockdev', @@ -128,19 +121,11 @@ class Aarch64VirtMachine(QemuSystemTest): time.sleep(0.1) def test_aarch64_virt_gicv3(self): - """ - :avocado: tags=arch:aarch64 - :avocado: tags=machine:virt - :avocado: tags=accel:tcg - :avocado: tags=cpu:max - """ self.common_aarch64_virt("virt,gic_version=3") def test_aarch64_virt_gicv2(self): - """ - :avocado: tags=arch:aarch64 - :avocado: tags=machine:virt - :avocado: tags=accel:tcg - :avocado: tags=cpu:max - """ self.common_aarch64_virt("virt,gic-version=2") + + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/avocado/acpi-bits.py b/tests/functional/test_acpi_bits.py index efe4f52ee0..ee40647d5b 100644..100755 --- a/tests/avocado/acpi-bits.py +++ b/tests/functional/test_acpi_bits.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# group: rw quick +# # Exercise QEMU generated ACPI/SMBIOS tables using biosbits, # https://biosbits.org/ # @@ -24,7 +24,7 @@ # pylint: disable=consider-using-f-string """ -This is QEMU ACPI/SMBIOS avocado tests using biosbits. +This is QEMU ACPI/SMBIOS functional tests using biosbits. Biosbits is available originally at https://biosbits.org/. This test uses a fork of the upstream bits and has numerous fixes including an upgraded acpica. The fork is located here: @@ -41,15 +41,16 @@ import tarfile import tempfile import time import zipfile + +from pathlib import Path from typing import ( List, Optional, Sequence, ) from qemu.machine import QEMUMachine -from avocado import skipIf -from avocado.utils import datadrainer as drainer -from avocado_qemu import QemuBaseTest +from unittest import skipIf +from qemu_test import QemuBaseTest, Asset deps = ["xorriso", "mformat"] # dependent tools needed in the test setup/box. supported_platforms = ['x86_64'] # supported test platforms. @@ -129,34 +130,32 @@ class QEMUBitsMachine(QEMUMachine): # pylint: disable=too-few-public-methods class AcpiBitsTest(QemuBaseTest): #pylint: disable=too-many-instance-attributes """ ACPI and SMBIOS tests using biosbits. - - :avocado: tags=arch:x86_64 - :avocado: tags=acpi - """ # in slower systems the test can take as long as 3 minutes to complete. timeout = BITS_TIMEOUT + # following are some standard configuration constants + # gitlab CI does shallow clones of depth 20 + BITS_INTERNAL_VER = 2020 + # commit hash must match the artifact tag below + BITS_COMMIT_HASH = 'c7920d2b' + # this is the latest bits release as of today. + BITS_TAG = "qemu-bits-10262023" + + ASSET_BITS = Asset(("https://gitlab.com/qemu-project/" + "biosbits-bits/-/jobs/artifacts/%s/" + "download?job=qemu-bits-build" % BITS_TAG), + '1b8dd612c6831a6b491716a77acc486666aaa867051cdc34f7ce169c2e25f487') + def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self._vm = None self._workDir = None self._baseDir = None - # following are some standard configuration constants - self._bitsInternalVer = 2020 # gitlab CI does shallow clones of depth 20 - self._bitsCommitHash = 'c7920d2b' # commit hash must match - # the artifact tag below - self._bitsTag = "qemu-bits-10262023" # this is the latest bits - # release as of today. - self._bitsArtSHA1Hash = 'b22cdfcfc7453875297d06d626f5474ee36a343f' - self._bitsArtURL = ("https://gitlab.com/qemu-project/" - "biosbits-bits/-/jobs/artifacts/%s/" - "download?job=qemu-bits-build" %self._bitsTag) self._debugcon_addr = '0x403' self._debugcon_log = 'debugcon-log.txt' - logging.basicConfig(level=logging.INFO) - self.logger = logging.getLogger('acpi-bits') + self.logger = self.log def _print_log(self, log): self.logger.info('\nlogs from biosbits follows:') @@ -171,7 +170,7 @@ class AcpiBitsTest(QemuBaseTest): #pylint: disable=too-many-instance-attributes bits_config_dir = os.path.join(self._baseDir, 'acpi-bits', 'bits-config') target_config_dir = os.path.join(self._workDir, - 'bits-%d' %self._bitsInternalVer, + 'bits-%d' %self.BITS_INTERNAL_VER, 'boot') self.assertTrue(os.path.exists(bits_config_dir)) self.assertTrue(os.path.exists(target_config_dir)) @@ -188,7 +187,7 @@ class AcpiBitsTest(QemuBaseTest): #pylint: disable=too-many-instance-attributes bits_test_dir = os.path.join(self._baseDir, 'acpi-bits', 'bits-tests') target_test_dir = os.path.join(self._workDir, - 'bits-%d' %self._bitsInternalVer, + 'bits-%d' %self.BITS_INTERNAL_VER, 'boot', 'python') self.assertTrue(os.path.exists(bits_test_dir)) @@ -248,9 +247,9 @@ class AcpiBitsTest(QemuBaseTest): #pylint: disable=too-many-instance-attributes test scripts """ bits_dir = os.path.join(self._workDir, - 'bits-%d' %self._bitsInternalVer) + 'bits-%d' %self.BITS_INTERNAL_VER) iso_file = os.path.join(self._workDir, - 'bits-%d.iso' %self._bitsInternalVer) + 'bits-%d.iso' %self.BITS_INTERNAL_VER) mkrescue_script = os.path.join(self._workDir, 'grub-inst-x86_64-efi', 'bin', 'grub-mkrescue') @@ -264,8 +263,12 @@ class AcpiBitsTest(QemuBaseTest): #pylint: disable=too-many-instance-attributes try: if os.getenv('V') or os.getenv('BITS_DEBUG'): - subprocess.check_call([mkrescue_script, '-o', iso_file, - bits_dir], stderr=subprocess.STDOUT) + proc = subprocess.run([mkrescue_script, '-o', iso_file, + bits_dir], + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + check=True) + self.logger.info("grub-mkrescue output %s" % proc.stdout) else: subprocess.check_call([mkrescue_script, '-o', iso_file, bits_dir], @@ -282,8 +285,9 @@ class AcpiBitsTest(QemuBaseTest): #pylint: disable=too-many-instance-attributes def setUp(self): # pylint: disable=arguments-differ super().setUp('qemu-system-') + self.logger = self.log - self._baseDir = os.getenv('AVOCADO_TEST_BASEDIR') + self._baseDir = Path(__file__).parent # workdir could also be avocado's own workdir in self.workdir. # At present, I prefer to maintain my own temporary working @@ -300,15 +304,14 @@ class AcpiBitsTest(QemuBaseTest): #pylint: disable=too-many-instance-attributes os.mkdir(prebuiltDir, mode=0o775) bits_zip_file = os.path.join(prebuiltDir, 'bits-%d-%s.zip' - %(self._bitsInternalVer, - self._bitsCommitHash)) + %(self.BITS_INTERNAL_VER, + self.BITS_COMMIT_HASH)) grub_tar_file = os.path.join(prebuiltDir, 'bits-%d-%s-grub.tar.gz' - %(self._bitsInternalVer, - self._bitsCommitHash)) + %(self.BITS_INTERNAL_VER, + self.BITS_COMMIT_HASH)) - bitsLocalArtLoc = self.fetch_asset(self._bitsArtURL, - asset_hash=self._bitsArtSHA1Hash) + bitsLocalArtLoc = self.ASSET_BITS.fetch() self.logger.info("downloaded bits artifacts to %s", bitsLocalArtLoc) # extract the bits artifact in the temp working directory @@ -369,7 +372,7 @@ class AcpiBitsTest(QemuBaseTest): #pylint: disable=too-many-instance-attributes """The main test case implementation.""" iso_file = os.path.join(self._workDir, - 'bits-%d.iso' %self._bitsInternalVer) + 'bits-%d.iso' %self.BITS_INTERNAL_VER) self.assertTrue(os.access(iso_file, os.R_OK)) @@ -393,12 +396,6 @@ class AcpiBitsTest(QemuBaseTest): #pylint: disable=too-many-instance-attributes self._vm.set_console() self._vm.launch() - self.logger.debug("Console output from bits VM follows ...") - c_drainer = drainer.LineLogger(self._vm.console_socket.fileno(), - logger=self.logger.getChild("console"), - stop_check=(lambda : - not self._vm.is_running())) - c_drainer.start() # biosbits has been configured to run all the specified test suites # in batch mode and then automatically initiate a vm shutdown. @@ -406,4 +403,8 @@ class AcpiBitsTest(QemuBaseTest): #pylint: disable=too-many-instance-attributes # with the avocado test timeout. self._vm.event_wait('SHUTDOWN', timeout=BITS_TIMEOUT) self._vm.wait(timeout=None) + self.logger.debug("Checking console output ...") self.parse_log() + +if __name__ == '__main__': + QemuBaseTest.main() diff --git a/tests/functional/test_arm_bflt.py b/tests/functional/test_arm_bflt.py new file mode 100755 index 0000000000..281925d11a --- /dev/null +++ b/tests/functional/test_arm_bflt.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python3 +# +# Test the bFLT loader format +# +# Copyright (C) 2019 Philippe Mathieu-Daudé <f4bug@amsat.org> +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import os +import bz2 + +from qemu_test import QemuUserTest, Asset +from qemu_test import has_cmd +from qemu_test.utils import cpio_extract +from unittest import skipUnless + + +class LoadBFLT(QemuUserTest): + + ASSET_ROOTFS = Asset( + ('https://elinux.org/images/5/51/Stm32_mini_rootfs.cpio.bz2'), + 'eefb788e4980c9e8d6c9d60ce7d15d4da6bf4fbc6a80f487673824600d5ba9cc') + + @skipUnless(*has_cmd('cpio')) + @skipUnless(os.getenv('QEMU_TEST_ALLOW_UNTRUSTED_CODE'), 'untrusted code') + def test_stm32(self): + # See https://elinux.org/STM32#User_Space + rootfs_path_bz2 = self.ASSET_ROOTFS.fetch() + busybox_path = os.path.join(self.workdir, "bin/busybox") + + with bz2.open(rootfs_path_bz2, 'rb') as cpio_handle: + cpio_extract(cpio_handle, self.workdir) + + res = self.run_cmd(busybox_path) + ver = 'BusyBox v1.24.0.git (2015-02-03 22:17:13 CET) multi-call binary.' + self.assertIn(ver, res.stdout) + + res = self.run_cmd(busybox_path, ['uname', '-a']) + unm = 'armv7l GNU/Linux' + self.assertIn(unm, res.stdout) + + +if __name__ == '__main__': + QemuUserTest.main() diff --git a/tests/avocado/machine_arm_canona1100.py b/tests/functional/test_arm_canona1100.py index a42d8b0f2b..65f1228296 100644..100755 --- a/tests/avocado/machine_arm_canona1100.py +++ b/tests/functional/test_arm_canona1100.py @@ -1,3 +1,5 @@ +#!/usr/bin/env python3 +# # Functional test that boots the canon-a1100 machine with firmware # # Copyright (c) 2020 Red Hat, Inc. @@ -8,28 +10,30 @@ # This work is licensed under the terms of the GNU GPL, version 2 or # later. See the COPYING file in the top-level directory. -from avocado_qemu import QemuSystemTest -from avocado_qemu import wait_for_console_pattern -from avocado.utils import archive +from qemu_test import QemuSystemTest, Asset +from qemu_test import wait_for_console_pattern +from qemu_test.utils import archive_extract class CanonA1100Machine(QemuSystemTest): """Boots the barebox firmware and checks that the console is operational""" timeout = 90 + ASSET_BIOS = Asset(('https://qemu-advcal.gitlab.io' + '/qac-best-of-multiarch/download/day18.tar.xz'), + '28e71874ce985be66b7fd1345ed88cb2523b982f899c8d2900d6353054a1be49') + def test_arm_canona1100(self): - """ - :avocado: tags=arch:arm - :avocado: tags=machine:canon-a1100 - :avocado: tags=device:pflash_cfi02 - """ - tar_url = ('https://qemu-advcal.gitlab.io' - '/qac-best-of-multiarch/download/day18.tar.xz') - tar_hash = '068b5fc4242b29381acee94713509f8a876e9db6' - file_path = self.fetch_asset(tar_url, asset_hash=tar_hash) - archive.extract(file_path, self.workdir) + self.set_machine('canon-a1100') + + file_path = self.ASSET_BIOS.fetch() + archive_extract(file_path, dest_dir=self.workdir, + member="day18/barebox.canon-a1100.bin") self.vm.set_console() self.vm.add_args('-bios', self.workdir + '/day18/barebox.canon-a1100.bin') self.vm.launch() wait_for_console_pattern(self, 'running /env/bin/init') + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/avocado/machine_arm_integratorcp.py b/tests/functional/test_arm_integratorcp.py index 87f5cf3953..0fe083f661 100644..100755 --- a/tests/avocado/machine_arm_integratorcp.py +++ b/tests/functional/test_arm_integratorcp.py @@ -1,3 +1,5 @@ +#!/usr/bin/env python3 +# # Functional test that boots a Linux kernel and checks the console # # Copyright (c) 2020 Red Hat, Inc. @@ -7,13 +9,15 @@ # # This work is licensed under the terms of the GNU GPL, version 2 or # later. See the COPYING file in the top-level directory. +# +# SPDX-License-Identifier: GPL-2.0-or-later import os import logging -from avocado import skipUnless -from avocado_qemu import QemuSystemTest -from avocado_qemu import wait_for_console_pattern +from qemu_test import QemuSystemTest, Asset +from qemu_test import wait_for_console_pattern +from unittest import skipUnless NUMPY_AVAILABLE = True @@ -33,50 +37,49 @@ class IntegratorMachine(QemuSystemTest): timeout = 90 - def boot_integratorcp(self): - kernel_url = ('https://github.com/zayac/qemu-arm/raw/master/' - 'arm-test/kernel/zImage.integrator') - kernel_hash = '0d7adba893c503267c946a3cbdc63b4b54f25468' - kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) + ASSET_KERNEL = Asset( + ('https://github.com/zayac/qemu-arm/raw/master/' + 'arm-test/kernel/zImage.integrator'), + '26e7c7e8f943de785d95bd3c74d66451604a9b6a7a3d25dceb279e7548fd8e78') + + ASSET_INITRD = Asset( + ('https://github.com/zayac/qemu-arm/raw/master/' + 'arm-test/kernel/arm_root.img'), + 'e187c27fb342ad148c7f33475fbed124933e0b3f4be8c74bc4f3426a4793373a') - initrd_url = ('https://github.com/zayac/qemu-arm/raw/master/' - 'arm-test/kernel/arm_root.img') - initrd_hash = 'b51e4154285bf784e017a37586428332d8c7bd8b' - initrd_path = self.fetch_asset(initrd_url, asset_hash=initrd_hash) + ASSET_TUXLOGO = Asset( + ('https://github.com/torvalds/linux/raw/v2.6.12/' + 'drivers/video/logo/logo_linux_vga16.ppm'), + 'b762f0d91ec018887ad1b334543c2fdf9be9fdfc87672b409211efaa3ea0ef79') + def boot_integratorcp(self): + kernel_path = self.ASSET_KERNEL.fetch() + initrd_path = self.ASSET_INITRD.fetch() + + self.set_machine('integratorcp') self.vm.set_console() self.vm.add_args('-kernel', kernel_path, '-initrd', initrd_path, '-append', 'printk.time=0 console=ttyAMA0') self.vm.launch() - @skipUnless(os.getenv('AVOCADO_ALLOW_UNTRUSTED_CODE'), 'untrusted code') + @skipUnless(os.getenv('QEMU_TEST_ALLOW_UNTRUSTED_CODE'), 'untrusted code') def test_integratorcp_console(self): """ Boots the Linux kernel and checks that the console is operational - :avocado: tags=arch:arm - :avocado: tags=machine:integratorcp - :avocado: tags=device:pl011 """ self.boot_integratorcp() wait_for_console_pattern(self, 'Log in as root') @skipUnless(NUMPY_AVAILABLE, 'Python NumPy not installed') @skipUnless(CV2_AVAILABLE, 'Python OpenCV not installed') - @skipUnless(os.getenv('AVOCADO_ALLOW_UNTRUSTED_CODE'), 'untrusted code') + @skipUnless(os.getenv('QEMU_TEST_ALLOW_UNTRUSTED_CODE'), 'untrusted code') def test_framebuffer_tux_logo(self): """ Boot Linux and verify the Tux logo is displayed on the framebuffer. - :avocado: tags=arch:arm - :avocado: tags=machine:integratorcp - :avocado: tags=device:pl110 - :avocado: tags=device:framebuffer """ screendump_path = os.path.join(self.workdir, "screendump.pbm") - tuxlogo_url = ('https://github.com/torvalds/linux/raw/v2.6.12/' - 'drivers/video/logo/logo_linux_vga16.ppm') - tuxlogo_hash = '3991c2ddbd1ddaecda7601f8aafbcf5b02dc86af' - tuxlogo_path = self.fetch_asset(tuxlogo_url, asset_hash=tuxlogo_hash) + tuxlogo_path = self.ASSET_TUXLOGO.fetch() self.boot_integratorcp() framebuffer_ready = 'Console: switching to colour frame buffer device' @@ -97,3 +100,6 @@ class IntegratorMachine(QemuSystemTest): for tux_count, pt in enumerate(zip(*loc[::-1]), start=1): logger.debug('found Tux at position [x, y] = %s', pt) self.assertGreaterEqual(tux_count, cpu_count) + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/avocado/machine_avr6.py b/tests/functional/test_avr_mega2560.py index 5485db79c6..8e47b4200b 100644..100755 --- a/tests/avocado/machine_avr6.py +++ b/tests/functional/test_avr_mega2560.py @@ -1,3 +1,4 @@ +#!/usr/bin/env python3 # # QEMU AVR integration tests # @@ -19,26 +20,24 @@ import time -from avocado_qemu import QemuSystemTest +from qemu_test import QemuSystemTest, Asset class AVR6Machine(QemuSystemTest): timeout = 5 + ASSET_ROM = Asset(('https://github.com/seharris/qemu-avr-tests' + '/raw/36c3e67b8755dcf/free-rtos/Demo' + '/AVR_ATMega2560_GCC/demo.elf'), + 'ee4833bd65fc69e84a79ed1c608affddbd499a60e63acf87d9113618401904e4') + def test_freertos(self): """ - :avocado: tags=arch:avr - :avocado: tags=machine:arduino-mega-2560-v3 - """ - """ https://github.com/seharris/qemu-avr-tests/raw/master/free-rtos/Demo/AVR_ATMega2560_GCC/demo.elf constantly prints out 'ABCDEFGHIJKLMNOPQRSTUVWXABCDEFGHIJKLMNOPQRSTUVWX' """ - rom_url = ('https://github.com/seharris/qemu-avr-tests' - '/raw/36c3e67b8755dcf/free-rtos/Demo' - '/AVR_ATMega2560_GCC/demo.elf') - rom_hash = '7eb521f511ca8f2622e0a3c5e8dd686efbb911d4' - rom_path = self.fetch_asset(rom_url, asset_hash=rom_hash) + rom_path = self.ASSET_ROM.fetch() + self.set_machine('arduino-mega-2560-v3') self.vm.add_args('-bios', rom_path) self.vm.add_args('-nographic') self.vm.launch() @@ -48,3 +47,6 @@ class AVR6Machine(QemuSystemTest): self.assertIn('ABCDEFGHIJKLMNOPQRSTUVWXABCDEFGHIJKLMNOPQRSTUVWX', self.vm.get_log()) + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/avocado/cpu_queries.py b/tests/functional/test_cpu_queries.py index d3faa14720..b1122a0e8f 100644..100755 --- a/tests/avocado/cpu_queries.py +++ b/tests/functional/test_cpu_queries.py @@ -1,3 +1,5 @@ +#!/usr/bin/env python3 +# # Sanity check of query-cpu-* results # # Copyright (c) 2019 Red Hat, Inc. @@ -8,7 +10,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 avocado_qemu import QemuSystemTest +from qemu_test import QemuSystemTest class QueryCPUModelExpansion(QemuSystemTest): """ @@ -16,10 +18,7 @@ class QueryCPUModelExpansion(QemuSystemTest): """ def test(self): - """ - :avocado: tags=arch:x86_64 - :avocado: tags=machine:none - """ + self.set_machine('none') self.vm.add_args('-S') self.vm.launch() @@ -33,3 +32,6 @@ class QueryCPUModelExpansion(QemuSystemTest): e = self.vm.cmd('query-cpu-model-expansion', model=model, type='full') self.assertEqual(e['model']['name'], c['name']) + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/avocado/empty_cpu_model.py b/tests/functional/test_empty_cpu_model.py index d906ef3d3c..0081b06d85 100644..100755 --- a/tests/avocado/empty_cpu_model.py +++ b/tests/functional/test_empty_cpu_model.py @@ -1,3 +1,5 @@ +#!/usr/bin/env python3 +# # Check for crash when using empty -cpu option # # Copyright (c) 2019 Red Hat, Inc. @@ -7,7 +9,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 avocado_qemu import QemuSystemTest +from qemu_test import QemuSystemTest class EmptyCPUModel(QemuSystemTest): def test(self): @@ -17,3 +19,6 @@ class EmptyCPUModel(QemuSystemTest): self.vm.wait() self.assertEqual(self.vm.exitcode(), 1, "QEMU exit code should be 1") self.assertRegex(self.vm.get_log(), r'-cpu option cannot be empty') + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/avocado/info_usernet.py b/tests/functional/test_info_usernet.py index e1aa7a6e0a..cd37524d94 100644..100755 --- a/tests/avocado/info_usernet.py +++ b/tests/functional/test_info_usernet.py @@ -1,3 +1,5 @@ +#!/usr/bin/env python3 +# # Test for the hmp command "info usernet" # # Copyright (c) 2021 Red Hat, Inc. @@ -8,18 +10,16 @@ # This work is licensed under the terms of the GNU GPL, version 2 or # later. See the COPYING file in the top-level directory. -from avocado_qemu import QemuSystemTest +from qemu_test import QemuSystemTest from qemu.utils import get_info_usernet_hostfwd_port class InfoUsernet(QemuSystemTest): - """ - :avocado: tags=machine:none - """ def test_hostfwd(self): self.require_netdev('user') + self.set_machine('none') self.vm.add_args('-netdev', 'user,id=vnet,hostfwd=:127.0.0.1:0-:22') self.vm.launch() res = self.vm.cmd('human-monitor-command', @@ -31,3 +31,6 @@ class InfoUsernet(QemuSystemTest): self.assertGreater(port, 0, ('Found a redirected port that is not greater than' ' zero')) + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/avocado/linux_initrd.py b/tests/functional/test_linux_initrd.py index 7f47b98ae7..c71a59d4c9 100644..100755 --- a/tests/avocado/linux_initrd.py +++ b/tests/functional/test_linux_initrd.py @@ -1,3 +1,5 @@ +#!/usr/bin/env python3 +# # Linux initrd integration test. # # Copyright (c) 2018 Red Hat, Inc. @@ -12,20 +14,27 @@ import os import logging import tempfile -from avocado_qemu import QemuSystemTest -from avocado import skipUnless +from qemu_test import QemuSystemTest, Asset +from unittest import skipUnless class LinuxInitrd(QemuSystemTest): """ Checks QEMU evaluates correctly the initrd file passed as -initrd option. - - :avocado: tags=arch:x86_64 - :avocado: tags=machine:pc """ timeout = 300 + ASSET_F18_KERNEL = Asset( + ('https://archives.fedoraproject.org/pub/archive/fedora/linux/' + 'releases/18/Fedora/x86_64/os/images/pxeboot/vmlinuz'), + '1a27cb42559ce29237ac186699d063556ad69c8349d732bb1bd8d614e5a8cc2e') + + ASSET_F28_KERNEL = Asset( + ('https://archives.fedoraproject.org/pub/archive/fedora/linux/' + 'releases/28/Everything/x86_64/os/images/pxeboot/vmlinuz'), + 'd05909c9d4a742a6fcc84dcc0361009e4611769619cc187a07107579a035f24e') + def test_with_2gib_file_should_exit_error_msg_with_linux_v3_6(self): """ Pretends to boot QEMU with an initrd file with size of 2GiB @@ -33,10 +42,8 @@ class LinuxInitrd(QemuSystemTest): Fedora-18 shipped with linux-3.6 which have not supported xloadflags cannot support more than 2GiB initrd. """ - kernel_url = ('https://archives.fedoraproject.org/pub/archive/fedora/li' - 'nux/releases/18/Fedora/x86_64/os/images/pxeboot/vmlinuz') - kernel_hash = '41464f68efe42b9991250bed86c7081d2ccdbb21' - kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) + self.set_machine('pc') + kernel_path = self.ASSET_F18_KERNEL.fetch() max_size = 2 * (1024 ** 3) - 1 with tempfile.NamedTemporaryFile() as initrd: @@ -56,16 +63,11 @@ class LinuxInitrd(QemuSystemTest): @skipUnless(os.getenv('QEMU_TEST_FLAKY_TESTS'), 'Test is unstable on GitLab') def test_with_2gib_file_should_work_with_linux_v4_16(self): """ - :avocado: tags=flaky - QEMU has supported up to 4 GiB initrd for recent kernel Expect guest can reach 'Unpacking initramfs...' """ - kernel_url = ('https://archives.fedoraproject.org/pub/archive/fedora' - '/linux/releases/28/Everything/x86_64/os/images/pxeboot/' - 'vmlinuz') - kernel_hash = '238e083e114c48200f80d889f7e32eeb2793e02a' - kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) + self.set_machine('pc') + kernel_path = self.ASSET_F28_KERNEL.fetch() max_size = 2 * (1024 ** 3) + 1 with tempfile.NamedTemporaryFile() as initrd: @@ -89,3 +91,6 @@ class LinuxInitrd(QemuSystemTest): break if 'Kernel panic - not syncing' in msg: self.fail("Kernel panic reached") + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/avocado/machine_loongarch.py b/tests/functional/test_loongarch64_virt.py index 8de308f2d6..2b8baa2c2a 100644..100755 --- a/tests/avocado/machine_loongarch.py +++ b/tests/functional/test_loongarch64_virt.py @@ -1,3 +1,5 @@ +#!/usr/bin/env python3 +# # SPDX-License-Identifier: GPL-2.0-or-later # # LoongArch virt test. @@ -5,15 +7,28 @@ # Copyright (c) 2023 Loongson Technology Corporation Limited # -from avocado_qemu import QemuSystemTest -from avocado_qemu import exec_command_and_wait_for_pattern -from avocado_qemu import wait_for_console_pattern +from qemu_test import QemuSystemTest, Asset +from qemu_test import exec_command_and_wait_for_pattern +from qemu_test import wait_for_console_pattern class LoongArchMachine(QemuSystemTest): KERNEL_COMMON_COMMAND_LINE = 'printk.time=0 ' timeout = 120 + ASSET_KERNEL = Asset( + ('https://github.com/yangxiaojuan-loongson/qemu-binary/' + 'releases/download/2024-05-30/vmlinuz.efi'), + '08b88a45f48a5fd92260bae895be4e5175be2397481a6f7821b9f39b2965b79e') + ASSET_INITRD = Asset( + ('https://github.com/yangxiaojuan-loongson/qemu-binary/' + 'releases/download/2024-05-30/ramdisk'), + '03d6fb6f8ee64ecac961120a0bdacf741f17b3bee2141f17fa01908c8baf176a') + ASSET_BIOS = Asset( + ('https://github.com/yangxiaojuan-loongson/qemu-binary/' + 'releases/download/2024-05-30/QEMU_EFI.fd'), + '937c1e7815e2340150c194a9f8f0474259038a3d7b8845ed62cc08163c46bea1') + def wait_for_console_pattern(self, success_message, vm=None): wait_for_console_pattern(self, success_message, failure_message='Kernel panic - not syncing', @@ -21,25 +36,11 @@ class LoongArchMachine(QemuSystemTest): def test_loongarch64_devices(self): - """ - :avocado: tags=arch:loongarch64 - :avocado: tags=machine:virt - """ - - kernel_url = ('https://github.com/yangxiaojuan-loongson/qemu-binary/' - 'releases/download/2024-05-30/vmlinuz.efi') - kernel_hash = '951b485b16e3788b6db03a3e1793c067009e31a2' - kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) + self.set_machine('virt') - initrd_url = ('https://github.com/yangxiaojuan-loongson/qemu-binary/' - 'releases/download/2024-05-30/ramdisk') - initrd_hash = 'c67658d9b2a447ce7db2f73ba3d373c9b2b90ab2' - initrd_path = self.fetch_asset(initrd_url, asset_hash=initrd_hash) - - bios_url = ('https://github.com/yangxiaojuan-loongson/qemu-binary/' - 'releases/download/2024-05-30/QEMU_EFI.fd') - bios_hash = ('f4d0966b5117d4cd82327c050dd668741046be69') - bios_path = self.fetch_asset(bios_url, asset_hash=bios_hash) + kernel_path = self.ASSET_KERNEL.fetch() + initrd_path = self.ASSET_INITRD.fetch() + bios_path = self.ASSET_BIOS.fetch() self.vm.set_console() kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + @@ -56,3 +57,6 @@ class LoongArchMachine(QemuSystemTest): self.wait_for_console_pattern('Run /sbin/init as init process') exec_command_and_wait_for_pattern(self, 'cat /proc/cpuinfo', 'processor : 3') + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/avocado/machine_m68k_nextcube.py b/tests/functional/test_m68k_nextcube.py index 1f3c883910..89385a134a 100644..100755 --- a/tests/avocado/machine_m68k_nextcube.py +++ b/tests/functional/test_m68k_nextcube.py @@ -1,3 +1,5 @@ +#!/usr/bin/env python3 +# # Functional test that boots a VM and run OCR on the framebuffer # # Copyright (c) 2019 Philippe Mathieu-Daudé <f4bug@amsat.org> @@ -8,10 +10,10 @@ import os import time -from avocado_qemu import QemuSystemTest -from avocado import skipUnless +from qemu_test import QemuSystemTest, Asset +from unittest import skipUnless -from tesseract_utils import tesseract_available, tesseract_ocr +from qemu_test.tesseract import tesseract_available, tesseract_ocr PIL_AVAILABLE = True try: @@ -21,19 +23,15 @@ except ImportError: class NextCubeMachine(QemuSystemTest): - """ - :avocado: tags=arch:m68k - :avocado: tags=machine:next-cube - :avocado: tags=device:framebuffer - """ timeout = 15 + ASSET_ROM = Asset(('https://sourceforge.net/p/previous/code/1350/tree/' + 'trunk/src/Rev_2.5_v66.BIN?format=raw'), + '1b753890b67095b73e104c939ddf62eca9e7d0aedde5108e3893b0ed9d8000a4') + def check_bootrom_framebuffer(self, screenshot_path): - rom_url = ('https://sourceforge.net/p/previous/code/1350/tree/' - 'trunk/src/Rev_2.5_v66.BIN?format=raw') - rom_hash = 'b3534796abae238a0111299fc406a9349f7fee24' - rom_path = self.fetch_asset(rom_url, asset_hash=rom_hash) + rom_path = self.ASSET_ROM.fetch() self.vm.add_args('-bios', rom_path) self.vm.launch() @@ -48,6 +46,7 @@ class NextCubeMachine(QemuSystemTest): @skipUnless(PIL_AVAILABLE, 'Python PIL not installed') def test_bootrom_framebuffer_size(self): + self.set_machine('next-cube') screenshot_path = os.path.join(self.workdir, "dump.ppm") self.check_bootrom_framebuffer(screenshot_path) @@ -60,11 +59,15 @@ class NextCubeMachine(QemuSystemTest): # that it is still alpha-level software. @skipUnless(tesseract_available(4), 'tesseract OCR tool not available') def test_bootrom_framebuffer_ocr_with_tesseract(self): + self.set_machine('next-cube') screenshot_path = os.path.join(self.workdir, "dump.ppm") self.check_bootrom_framebuffer(screenshot_path) - lines = tesseract_ocr(screenshot_path, tesseract_version=4) + lines = tesseract_ocr(screenshot_path) text = '\n'.join(lines) self.assertIn('Testing the FPU', text) self.assertIn('System test failed. Error code', text) self.assertIn('Boot command', text) self.assertIn('Next>', text) + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/avocado/mem-addr-space-check.py b/tests/functional/test_mem_addr_space.py index d3974599f4..bb0cf062ca 100644..100755 --- a/tests/avocado/mem-addr-space-check.py +++ b/tests/functional/test_mem_addr_space.py @@ -1,3 +1,5 @@ +#!/usr/bin/env python3 +# # Check for crash when using memory beyond the available guest processor # address space. # @@ -8,7 +10,7 @@ # # SPDX-License-Identifier: GPL-2.0-or-later -from avocado_qemu import QemuSystemTest +from qemu_test import QemuSystemTest import time class MemAddrCheck(QemuSystemTest): @@ -22,9 +24,6 @@ class MemAddrCheck(QemuSystemTest): # for all 32-bit cases, pci64_hole_size is 0. def test_phybits_low_pse36(self): """ - :avocado: tags=machine:q35 - :avocado: tags=arch:x86_64 - With pse36 feature ON, a processor has 36 bits of addressing. So it can access up to a maximum of 64GiB of memory. Memory hotplug region begins at 4 GiB boundary when "above_4g_mem_size" is 0 (this would be true when @@ -52,9 +51,6 @@ class MemAddrCheck(QemuSystemTest): def test_phybits_low_pae(self): """ - :avocado: tags=machine:q35 - :avocado: tags=arch:x86_64 - With pae feature ON, a processor has 36 bits of addressing. So it can access up to a maximum of 64GiB of memory. Rest is the same as the case with pse36 above. @@ -72,9 +68,6 @@ class MemAddrCheck(QemuSystemTest): def test_phybits_ok_pentium_pse36(self): """ - :avocado: tags=machine:q35 - :avocado: tags=arch:x86_64 - Setting maxmem to 59.5G and making sure that QEMU can start with the same options as the failing case above with pse36 cpu feature. """ @@ -91,9 +84,6 @@ class MemAddrCheck(QemuSystemTest): def test_phybits_ok_pentium_pae(self): """ - :avocado: tags=machine:q35 - :avocado: tags=arch:x86_64 - Test is same as above but now with pae cpu feature turned on. Setting maxmem to 59.5G and making sure that QEMU can start fine with the same options as the case above. @@ -111,9 +101,6 @@ class MemAddrCheck(QemuSystemTest): def test_phybits_ok_pentium2(self): """ - :avocado: tags=machine:q35 - :avocado: tags=arch:x86_64 - Pentium2 has 36 bits of addressing, so its same as pentium with pse36 ON. """ @@ -130,9 +117,6 @@ class MemAddrCheck(QemuSystemTest): def test_phybits_low_nonpse36(self): """ - :avocado: tags=machine:q35 - :avocado: tags=arch:x86_64 - Pentium processor has 32 bits of addressing without pse36 or pae so it can access physical address up to 4 GiB. Setting maxmem to 4 GiB should make QEMU fail to start with "phys-bits too low" @@ -153,9 +137,6 @@ class MemAddrCheck(QemuSystemTest): # now lets test some 64-bit CPU cases. def test_phybits_low_tcg_q35_70_amd(self): """ - :avocado: tags=machine:q35 - :avocado: tags=arch:x86_64 - For q35 7.1 machines and above, there is a HT window that starts at 1024 GiB and ends at 1 TiB - 1. If the max GPA falls in this range, "above_4G" memory is adjusted to start at 1 TiB boundary for AMD cpus @@ -182,9 +163,6 @@ class MemAddrCheck(QemuSystemTest): def test_phybits_low_tcg_q35_71_amd(self): """ - :avocado: tags=machine:q35 - :avocado: tags=arch:x86_64 - AMD_HT_START is defined to be at 1012 GiB. So for q35 machines version > 7.0 and AMD cpus, instead of 1024 GiB limit for 40 bit processor address space, it has to be 1012 GiB , that is 12 GiB @@ -205,9 +183,6 @@ class MemAddrCheck(QemuSystemTest): def test_phybits_ok_tcg_q35_70_amd(self): """ - :avocado: tags=machine:q35 - :avocado: tags=arch:x86_64 - Same as q35-7.0 AMD case except that here we check that QEMU can successfully start when maxmem is < 988G. """ @@ -224,9 +199,6 @@ class MemAddrCheck(QemuSystemTest): def test_phybits_ok_tcg_q35_71_amd(self): """ - :avocado: tags=machine:q35 - :avocado: tags=arch:x86_64 - Same as q35-7.1 AMD case except that here we check that QEMU can successfully start when maxmem is < 976G. """ @@ -243,9 +215,6 @@ class MemAddrCheck(QemuSystemTest): def test_phybits_ok_tcg_q35_71_intel(self): """ - :avocado: tags=machine:q35 - :avocado: tags=arch:x86_64 - Same parameters as test_phybits_low_tcg_q35_71_amd() but use Intel cpu instead. QEMU should start fine in this case as "above_4G" memory starts at 4G. @@ -264,9 +233,6 @@ class MemAddrCheck(QemuSystemTest): def test_phybits_low_tcg_q35_71_amd_41bits(self): """ - :avocado: tags=machine:q35 - :avocado: tags=arch:x86_64 - AMD processor with 41 bits. Max cpu hw address = 2 TiB. By setting maxram above 1012 GiB - 32 GiB - 4 GiB = 976 GiB, we can force "above_4G" memory to start at 1 TiB for q35-7.1 machines @@ -291,9 +257,6 @@ class MemAddrCheck(QemuSystemTest): def test_phybits_ok_tcg_q35_71_amd_41bits(self): """ - :avocado: tags=machine:q35 - :avocado: tags=arch:x86_64 - AMD processor with 41 bits. Max cpu hw address = 2 TiB. Same as above but by setting maxram between 976 GiB and 992 Gib, QEMU should start fine. @@ -312,9 +275,6 @@ class MemAddrCheck(QemuSystemTest): def test_phybits_low_tcg_q35_intel_cxl(self): """ - :avocado: tags=machine:q35 - :avocado: tags=arch:x86_64 - cxl memory window starts after memory device range. Here, we use 1 GiB of cxl window memory. 4G_mem end aligns at 4G. pci64_hole is 32 GiB and starts after the cxl memory window. @@ -335,9 +295,6 @@ class MemAddrCheck(QemuSystemTest): def test_phybits_ok_tcg_q35_intel_cxl(self): """ - :avocado: tags=machine:q35 - :avocado: tags=arch:x86_64 - Same as above but here we do not reserve any cxl memory window. Hence, with the exact same parameters as above, QEMU should start fine even with cxl enabled. @@ -352,3 +309,6 @@ class MemAddrCheck(QemuSystemTest): time.sleep(self.DELAY_Q35_BOOT_SEQUENCE) self.vm.shutdown() self.assertNotRegex(self.vm.get_log(), r'phys-bits too low') + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/functional/test_microblaze_s3adsp1800.py b/tests/functional/test_microblaze_s3adsp1800.py new file mode 100755 index 0000000000..4f692ffdb1 --- /dev/null +++ b/tests/functional/test_microblaze_s3adsp1800.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python3 +# +# Functional test that boots a microblaze Linux kernel and checks the console +# +# Copyright (c) 2018, 2021 Red Hat, Inc. +# +# This work is licensed under the terms of the GNU GPL, version 2 or +# later. See the COPYING file in the top-level directory. + +import time +from qemu_test import exec_command, exec_command_and_wait_for_pattern +from qemu_test import QemuSystemTest, Asset +from qemu_test import wait_for_console_pattern +from qemu_test.utils import archive_extract + +class MicroblazeMachine(QemuSystemTest): + + timeout = 90 + + ASSET_IMAGE = Asset( + ('https://qemu-advcal.gitlab.io/qac-best-of-multiarch/download/' + 'day17.tar.xz'), + '3ba7439dfbea7af4876662c97f8e1f0cdad9231fc166e4861d17042489270057') + + def test_microblaze_s3adsp1800(self): + self.set_machine('petalogix-s3adsp1800') + file_path = self.ASSET_IMAGE.fetch() + archive_extract(file_path, self.workdir) + self.vm.set_console() + self.vm.add_args('-kernel', self.workdir + '/day17/ballerina.bin') + self.vm.launch() + wait_for_console_pattern(self, 'This architecture does not have ' + 'kernel memory protection') + # Note: + # The kernel sometimes gets stuck after the "This architecture ..." + # message, that's why we don't test for a later string here. This + # needs some investigation by a microblaze wizard one day... + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/functional/test_microblazeel_s3adsp1800.py b/tests/functional/test_microblazeel_s3adsp1800.py new file mode 100755 index 0000000000..faa3927f2e --- /dev/null +++ b/tests/functional/test_microblazeel_s3adsp1800.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python3 +# +# Functional test that boots a microblaze Linux kernel and checks the console +# +# Copyright (c) 2018, 2021 Red Hat, Inc. +# +# This work is licensed under the terms of the GNU GPL, version 2 or +# later. See the COPYING file in the top-level directory. + +import time +from qemu_test import exec_command, exec_command_and_wait_for_pattern +from qemu_test import QemuSystemTest, Asset +from qemu_test import wait_for_console_pattern +from qemu_test.utils import archive_extract + +class MicroblazeelMachine(QemuSystemTest): + + timeout = 90 + + ASSET_IMAGE = Asset( + ('http://www.qemu-advent-calendar.org/2023/download/day13.tar.gz'), + 'b9b3d43c5dd79db88ada495cc6e0d1f591153fe41355e925d791fbf44de50c22') + + def test_microblazeel_s3adsp1800(self): + self.require_netdev('user') + self.set_machine('petalogix-s3adsp1800') + file_path = self.ASSET_IMAGE.fetch() + archive_extract(file_path, self.workdir) + self.vm.set_console() + self.vm.add_args('-kernel', self.workdir + '/day13/xmaton.bin') + self.vm.add_args('-nic', 'user,tftp=' + self.workdir + '/day13/') + self.vm.launch() + wait_for_console_pattern(self, 'QEMU Advent Calendar 2023') + time.sleep(0.1) + exec_command(self, 'root') + time.sleep(0.1) + exec_command_and_wait_for_pattern(self, + 'tftp -g -r xmaton.png 10.0.2.2 ; md5sum xmaton.png', + '821cd3cab8efd16ad6ee5acc3642a8ea') + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/avocado/machine_mips_fuloong2e.py b/tests/functional/test_mips64el_fuloong2e.py index 89291f47b2..7688a32713 100644..100755 --- a/tests/avocado/machine_mips_fuloong2e.py +++ b/tests/functional/test_mips64el_fuloong2e.py @@ -1,3 +1,5 @@ +#!/usr/bin/env python3 +# # Functional tests for the Lemote Fuloong-2E machine. # # Copyright (c) 2019 Philippe Mathieu-Daudé <f4bug@amsat.org> @@ -8,35 +10,36 @@ # SPDX-License-Identifier: GPL-2.0-or-later import os +import subprocess -from avocado import skipUnless -from avocado_qemu import QemuSystemTest -from avocado_qemu import wait_for_console_pattern +from qemu_test import QemuSystemTest +from qemu_test import wait_for_console_pattern +from unittest import skipUnless class MipsFuloong2e(QemuSystemTest): timeout = 60 - @skipUnless(os.getenv('AVOCADO_ALLOW_UNTRUSTED_CODE'), 'untrusted code') + @skipUnless(os.getenv('QEMU_TEST_ALLOW_UNTRUSTED_CODE'), 'untrusted code') @skipUnless(os.getenv('RESCUE_YL_PATH'), 'RESCUE_YL_PATH not available') - def test_linux_kernel_isa_serial(self): - """ - :avocado: tags=arch:mips64el - :avocado: tags=machine:fuloong2e - :avocado: tags=endian:little - :avocado: tags=device:bonito64 - :avocado: tags=device:via686b - """ + def test_linux_kernel_2_6_27_isa_serial(self): # Recovery system for the Yeeloong laptop # (enough to test the fuloong2e southbridge, accessing its ISA bus) # http://dev.lemote.com/files/resource/download/rescue/rescue-yl - kernel_hash = 'ec4d1bd89a8439c41033ca63db60160cc6d6f09a' - kernel_path = self.fetch_asset('file://' + os.getenv('RESCUE_YL_PATH'), - asset_hash=kernel_hash) + sha = 'ab588d3316777c62cc81baa20ac92e98b01955c244dff3794b711bc34e26e51d' + kernel_path = os.getenv('RESCUE_YL_PATH') + output = subprocess.check_output(['sha256sum', kernel_path]) + checksum = output.split()[0] + assert checksum.decode("utf-8") == sha + self.set_machine('fuloong2e') self.vm.set_console() self.vm.add_args('-kernel', kernel_path) self.vm.launch() wait_for_console_pattern(self, 'Linux version 2.6.27.7lemote') cpu_revision = 'CPU revision is: 00006302 (ICT Loongson-2)' wait_for_console_pattern(self, cpu_revision) + + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/functional/test_mips64el_loongson3v.py b/tests/functional/test_mips64el_loongson3v.py new file mode 100755 index 0000000000..55d62928c7 --- /dev/null +++ b/tests/functional/test_mips64el_loongson3v.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python3 +# +# Functional tests for the Generic Loongson-3 Platform. +# +# Copyright (c) 2021 Jiaxun Yang <jiaxun.yang@flygoat.com> +# +# This work is licensed under the terms of the GNU GPL, version 2 or later. +# See the COPYING file in the top-level directory. +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import os +import time + +from unittest import skipUnless +from qemu_test import QemuSystemTest, Asset +from qemu_test import wait_for_console_pattern + +class MipsLoongson3v(QemuSystemTest): + timeout = 60 + + ASSET_PMON = Asset( + ('https://github.com/loongson-community/pmon/' + 'releases/download/20210112/pmon-3avirt.bin'), + 'fcdf6bb2cb7885a4a62f31fcb0d5e368bac7b6cea28f40c6dfa678af22fea20a') + + @skipUnless(os.getenv('QEMU_TEST_ALLOW_UNTRUSTED_CODE'), 'untrusted code') + def test_pmon_serial_console(self): + self.set_machine('loongson3-virt') + + pmon_path = self.ASSET_PMON.fetch() + + self.vm.set_console() + self.vm.add_args('-bios', pmon_path) + self.vm.launch() + wait_for_console_pattern(self, 'CPU GODSON3 BogoMIPS:') + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/avocado/netdev-ethtool.py b/tests/functional/test_netdev_ethtool.py index 5f33288f81..d5b911c918 100644..100755 --- a/tests/avocado/netdev-ethtool.py +++ b/tests/functional/test_netdev_ethtool.py @@ -1,3 +1,5 @@ +#!/usr/bin/env python3 +# # ethtool tests for emulated network devices # # This test leverages ethtool's --test sequence to validate network @@ -5,39 +7,33 @@ # # SPDX-License-Identifier: GPL-2.0-or-late -from avocado import skip -from avocado_qemu import QemuSystemTest -from avocado_qemu import wait_for_console_pattern +from unittest import skip +from qemu_test import QemuSystemTest, Asset +from qemu_test import wait_for_console_pattern class NetDevEthtool(QemuSystemTest): - """ - :avocado: tags=arch:x86_64 - :avocado: tags=machine:q35 - """ # Runs in about 17s under KVM, 19s under TCG, 25s under GCOV timeout = 45 # Fetch assets from the netdev-ethtool subdir of my shared test # images directory on fileserver.linaro.org. - def get_asset(self, name, sha1): - base_url = ('https://fileserver.linaro.org/s/' - 'kE4nCFLdQcoBF9t/download?' - 'path=%2Fnetdev-ethtool&files=' ) - url = base_url + name - # use explicit name rather than failing to neatly parse the - # URL into a unique one - return self.fetch_asset(name=name, locations=(url), asset_hash=sha1) + ASSET_BASEURL = ('https://fileserver.linaro.org/s/kE4nCFLdQcoBF9t/' + 'download?path=%2Fnetdev-ethtool&files=') + ASSET_BZIMAGE = Asset( + ASSET_BASEURL + "bzImage", + "ed62ee06ea620b1035747f3f66a5e9fc5d3096b29f75562ada888b04cd1c4baf") + ASSET_ROOTFS = Asset( + ASSET_BASEURL + "rootfs.squashfs", + "8f0207e3c4d40832ae73c1a927e42ca30ccb1e71f047acb6ddb161ba422934e6") def common_test_code(self, netdev, extra_args=None): + self.set_machine('q35') # This custom kernel has drivers for all the supported network # devices we can emulate in QEMU - kernel = self.get_asset("bzImage", - "33469d7802732d5815226166581442395cb289e2") - - rootfs = self.get_asset("rootfs.squashfs", - "9793cea7021414ae844bda51f558bd6565b50cdc") + kernel = self.ASSET_BZIMAGE.fetch() + rootfs = self.ASSET_ROOTFS.fetch() append = 'printk.time=0 console=ttyS0 ' append += 'root=/dev/sr0 rootfstype=squashfs ' @@ -68,15 +64,9 @@ class NetDevEthtool(QemuSystemTest): self.vm.kill() def test_igb(self): - """ - :avocado: tags=device:igb - """ self.common_test_code("igb") def test_igb_nomsi(self): - """ - :avocado: tags=device:igb - """ self.common_test_code("igb", "pci=nomsi") # It seems the other popular cards we model in QEMU currently fail @@ -88,14 +78,11 @@ class NetDevEthtool(QemuSystemTest): @skip("Incomplete reg 0x00178 support") def test_e1000(self): - """ - :avocado: tags=device:e1000 - """ self.common_test_code("e1000") @skip("Incomplete reg 0x00178 support") def test_i82550(self): - """ - :avocado: tags=device:i82550 - """ self.common_test_code("i82550") + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/avocado/pc_cpu_hotplug_props.py b/tests/functional/test_pc_cpu_hotplug_props.py index 4bd3e02665..9d5a37cb17 100644..100755 --- a/tests/avocado/pc_cpu_hotplug_props.py +++ b/tests/functional/test_pc_cpu_hotplug_props.py @@ -1,3 +1,4 @@ +#!/usr/bin/env python3 # # Ensure CPU die-id can be omitted on -device # @@ -20,16 +21,16 @@ # License along with this library; if not, see <http://www.gnu.org/licenses/>. # -from avocado_qemu import QemuSystemTest +from qemu_test import QemuSystemTest class OmittedCPUProps(QemuSystemTest): - """ - :avocado: tags=arch:x86_64 - :avocado: tags=cpu:qemu64 - """ + def test_no_die_id(self): self.vm.add_args('-nodefaults', '-S') self.vm.add_args('-smp', '1,sockets=2,cores=2,threads=2,maxcpus=8') self.vm.add_args('-device', 'qemu64-x86_64-cpu,socket-id=1,core-id=0,thread-id=0') self.vm.launch() self.assertEqual(len(self.vm.cmd('query-cpus-fast')), 2) + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/avocado/ppc_hv_tests.py b/tests/functional/test_ppc64_hv.py index 0e83bbac71..1a6e4b6d07 100644..100755 --- a/tests/avocado/ppc_hv_tests.py +++ b/tests/functional/test_ppc64_hv.py @@ -1,3 +1,5 @@ +#!/usr/bin/env python3 +# # Tests that specifically try to exercise hypervisor features of the # target machines. powernv supports the Power hypervisor ISA, and # pseries supports the nested-HV hypervisor spec. @@ -7,10 +9,9 @@ # This work is licensed under the terms of the GNU GPL, version 2 or # later. See the COPYING file in the top-level directory. -from avocado import skipIf, skipUnless -from avocado.utils import archive -from avocado_qemu import QemuSystemTest -from avocado_qemu import wait_for_console_pattern, exec_command +from unittest import skipIf, skipUnless +from qemu_test import QemuSystemTest, Asset +from qemu_test import wait_for_console_pattern, exec_command import os import time import subprocess @@ -45,8 +46,7 @@ def missing_deps(): # QEMU already installed and use that. # XXX: The order of these tests seems to matter, see git blame. @skipIf(missing_deps(), 'dependencies (%s) not installed' % ','.join(deps)) -@skipUnless(os.getenv('AVOCADO_ALLOW_LARGE_STORAGE'), 'storage limited') -@skipUnless(os.getenv('SPEED') == 'slow', 'runtime limited') +@skipUnless(os.getenv('QEMU_TEST_ALLOW_LARGE_STORAGE'), 'storage limited') class HypervisorTest(QemuSystemTest): timeout = 1000 @@ -54,6 +54,11 @@ class HypervisorTest(QemuSystemTest): panic_message = 'Kernel panic - not syncing' good_message = 'VFS: Cannot open root device' + ASSET_ISO = Asset( + ('https://dl-cdn.alpinelinux.org/alpine/v3.18/' + 'releases/ppc64le/alpine-standard-3.18.4-ppc64le.iso'), + 'c26b8d3e17c2f3f0fed02b4b1296589c2390e6d5548610099af75300edd7b3ff') + def extract_from_iso(self, iso, path): """ Extracts a file from an iso file into the test workdir @@ -72,6 +77,7 @@ class HypervisorTest(QemuSystemTest): subprocess.run(cmd.split(), stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + os.chmod(filename, 0o600) os.chdir(cwd) # Return complete path to extracted file. Because callers to @@ -83,16 +89,9 @@ class HypervisorTest(QemuSystemTest): def setUp(self): super().setUp() - iso_url = ('https://dl-cdn.alpinelinux.org/alpine/v3.18/releases/ppc64le/alpine-standard-3.18.4-ppc64le.iso') - - # Alpine use sha256 so I recalculated this myself - iso_sha256 = 'c26b8d3e17c2f3f0fed02b4b1296589c2390e6d5548610099af75300edd7b3ff' - iso_path = self.fetch_asset(iso_url, asset_hash=iso_sha256, - algorithm = "sha256") - - self.iso_path = iso_path - self.vmlinuz = self.extract_from_iso(iso_path, '/boot/vmlinuz-lts') - self.initramfs = self.extract_from_iso(iso_path, '/boot/initramfs-lts') + self.iso_path = self.ASSET_ISO.fetch() + self.vmlinuz = self.extract_from_iso(self.iso_path, '/boot/vmlinuz-lts') + self.initramfs = self.extract_from_iso(self.iso_path, '/boot/initramfs-lts') def do_start_alpine(self): self.vm.set_console() @@ -158,12 +157,8 @@ class HypervisorTest(QemuSystemTest): wait_for_console_pattern(self, 'alpine:~#') def test_hv_pseries(self): - """ - :avocado: tags=arch:ppc64 - :avocado: tags=machine:pseries - :avocado: tags=accel:tcg - """ self.require_accelerator("tcg") + self.set_machine('pseries') self.vm.add_args("-accel", "tcg,thread=multi") self.vm.add_args('-device', 'nvme,serial=1234,drive=drive0') self.vm.add_args("-machine", "x-vof=on,cap-nested-hv=on") @@ -173,12 +168,8 @@ class HypervisorTest(QemuSystemTest): self.do_stop_alpine() def test_hv_pseries_kvm(self): - """ - :avocado: tags=arch:ppc64 - :avocado: tags=machine:pseries - :avocado: tags=accel:kvm - """ self.require_accelerator("kvm") + self.set_machine('pseries') self.vm.add_args("-accel", "kvm") self.vm.add_args('-device', 'nvme,serial=1234,drive=drive0') self.vm.add_args("-machine", "x-vof=on,cap-nested-hv=on,cap-ccf-assist=off") @@ -188,12 +179,8 @@ class HypervisorTest(QemuSystemTest): self.do_stop_alpine() def test_hv_powernv(self): - """ - :avocado: tags=arch:ppc64 - :avocado: tags=machine:powernv - :avocado: tags=accel:tcg - """ self.require_accelerator("tcg") + self.set_machine('powernv') self.vm.add_args("-accel", "tcg,thread=multi") self.vm.add_args('-device', 'nvme,bus=pcie.2,addr=0x0,serial=1234,drive=drive0', '-device', 'e1000e,netdev=net0,mac=C0:FF:EE:00:00:02,bus=pcie.0,addr=0x0', @@ -203,3 +190,6 @@ class HypervisorTest(QemuSystemTest): self.do_test_kvm() self.do_test_kvm(True) self.do_stop_alpine() + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/avocado/ppc_powernv.py b/tests/functional/test_ppc64_powernv.py index 4342941d5d..67497d6404 100644..100755 --- a/tests/avocado/ppc_powernv.py +++ b/tests/functional/test_ppc64_powernv.py @@ -1,3 +1,5 @@ +#!/usr/bin/env python3 +# # Test that Linux kernel boots on ppc powernv machines and check the console # # Copyright (c) 2018, 2020 Red Hat, Inc. @@ -5,9 +7,8 @@ # This work is licensed under the terms of the GNU GPL, version 2 or # later. See the COPYING file in the top-level directory. -from avocado.utils import archive -from avocado_qemu import QemuSystemTest -from avocado_qemu import wait_for_console_pattern +from qemu_test import QemuSystemTest, Asset +from qemu_test import wait_for_console_pattern class powernvMachine(QemuSystemTest): @@ -16,13 +17,14 @@ class powernvMachine(QemuSystemTest): panic_message = 'Kernel panic - not syncing' good_message = 'VFS: Cannot open root device' + ASSET_KERNEL = Asset( + ('https://archives.fedoraproject.org/pub/archive/fedora-secondary/' + 'releases/29/Everything/ppc64le/os/ppc/ppc64/vmlinuz'), + '383c2f5c23bc0d9d32680c3924d3fd7ee25cc5ef97091ac1aa5e1d853422fc5f') + def do_test_linux_boot(self, command_line = KERNEL_COMMON_COMMAND_LINE): self.require_accelerator("tcg") - kernel_url = ('https://archives.fedoraproject.org/pub/archive' - '/fedora-secondary/releases/29/Everything/ppc64le/os' - '/ppc/ppc64/vmlinuz') - kernel_hash = '3fe04abfc852b66653b8c3c897a59a689270bc77' - kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) + kernel_path = self.ASSET_KERNEL.fetch() self.vm.set_console() self.vm.add_args('-kernel', kernel_path, @@ -30,23 +32,13 @@ class powernvMachine(QemuSystemTest): self.vm.launch() def test_linux_boot(self): - """ - :avocado: tags=arch:ppc64 - :avocado: tags=machine:powernv - :avocado: tags=accel:tcg - """ - + self.set_machine('powernv') self.do_test_linux_boot() console_pattern = 'VFS: Cannot open root device' wait_for_console_pattern(self, console_pattern, self.panic_message) def test_linux_smp_boot(self): - """ - :avocado: tags=arch:ppc64 - :avocado: tags=machine:powernv - :avocado: tags=accel:tcg - """ - + self.set_machine('powernv') self.vm.add_args('-smp', '4') self.do_test_linux_boot() console_pattern = 'smp: Brought up 1 node, 4 CPUs' @@ -54,12 +46,7 @@ class powernvMachine(QemuSystemTest): wait_for_console_pattern(self, self.good_message, self.panic_message) def test_linux_smp_hpt_boot(self): - """ - :avocado: tags=arch:ppc64 - :avocado: tags=machine:powernv - :avocado: tags=accel:tcg - """ - + self.set_machine('powernv') self.vm.add_args('-smp', '4') self.do_test_linux_boot(self.KERNEL_COMMON_COMMAND_LINE + 'disable_radix') @@ -70,12 +57,7 @@ class powernvMachine(QemuSystemTest): wait_for_console_pattern(self, self.good_message, self.panic_message) def test_linux_smt_boot(self): - """ - :avocado: tags=arch:ppc64 - :avocado: tags=machine:powernv - :avocado: tags=accel:tcg - """ - + self.set_machine('powernv') self.vm.add_args('-smp', '4,threads=4') self.do_test_linux_boot() console_pattern = 'CPU maps initialized for 4 threads per core' @@ -85,12 +67,7 @@ class powernvMachine(QemuSystemTest): wait_for_console_pattern(self, self.good_message, self.panic_message) def test_linux_big_boot(self): - """ - :avocado: tags=arch:ppc64 - :avocado: tags=machine:powernv - :avocado: tags=accel:tcg - """ - + self.set_machine('powernv') self.vm.add_args('-smp', '16,threads=4,cores=2,sockets=2') # powernv does not support NUMA @@ -100,3 +77,6 @@ class powernvMachine(QemuSystemTest): console_pattern = 'smp: Brought up 2 nodes, 16 CPUs' wait_for_console_pattern(self, console_pattern, self.panic_message) wait_for_console_pattern(self, self.good_message, self.panic_message) + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/avocado/ppc_pseries.py b/tests/functional/test_ppc64_pseries.py index 74aaa4ac4a..fdc404ed03 100644..100755 --- a/tests/avocado/ppc_pseries.py +++ b/tests/functional/test_ppc64_pseries.py @@ -1,3 +1,5 @@ +#!/usr/bin/env python3 +# # Test that Linux kernel boots on ppc machines and check the console # # Copyright (c) 2018, 2020 Red Hat, Inc. @@ -5,9 +7,8 @@ # This work is licensed under the terms of the GNU GPL, version 2 or # later. See the COPYING file in the top-level directory. -from avocado.utils import archive -from avocado_qemu import QemuSystemTest -from avocado_qemu import wait_for_console_pattern +from qemu_test import QemuSystemTest, Asset +from qemu_test import wait_for_console_pattern class pseriesMachine(QemuSystemTest): @@ -16,12 +17,13 @@ class pseriesMachine(QemuSystemTest): panic_message = 'Kernel panic - not syncing' good_message = 'VFS: Cannot open root device' + ASSET_KERNEL = Asset( + ('https://archives.fedoraproject.org/pub/archive/fedora-secondary/' + 'releases/29/Everything/ppc64le/os/ppc/ppc64/vmlinuz'), + '383c2f5c23bc0d9d32680c3924d3fd7ee25cc5ef97091ac1aa5e1d853422fc5f') + def do_test_ppc64_linux_boot(self, kernel_command_line = KERNEL_COMMON_COMMAND_LINE): - kernel_url = ('https://archives.fedoraproject.org/pub/archive' - '/fedora-secondary/releases/29/Everything/ppc64le/os' - '/ppc/ppc64/vmlinuz') - kernel_hash = '3fe04abfc852b66653b8c3c897a59a689270bc77' - kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) + kernel_path = self.ASSET_KERNEL.fetch() self.vm.set_console() self.vm.add_args('-kernel', kernel_path, @@ -29,32 +31,20 @@ class pseriesMachine(QemuSystemTest): self.vm.launch() def test_ppc64_vof_linux_boot(self): - """ - :avocado: tags=arch:ppc64 - :avocado: tags=machine:pseries - """ - + self.set_machine('pseries') self.vm.add_args('-machine', 'x-vof=on') self.do_test_ppc64_linux_boot() console_pattern = 'VFS: Cannot open root device' wait_for_console_pattern(self, console_pattern, self.panic_message) def test_ppc64_linux_boot(self): - """ - :avocado: tags=arch:ppc64 - :avocado: tags=machine:pseries - """ - + self.set_machine('pseries') self.do_test_ppc64_linux_boot() console_pattern = 'VFS: Cannot open root device' wait_for_console_pattern(self, console_pattern, self.panic_message) def test_ppc64_linux_smp_boot(self): - """ - :avocado: tags=arch:ppc64 - :avocado: tags=machine:pseries - """ - + self.set_machine('pseries') self.vm.add_args('-smp', '4') self.do_test_ppc64_linux_boot() console_pattern = 'smp: Brought up 1 node, 4 CPUs' @@ -62,11 +52,7 @@ class pseriesMachine(QemuSystemTest): wait_for_console_pattern(self, self.good_message, self.panic_message) def test_ppc64_linux_hpt_smp_boot(self): - """ - :avocado: tags=arch:ppc64 - :avocado: tags=machine:pseries - """ - + self.set_machine('pseries') self.vm.add_args('-smp', '4') self.do_test_ppc64_linux_boot(self.KERNEL_COMMON_COMMAND_LINE + 'disable_radix') @@ -77,11 +63,6 @@ class pseriesMachine(QemuSystemTest): wait_for_console_pattern(self, self.good_message, self.panic_message) def test_ppc64_linux_smt_boot(self): - """ - :avocado: tags=arch:ppc64 - :avocado: tags=machine:pseries - """ - self.vm.add_args('-smp', '4,threads=4') self.do_test_ppc64_linux_boot() console_pattern = 'CPU maps initialized for 4 threads per core' @@ -91,11 +72,7 @@ class pseriesMachine(QemuSystemTest): wait_for_console_pattern(self, self.good_message, self.panic_message) def test_ppc64_linux_big_boot(self): - """ - :avocado: tags=arch:ppc64 - :avocado: tags=machine:pseries - """ - + self.set_machine('pseries') self.vm.add_args('-smp', '16,threads=4,cores=2,sockets=2') self.vm.add_args('-m', '512M', '-object', 'memory-backend-ram,size=256M,id=m0', @@ -108,3 +85,6 @@ class pseriesMachine(QemuSystemTest): console_pattern = 'smp: Brought up 2 nodes, 16 CPUs' wait_for_console_pattern(self, console_pattern, self.panic_message) wait_for_console_pattern(self, self.good_message, self.panic_message) + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/avocado/ppc_405.py b/tests/functional/test_ppc_405.py index 4e7e01aa76..9851c03ee9 100644..100755 --- a/tests/avocado/ppc_405.py +++ b/tests/functional/test_ppc_405.py @@ -1,3 +1,5 @@ +#!/usr/bin/env python3 +# # Test that the U-Boot firmware boots on ppc 405 machines and check the console # # Copyright (c) 2021 Red Hat, Inc. @@ -5,20 +7,21 @@ # This work is licensed under the terms of the GNU GPL, version 2 or # later. See the COPYING file in the top-level directory. -from avocado.utils import archive -from avocado_qemu import QemuSystemTest -from avocado_qemu import wait_for_console_pattern -from avocado_qemu import exec_command_and_wait_for_pattern +from qemu_test import QemuSystemTest, Asset +from qemu_test import wait_for_console_pattern +from qemu_test import exec_command_and_wait_for_pattern class Ppc405Machine(QemuSystemTest): timeout = 90 + ASSET_UBOOT = Asset( + ('https://gitlab.com/huth/u-boot/-/raw/taihu-2021-10-09/' + 'u-boot-taihu.bin'), + 'a076bb6cdeaafa406330e51e074b66d8878d9036d67d4caa0137be03ee4c112c') + def do_test_ppc405(self): - uboot_url = ('https://gitlab.com/huth/u-boot/-/raw/' - 'taihu-2021-10-09/u-boot-taihu.bin') - uboot_hash = ('3208940e908a5edc7c03eab072c60f0dcfadc2ab'); - file_path = self.fetch_asset(uboot_url, asset_hash=uboot_hash) + file_path = self.ASSET_UBOOT.fetch() self.vm.set_console(console_index=1) self.vm.add_args('-bios', file_path) self.vm.launch() @@ -26,11 +29,9 @@ class Ppc405Machine(QemuSystemTest): exec_command_and_wait_for_pattern(self, 'reset', 'AMCC PowerPC 405EP') def test_ppc_ref405ep(self): - """ - :avocado: tags=arch:ppc - :avocado: tags=machine:ref405ep - :avocado: tags=cpu:405ep - :avocado: tags=accel:tcg - """ self.require_accelerator("tcg") + self.set_machine('ref405ep') self.do_test_ppc405() + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/avocado/ppc_prep_40p.py b/tests/functional/test_ppc_40p.py index d4f1eb7e1d..c64e876c1f 100644..100755 --- a/tests/avocado/ppc_prep_40p.py +++ b/tests/functional/test_ppc_40p.py @@ -1,3 +1,5 @@ +#!/usr/bin/env python3 +# # Functional test that boots a PReP/40p machine and checks its serial console. # # Copyright (c) Philippe Mathieu-Daudé <f4bug@amsat.org> @@ -7,39 +9,40 @@ import os -from avocado import skipUnless -from avocado_qemu import QemuSystemTest -from avocado_qemu import wait_for_console_pattern +from unittest import skipUnless +from qemu_test import QemuSystemTest, Asset +from qemu_test import wait_for_console_pattern class IbmPrep40pMachine(QemuSystemTest): timeout = 60 + ASSET_BIOS = Asset( + ('http://ftpmirror.your.org/pub/misc/' + 'ftp.software.ibm.com/rs6000/firmware/' + '7020-40p/P12H0456.IMG'), + 'd957f79c73f760d1455d2286fcd901ed6d06167320eb73511b478a939be25b3f') + ASSET_NETBSD40 = Asset( + ('https://archive.netbsd.org/pub/NetBSD-archive/' + 'NetBSD-4.0/prep/installation/floppy/generic_com0.fs'), + 'f86236e9d01b3f0dd0f5d3b8d5bbd40c68e78b4db560a108358f5ad58e636619') + ASSET_NETBSD71 = Asset( + ('https://archive.netbsd.org/pub/NetBSD-archive/' + 'NetBSD-7.1.2/iso/NetBSD-7.1.2-prep.iso'), + 'cc7cb290b06aaa839362deb7bd9f417ac5015557db24088508330f76c3f825ec') + # 12H0455 PPS Firmware Licensed Materials # Property of IBM (C) Copyright IBM Corp. 1994. # All rights reserved. # U.S. Government Users Restricted Rights - Use, duplication or disclosure # restricted by GSA ADP Schedule Contract with IBM Corp. - @skipUnless(os.getenv('AVOCADO_ALLOW_UNTRUSTED_CODE'), 'untrusted code') + @skipUnless(os.getenv('QEMU_TEST_ALLOW_UNTRUSTED_CODE'), 'untrusted code') def test_factory_firmware_and_netbsd(self): - """ - :avocado: tags=arch:ppc - :avocado: tags=machine:40p - :avocado: tags=os:netbsd - :avocado: tags=slowness:high - :avocado: tags=accel:tcg - """ + self.set_machine('40p') self.require_accelerator("tcg") - bios_url = ('http://ftpmirror.your.org/pub/misc/' - 'ftp.software.ibm.com/rs6000/firmware/' - '7020-40p/P12H0456.IMG') - bios_hash = '1775face4e6dc27f3a6ed955ef6eb331bf817f03' - bios_path = self.fetch_asset(bios_url, asset_hash=bios_hash) - drive_url = ('https://archive.netbsd.org/pub/NetBSD-archive/' - 'NetBSD-4.0/prep/installation/floppy/generic_com0.fs') - drive_hash = 'dbcfc09912e71bd5f0d82c7c1ee43082fb596ceb' - drive_path = self.fetch_asset(drive_url, asset_hash=drive_hash) + bios_path = self.ASSET_BIOS.fetch() + drive_path = self.ASSET_NETBSD40.fetch() self.vm.set_console() self.vm.add_args('-bios', bios_path, @@ -50,11 +53,7 @@ class IbmPrep40pMachine(QemuSystemTest): wait_for_console_pattern(self, 'Model: IBM PPS Model 6015') def test_openbios_192m(self): - """ - :avocado: tags=arch:ppc - :avocado: tags=machine:40p - :avocado: tags=accel:tcg - """ + self.set_machine('40p') self.require_accelerator("tcg") self.vm.set_console() self.vm.add_args('-m', '192') # test fw_cfg @@ -65,21 +64,15 @@ class IbmPrep40pMachine(QemuSystemTest): wait_for_console_pattern(self, '>> CPU type PowerPC,604') def test_openbios_and_netbsd(self): - """ - :avocado: tags=arch:ppc - :avocado: tags=machine:40p - :avocado: tags=os:netbsd - :avocado: tags=accel:tcg - """ + self.set_machine('40p') self.require_accelerator("tcg") - drive_url = ('https://archive.netbsd.org/pub/NetBSD-archive/' - 'NetBSD-7.1.2/iso/NetBSD-7.1.2-prep.iso') - drive_hash = 'ac6fa2707d888b36d6fa64de6e7fe48e' - drive_path = self.fetch_asset(drive_url, asset_hash=drive_hash, - algorithm='md5') + drive_path = self.ASSET_NETBSD71.fetch() self.vm.set_console() self.vm.add_args('-cdrom', drive_path, '-boot', 'd') self.vm.launch() wait_for_console_pattern(self, 'NetBSD/prep BOOT, Revision 1.9') + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/avocado/ppc_74xx.py b/tests/functional/test_ppc_74xx.py index f54757c243..5386016f26 100644..100755 --- a/tests/avocado/ppc_74xx.py +++ b/tests/functional/test_ppc_74xx.py @@ -1,3 +1,5 @@ +#!/usr/bin/env python3 +# # Smoke tests for 74xx cpus (aka G4). # # Copyright (c) 2021, IBM Corp. @@ -5,132 +7,120 @@ # This work is licensed under the terms of the GNU GPL, version 2 or # later. See the COPYING file in the top-level directory. -from avocado_qemu import QemuSystemTest -from avocado_qemu import wait_for_console_pattern +from qemu_test import QemuSystemTest +from qemu_test import wait_for_console_pattern class ppc74xxCpu(QemuSystemTest): - """ - :avocado: tags=arch:ppc - :avocado: tags=accel:tcg - """ + timeout = 5 def test_ppc_7400(self): - """ - :avocado: tags=cpu:7400 - """ self.require_accelerator("tcg") + self.set_machine('g3beige') self.vm.set_console() + self.vm.add_args('-cpu', '7400') self.vm.launch() wait_for_console_pattern(self, '>> OpenBIOS') wait_for_console_pattern(self, '>> CPU type PowerPC,G4') def test_ppc_7410(self): - """ - :avocado: tags=cpu:7410 - """ self.require_accelerator("tcg") + self.set_machine('g3beige') self.vm.set_console() + self.vm.add_args('-cpu', '7410') self.vm.launch() wait_for_console_pattern(self, '>> OpenBIOS') wait_for_console_pattern(self, '>> CPU type PowerPC,74xx') def test_ppc_7441(self): - """ - :avocado: tags=cpu:7441 - """ self.require_accelerator("tcg") + self.set_machine('g3beige') self.vm.set_console() + self.vm.add_args('-cpu', '7441') self.vm.launch() wait_for_console_pattern(self, '>> OpenBIOS') wait_for_console_pattern(self, '>> CPU type PowerPC,G4') def test_ppc_7445(self): - """ - :avocado: tags=cpu:7445 - """ self.require_accelerator("tcg") + self.set_machine('g3beige') self.vm.set_console() + self.vm.add_args('-cpu', '7445') self.vm.launch() wait_for_console_pattern(self, '>> OpenBIOS') wait_for_console_pattern(self, '>> CPU type PowerPC,G4') def test_ppc_7447(self): - """ - :avocado: tags=cpu:7447 - """ self.require_accelerator("tcg") + self.set_machine('g3beige') self.vm.set_console() + self.vm.add_args('-cpu', '7447') self.vm.launch() wait_for_console_pattern(self, '>> OpenBIOS') wait_for_console_pattern(self, '>> CPU type PowerPC,G4') def test_ppc_7447a(self): - """ - :avocado: tags=cpu:7447a - """ self.require_accelerator("tcg") + self.set_machine('g3beige') self.vm.set_console() + self.vm.add_args('-cpu', '7447a') self.vm.launch() wait_for_console_pattern(self, '>> OpenBIOS') wait_for_console_pattern(self, '>> CPU type PowerPC,G4') def test_ppc_7448(self): - """ - :avocado: tags=cpu:7448 - """ self.require_accelerator("tcg") + self.set_machine('g3beige') self.vm.set_console() + self.vm.add_args('-cpu', '7448') self.vm.launch() wait_for_console_pattern(self, '>> OpenBIOS') wait_for_console_pattern(self, '>> CPU type PowerPC,MPC86xx') def test_ppc_7450(self): - """ - :avocado: tags=cpu:7450 - """ self.require_accelerator("tcg") + self.set_machine('g3beige') self.vm.set_console() + self.vm.add_args('-cpu', '7450') self.vm.launch() wait_for_console_pattern(self, '>> OpenBIOS') wait_for_console_pattern(self, '>> CPU type PowerPC,G4') def test_ppc_7451(self): - """ - :avocado: tags=cpu:7451 - """ self.require_accelerator("tcg") + self.set_machine('g3beige') self.vm.set_console() + self.vm.add_args('-cpu', '7451') self.vm.launch() wait_for_console_pattern(self, '>> OpenBIOS') wait_for_console_pattern(self, '>> CPU type PowerPC,G4') def test_ppc_7455(self): - """ - :avocado: tags=cpu:7455 - """ self.require_accelerator("tcg") + self.set_machine('g3beige') self.vm.set_console() + self.vm.add_args('-cpu', '7455') self.vm.launch() wait_for_console_pattern(self, '>> OpenBIOS') wait_for_console_pattern(self, '>> CPU type PowerPC,G4') def test_ppc_7457(self): - """ - :avocado: tags=cpu:7457 - """ self.require_accelerator("tcg") + self.set_machine('g3beige') self.vm.set_console() + self.vm.add_args('-cpu', '7457') self.vm.launch() wait_for_console_pattern(self, '>> OpenBIOS') wait_for_console_pattern(self, '>> CPU type PowerPC,G4') def test_ppc_7457a(self): - """ - :avocado: tags=cpu:7457a - """ self.require_accelerator("tcg") + self.set_machine('g3beige') self.vm.set_console() + self.vm.add_args('-cpu', '7457a') self.vm.launch() wait_for_console_pattern(self, '>> OpenBIOS') wait_for_console_pattern(self, '>> CPU type PowerPC,G4') + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/functional/test_ppc_amiga.py b/tests/functional/test_ppc_amiga.py new file mode 100755 index 0000000000..b793b5c432 --- /dev/null +++ b/tests/functional/test_ppc_amiga.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python3 +# +# Test AmigaNG boards +# +# Copyright (c) 2023 BALATON Zoltan +# +# This work is licensed under the terms of the GNU GPL, version 2 or +# later. See the COPYING file in the top-level directory. + +import subprocess + +from qemu_test import QemuSystemTest, Asset +from qemu_test import wait_for_console_pattern, run_cmd +from zipfile import ZipFile + +class AmigaOneMachine(QemuSystemTest): + + timeout = 90 + + ASSET_IMAGE = Asset( + ('https://www.hyperion-entertainment.com/index.php/' + 'downloads?view=download&format=raw&file=25'), + '8ff39330ba47d4f64de4ee8fd6809e9c010a9ef17fe51e95c3c1d53437cb481f') + + def test_ppc_amigaone(self): + self.require_accelerator("tcg") + self.set_machine('amigaone') + tar_name = 'A1Firmware_Floppy_05-Mar-2005.zip' + zip_file = self.ASSET_IMAGE.fetch() + with ZipFile(zip_file, 'r') as zf: + zf.extractall(path=self.workdir) + bios_fh = open(self.workdir + "/u-boot-amigaone.bin", "wb") + subprocess.run(['tail', '-c', '524288', + self.workdir + "/floppy_edition/updater.image"], + stdout=bios_fh) + + self.vm.set_console() + self.vm.add_args('-bios', self.workdir + '/u-boot-amigaone.bin') + self.vm.launch() + wait_for_console_pattern(self, 'FLASH:') + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/avocado/ppc_bamboo.py b/tests/functional/test_ppc_bamboo.py index a81be3d608..e72cbdee12 100644..100755 --- a/tests/avocado/ppc_bamboo.py +++ b/tests/functional/test_ppc_bamboo.py @@ -1,3 +1,5 @@ +#!/usr/bin/env python3 +# # Test that Linux kernel boots on the ppc bamboo board and check the console # # Copyright (c) 2021 Red Hat @@ -5,30 +7,26 @@ # This work is licensed under the terms of the GNU GPL, version 2 or # later. See the COPYING file in the top-level directory. -from avocado.utils import archive -from avocado_qemu import QemuSystemTest -from avocado_qemu import wait_for_console_pattern -from avocado_qemu import exec_command_and_wait_for_pattern +from qemu_test.utils import archive_extract +from qemu_test import QemuSystemTest, Asset +from qemu_test import wait_for_console_pattern +from qemu_test import exec_command_and_wait_for_pattern class BambooMachine(QemuSystemTest): timeout = 90 + ASSET_IMAGE = Asset( + ('http://landley.net/aboriginal/downloads/binaries/' + 'system-image-powerpc-440fp.tar.gz'), + 'c12b58f841c775a0e6df4832a55afe6b74814d1565d08ddeafc1fb949a075c5e') + def test_ppc_bamboo(self): - """ - :avocado: tags=arch:ppc - :avocado: tags=machine:bamboo - :avocado: tags=cpu:440epb - :avocado: tags=device:rtl8139 - :avocado: tags=accel:tcg - """ + self.set_machine('bamboo') self.require_accelerator("tcg") self.require_netdev('user') - tar_url = ('http://landley.net/aboriginal/downloads/binaries/' - 'system-image-powerpc-440fp.tar.gz') - tar_hash = '53e5f16414b195b82d2c70272f81c2eedb39bad9' - file_path = self.fetch_asset(tar_url, asset_hash=tar_hash) - archive.extract(file_path, self.workdir) + file_path = self.ASSET_IMAGE.fetch() + archive_extract(file_path, self.workdir) self.vm.set_console() self.vm.add_args('-kernel', self.workdir + '/system-image-powerpc-440fp/linux', @@ -40,3 +38,6 @@ class BambooMachine(QemuSystemTest): exec_command_and_wait_for_pattern(self, 'ping 10.0.2.2', '10.0.2.2 is alive!') exec_command_and_wait_for_pattern(self, 'halt', 'System Halted') + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/avocado/ppc_mpc8544ds.py b/tests/functional/test_ppc_mpc8544ds.py index b599fb1cc9..2b3f0894ae 100644..100755 --- a/tests/avocado/ppc_mpc8544ds.py +++ b/tests/functional/test_ppc_mpc8544ds.py @@ -1,3 +1,5 @@ +#!/usr/bin/env python3 +# # Test that Linux kernel boots on ppc machines and check the console # # Copyright (c) 2018, 2020 Red Hat, Inc. @@ -5,9 +7,9 @@ # This work is licensed under the terms of the GNU GPL, version 2 or # later. See the COPYING file in the top-level directory. -from avocado.utils import archive -from avocado_qemu import QemuSystemTest -from avocado_qemu import wait_for_console_pattern +from qemu_test.utils import archive_extract +from qemu_test import QemuSystemTest, Asset +from qemu_test import wait_for_console_pattern class Mpc8544dsMachine(QemuSystemTest): @@ -15,20 +17,21 @@ class Mpc8544dsMachine(QemuSystemTest): KERNEL_COMMON_COMMAND_LINE = 'printk.time=0 ' panic_message = 'Kernel panic - not syncing' + ASSET_IMAGE = Asset( + ('https://qemu-advcal.gitlab.io/qac-best-of-multiarch/download/' + 'day04.tar.xz'), + '88bc83f3c9f3d633bcfc108a6342d677abca247066a2fb8d4636744a0d319f94') + def test_ppc_mpc8544ds(self): - """ - :avocado: tags=arch:ppc - :avocado: tags=machine:mpc8544ds - :avocado: tags=accel:tcg - """ self.require_accelerator("tcg") - tar_url = ('https://qemu-advcal.gitlab.io' - '/qac-best-of-multiarch/download/day04.tar.xz') - tar_hash = 'f46724d281a9f30fa892d458be7beb7d34dc25f9' - file_path = self.fetch_asset(tar_url, asset_hash=tar_hash) - archive.extract(file_path, self.workdir) + self.set_machine('mpc8544ds') + file_path = self.ASSET_IMAGE.fetch() + archive_extract(file_path, self.workdir, member='creek/creek.bin') self.vm.set_console() self.vm.add_args('-kernel', self.workdir + '/creek/creek.bin') self.vm.launch() wait_for_console_pattern(self, 'QEMU advent calendar 2020', self.panic_message) + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/avocado/ppc_virtex_ml507.py b/tests/functional/test_ppc_virtex_ml507.py index a73f8ae396..ffa9a0633e 100644..100755 --- a/tests/avocado/ppc_virtex_ml507.py +++ b/tests/functional/test_ppc_virtex_ml507.py @@ -1,3 +1,5 @@ +#!/usr/bin/env python3 +# # Test that Linux kernel boots on ppc machines and check the console # # Copyright (c) 2018, 2020 Red Hat, Inc. @@ -5,9 +7,9 @@ # This work is licensed under the terms of the GNU GPL, version 2 or # later. See the COPYING file in the top-level directory. -from avocado.utils import archive -from avocado_qemu import QemuSystemTest -from avocado_qemu import wait_for_console_pattern +from qemu_test.utils import archive_extract +from qemu_test import QemuSystemTest, Asset +from qemu_test import wait_for_console_pattern class VirtexMl507Machine(QemuSystemTest): @@ -15,18 +17,16 @@ class VirtexMl507Machine(QemuSystemTest): KERNEL_COMMON_COMMAND_LINE = 'printk.time=0 ' panic_message = 'Kernel panic - not syncing' + ASSET_IMAGE = Asset( + ('https://qemu-advcal.gitlab.io/qac-best-of-multiarch/download/' + 'day08.tar.xz'), + 'cefe5b8aeb5e9d2d1d4fd22dcf48d917d68d5a765132bf2ddd6332dc393b824c') + def test_ppc_virtex_ml507(self): - """ - :avocado: tags=arch:ppc - :avocado: tags=machine:virtex-ml507 - :avocado: tags=accel:tcg - """ self.require_accelerator("tcg") - tar_url = ('https://qemu-advcal.gitlab.io' - '/qac-best-of-multiarch/download/day08.tar.xz') - tar_hash = '74c68f5af7a7b8f21c03097b298f3bb77ff52c1f' - file_path = self.fetch_asset(tar_url, asset_hash=tar_hash) - archive.extract(file_path, self.workdir) + self.set_machine('virtex-ml507') + file_path = self.ASSET_IMAGE.fetch() + archive_extract(file_path, self.workdir) self.vm.set_console() self.vm.add_args('-kernel', self.workdir + '/hippo/hippo.linux', '-dtb', self.workdir + '/hippo/virtex440-ml507.dtb', @@ -34,3 +34,6 @@ class VirtexMl507Machine(QemuSystemTest): self.vm.launch() wait_for_console_pattern(self, 'QEMU advent calendar 2020', self.panic_message) + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/avocado/machine_rx_gdbsim.py b/tests/functional/test_rx_gdbsim.py index 6bd9ce8199..5687f756bb 100644..100755 --- a/tests/avocado/machine_rx_gdbsim.py +++ b/tests/functional/test_rx_gdbsim.py @@ -1,3 +1,5 @@ +#!/usr/bin/env python3 +# # Functional test that boots a Linux kernel and checks the console # # Copyright (c) 2018 Red Hat, Inc. @@ -10,11 +12,11 @@ import os -from avocado import skipUnless -from avocado_qemu import QemuSystemTest -from avocado_qemu import exec_command_and_wait_for_pattern -from avocado_qemu import wait_for_console_pattern -from avocado.utils import archive +from unittest import skipUnless +from qemu_test import QemuSystemTest, Asset +from qemu_test import exec_command_and_wait_for_pattern +from qemu_test import wait_for_console_pattern +from qemu_test.utils import gzip_uncompress class RxGdbSimMachine(QemuSystemTest): @@ -22,19 +24,25 @@ class RxGdbSimMachine(QemuSystemTest): timeout = 30 KERNEL_COMMON_COMMAND_LINE = 'printk.time=0 ' + ASSET_UBOOT = Asset( + 'https://acc.dl.osdn.jp/users/23/23888/u-boot.bin.gz', + '7146567d669e91dbac166384b29aeba1715beb844c8551e904b86831bfd9d046') + ASSET_DTB = Asset( + 'https://acc.dl.osdn.jp/users/23/23887/rx-virt.dtb', + 'aa278d9c1907a4501741d7ee57e7f65c02dd1b3e0323b33c6d4247f1b32cf29a') + ASSET_KERNEL = Asset( + 'http://acc.dl.osdn.jp/users/23/23845/zImage', + 'baa43205e74a7220ed8482188c5e9ce497226712abb7f4e7e4f825ce19ff9656') + def test_uboot(self): """ U-Boot and checks that the console is operational. - - :avocado: tags=arch:rx - :avocado: tags=machine:gdbsim-r5f562n8 - :avocado: tags=endian:little - :avocado: tags=flaky """ - uboot_url = ('https://acc.dl.osdn.jp/users/23/23888/u-boot.bin.gz') - uboot_hash = '9b78dbd43b40b2526848c0b1ce9de02c24f4dcdb' - uboot_path = self.fetch_asset(uboot_url, asset_hash=uboot_hash) - uboot_path = archive.uncompress(uboot_path, self.workdir) + self.set_machine('gdbsim-r5f562n8') + + uboot_path_gz = self.ASSET_UBOOT.fetch() + uboot_path = os.path.join(self.workdir, 'u-boot.bin') + gzip_uncompress(uboot_path_gz, uboot_path) self.vm.set_console() self.vm.add_args('-bios', uboot_path, @@ -50,18 +58,11 @@ class RxGdbSimMachine(QemuSystemTest): def test_linux_sash(self): """ Boots a Linux kernel and checks that the console is operational. - - :avocado: tags=arch:rx - :avocado: tags=machine:gdbsim-r5f562n7 - :avocado: tags=endian:little - :avocado: tags=flaky """ - dtb_url = ('https://acc.dl.osdn.jp/users/23/23887/rx-virt.dtb') - dtb_hash = '7b4e4e2c71905da44e86ce47adee2210b026ac18' - dtb_path = self.fetch_asset(dtb_url, asset_hash=dtb_hash) - kernel_url = ('http://acc.dl.osdn.jp/users/23/23845/zImage') - kernel_hash = '39a81067f8d72faad90866ddfefa19165d68fc99' - kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) + self.set_machine('gdbsim-r5f562n7') + + dtb_path = self.ASSET_DTB.fetch() + kernel_path = self.ASSET_KERNEL.fetch() self.vm.set_console() kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'earlycon' @@ -72,3 +73,6 @@ class RxGdbSimMachine(QemuSystemTest): wait_for_console_pattern(self, 'Sash command shell (version 1.1.1)', failure_message='Kernel panic - not syncing') exec_command_and_wait_for_pattern(self, 'printenv', 'TERM=linux') + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/avocado/machine_s390_ccw_virtio.py b/tests/functional/test_s390x_ccw_virtio.py index 26e938c9e9..f7acd90a89 100644..100755 --- a/tests/avocado/machine_s390_ccw_virtio.py +++ b/tests/functional/test_s390x_ccw_virtio.py @@ -1,3 +1,5 @@ +#!/usr/bin/env python3 +# # Functional test that boots an s390x Linux guest with ccw and PCI devices # attached and checks whether the devices are recognized by Linux # @@ -12,17 +14,38 @@ import os import tempfile -from avocado import skipUnless -from avocado_qemu import QemuSystemTest -from avocado_qemu import exec_command_and_wait_for_pattern -from avocado_qemu import wait_for_console_pattern -from avocado.utils import archive +from qemu_test import QemuSystemTest, Asset +from qemu_test import exec_command_and_wait_for_pattern +from qemu_test import wait_for_console_pattern +from qemu_test.utils import lzma_uncompress class S390CCWVirtioMachine(QemuSystemTest): KERNEL_COMMON_COMMAND_LINE = 'printk.time=0 ' timeout = 120 + ASSET_BUSTER_KERNEL = Asset( + ('https://snapshot.debian.org/archive/debian/' + '20201126T092837Z/dists/buster/main/installer-s390x/' + '20190702+deb10u6/images/generic/kernel.debian'), + 'd411d17c39ae7ad38d27534376cbe88b68b403c325739364122c2e6f1537e818') + ASSET_BUSTER_INITRD = Asset( + ('https://snapshot.debian.org/archive/debian/' + '20201126T092837Z/dists/buster/main/installer-s390x/' + '20190702+deb10u6/images/generic/initrd.debian'), + '836bbd0fe6a5ca81274c28c2b063ea315ce1868660866e9b60180c575fef9fd5') + + ASSET_F31_KERNEL = Asset( + ('https://archives.fedoraproject.org/pub/archive' + '/fedora-secondary/releases/31/Server/s390x/os' + '/images/kernel.img'), + '480859574f3f44caa6cd35c62d70e1ac0609134e22ce2a954bbed9b110c06e0b') + ASSET_F31_INITRD = Asset( + ('https://archives.fedoraproject.org/pub/archive' + '/fedora-secondary/releases/31/Server/s390x/os' + '/images/initrd.img'), + '04c46095b2c49020b1c2327158898b7db747e4892ae319726192fb949716aa9c') + def wait_for_console_pattern(self, success_message, vm=None): wait_for_console_pattern(self, success_message, failure_message='Kernel panic - not syncing', @@ -41,23 +64,10 @@ class S390CCWVirtioMachine(QemuSystemTest): self.dmesg_clear_count += 1 def test_s390x_devices(self): + self.set_machine('s390-ccw-virtio') - """ - :avocado: tags=arch:s390x - :avocado: tags=machine:s390-ccw-virtio - """ - - kernel_url = ('https://snapshot.debian.org/archive/debian/' - '20201126T092837Z/dists/buster/main/installer-s390x/' - '20190702+deb10u6/images/generic/kernel.debian') - kernel_hash = '5821fbee57d6220a067a8b967d24595621aa1eb6' - kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) - - initrd_url = ('https://snapshot.debian.org/archive/debian/' - '20201126T092837Z/dists/buster/main/installer-s390x/' - '20190702+deb10u6/images/generic/initrd.debian') - initrd_hash = '81ba09c97bef46e8f4660ac25b4ac0a5be3a94d6' - initrd_path = self.fetch_asset(initrd_url, asset_hash=initrd_hash) + kernel_path = self.ASSET_BUSTER_KERNEL.fetch() + initrd_path = self.ASSET_BUSTER_INITRD.fetch() self.vm.set_console() kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + @@ -160,29 +170,13 @@ class S390CCWVirtioMachine(QemuSystemTest): def test_s390x_fedora(self): + self.set_machine('s390-ccw-virtio') - """ - :avocado: tags=arch:s390x - :avocado: tags=machine:s390-ccw-virtio - :avocado: tags=device:virtio-gpu - :avocado: tags=device:virtio-crypto - :avocado: tags=device:virtio-net - :avocado: tags=flaky - """ - - kernel_url = ('https://archives.fedoraproject.org/pub/archive' - '/fedora-secondary/releases/31/Server/s390x/os' - '/images/kernel.img') - kernel_hash = 'b93d1efcafcf29c1673a4ce371a1f8b43941cfeb' - kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) + kernel_path = self.ASSET_F31_KERNEL.fetch() - initrd_url = ('https://archives.fedoraproject.org/pub/archive' - '/fedora-secondary/releases/31/Server/s390x/os' - '/images/initrd.img') - initrd_hash = '3de45d411df5624b8d8ef21cd0b44419ab59b12f' - initrd_path_xz = self.fetch_asset(initrd_url, asset_hash=initrd_hash) + initrd_path_xz = self.ASSET_F31_INITRD.fetch() initrd_path = os.path.join(self.workdir, 'initrd-raw.img') - archive.lzma_uncompress(initrd_path_xz, initrd_path) + lzma_uncompress(initrd_path_xz, initrd_path) self.vm.set_console() kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + ' audit=0 ' @@ -200,6 +194,8 @@ class S390CCWVirtioMachine(QemuSystemTest): '-device', 'virtio-rng-ccw,devno=fe.1.9876', '-device', 'virtio-gpu-ccw,devno=fe.2.5432') self.vm.launch() + self.wait_for_console_pattern('Kernel command line: %s' + % kernel_command_line) self.wait_for_console_pattern('Entering emergency mode') # Some tests to see whether the CLI options have been considered: @@ -275,3 +271,6 @@ class S390CCWVirtioMachine(QemuSystemTest): exec_command_and_wait_for_pattern(self, 'while ! (dmesg -c | grep Start.virtcrypto_remove) ; do' ' sleep 1 ; done', 'Start virtcrypto_remove.') + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/avocado/s390_topology.py b/tests/functional/test_s390x_topology.py index 9154ac8776..20727f6bdf 100644..100755 --- a/tests/avocado/s390_topology.py +++ b/tests/functional/test_s390x_topology.py @@ -1,3 +1,5 @@ +#!/usr/bin/env python3 +# # Functional test that boots a Linux kernel and checks the console # # Copyright IBM Corp. 2023 @@ -9,16 +11,13 @@ # later. See the COPYING file in the top-level directory. import os -import shutil import time -from avocado_qemu import QemuSystemTest -from avocado_qemu import exec_command -from avocado_qemu import exec_command_and_wait_for_pattern -from avocado_qemu import interrupt_interactive_console_until_pattern -from avocado_qemu import wait_for_console_pattern -from avocado.utils import process -from avocado.utils import archive +from qemu_test import QemuSystemTest, Asset +from qemu_test import exec_command +from qemu_test import exec_command_and_wait_for_pattern +from qemu_test import wait_for_console_pattern +from qemu_test.utils import lzma_uncompress class S390CPUTopology(QemuSystemTest): @@ -47,6 +46,17 @@ class S390CPUTopology(QemuSystemTest): 'root=/dev/ram ' 'selinux=0 ' 'rdinit=/bin/sh') + ASSET_F35_KERNEL = Asset( + ('https://archives.fedoraproject.org/pub/archive' + '/fedora-secondary/releases/35/Server/s390x/os' + '/images/kernel.img'), + '1f2dddfd11bb1393dd2eb2e784036fbf6fc11057a6d7d27f9eb12d3edc67ef73') + + ASSET_F35_INITRD = Asset( + ('https://archives.fedoraproject.org/pub/archive' + '/fedora-secondary/releases/35/Server/s390x/os' + '/images/initrd.img'), + '1100145fbca00240c8c372ae4b89b48c99844bc189b3dfbc3f481dc60055ca46') def wait_until_booted(self): wait_for_console_pattern(self, 'no job control', @@ -78,21 +88,10 @@ class S390CPUTopology(QemuSystemTest): We need a minimal root filesystem with a shell. """ self.require_accelerator("kvm") - kernel_url = ('https://archives.fedoraproject.org/pub/archive' - '/fedora-secondary/releases/35/Server/s390x/os' - '/images/kernel.img') - kernel_hash = '0d1aaaf303f07cf0160c8c48e56fe638' - kernel_path = self.fetch_asset(kernel_url, algorithm='md5', - asset_hash=kernel_hash) - - initrd_url = ('https://archives.fedoraproject.org/pub/archive' - '/fedora-secondary/releases/35/Server/s390x/os' - '/images/initrd.img') - initrd_hash = 'a122057d95725ac030e2ec51df46e172' - initrd_path_xz = self.fetch_asset(initrd_url, algorithm='md5', - asset_hash=initrd_hash) + kernel_path = self.ASSET_F35_KERNEL.fetch() + initrd_path_xz = self.ASSET_F35_INITRD.fetch() initrd_path = os.path.join(self.workdir, 'initrd-raw.img') - archive.lzma_uncompress(initrd_path_xz, initrd_path) + lzma_uncompress(initrd_path_xz, initrd_path) self.vm.set_console() kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE @@ -115,10 +114,8 @@ class S390CPUTopology(QemuSystemTest): def test_single(self): """ This test checks the simplest topology with a single CPU. - - :avocado: tags=arch:s390x - :avocado: tags=machine:s390-ccw-virtio """ + self.set_machine('s390-ccw-virtio') self.kernel_init() self.vm.launch() self.wait_until_booted() @@ -127,10 +124,8 @@ class S390CPUTopology(QemuSystemTest): def test_default(self): """ This test checks the implicit topology. - - :avocado: tags=arch:s390x - :avocado: tags=machine:s390-ccw-virtio """ + self.set_machine('s390-ccw-virtio') self.kernel_init() self.vm.add_args('-smp', '13,drawers=2,books=2,sockets=3,cores=2,maxcpus=24') @@ -154,10 +149,8 @@ class S390CPUTopology(QemuSystemTest): """ This test checks the topology modification by moving a CPU to another socket: CPU 0 is moved from socket 0 to socket 2. - - :avocado: tags=arch:s390x - :avocado: tags=machine:s390-ccw-virtio """ + self.set_machine('s390-ccw-virtio') self.kernel_init() self.vm.add_args('-smp', '1,drawers=2,books=2,sockets=3,cores=2,maxcpus=24') @@ -174,10 +167,8 @@ class S390CPUTopology(QemuSystemTest): """ This test verifies that a CPU defined with the '-device' command line option finds its right place inside the topology. - - :avocado: tags=arch:s390x - :avocado: tags=machine:s390-ccw-virtio """ + self.set_machine('s390-ccw-virtio') self.kernel_init() self.vm.add_args('-smp', '1,drawers=2,books=2,sockets=3,cores=2,maxcpus=24') @@ -221,10 +212,8 @@ class S390CPUTopology(QemuSystemTest): """ This test verifies that QEMU modifies the entitlement change after several guest polarization change requests. - - :avocado: tags=arch:s390x - :avocado: tags=machine:s390-ccw-virtio """ + self.set_machine('s390-ccw-virtio') self.kernel_init() self.vm.launch() self.wait_until_booted() @@ -267,10 +256,8 @@ class S390CPUTopology(QemuSystemTest): """ This test verifies that QEMU modifies the entitlement after a guest request and that the guest sees the change. - - :avocado: tags=arch:s390x - :avocado: tags=machine:s390-ccw-virtio """ + self.set_machine('s390-ccw-virtio') self.kernel_init() self.vm.launch() self.wait_until_booted() @@ -313,10 +300,8 @@ class S390CPUTopology(QemuSystemTest): CPU is made dedicated. QEMU retains the entitlement value when horizontal polarization is in effect. For the guest, the field shows the effective value of the entitlement. - - :avocado: tags=arch:s390x - :avocado: tags=machine:s390-ccw-virtio """ + self.set_machine('s390-ccw-virtio') self.kernel_init() self.vm.launch() self.wait_until_booted() @@ -345,10 +330,8 @@ class S390CPUTopology(QemuSystemTest): This test verifies that QEMU does not accept to overload a socket. The socket-id 0 on book-id 0 already contains CPUs 0 and 1 and can not accept any new CPU while socket-id 0 on book-id 1 is free. - - :avocado: tags=arch:s390x - :avocado: tags=machine:s390-ccw-virtio """ + self.set_machine('s390-ccw-virtio') self.kernel_init() self.vm.add_args('-smp', '3,drawers=2,books=2,sockets=3,cores=2,maxcpus=24') @@ -369,10 +352,8 @@ class S390CPUTopology(QemuSystemTest): """ This test verifies that QEMU refuses to lower the entitlement of a dedicated CPU - - :avocado: tags=arch:s390x - :avocado: tags=machine:s390-ccw-virtio """ + self.set_machine('s390-ccw-virtio') self.kernel_init() self.vm.launch() self.wait_until_booted() @@ -417,10 +398,8 @@ class S390CPUTopology(QemuSystemTest): """ This test verifies that QEMU refuses to move a CPU to an nonexistent location - - :avocado: tags=arch:s390x - :avocado: tags=machine:s390-ccw-virtio """ + self.set_machine('s390-ccw-virtio') self.kernel_init() self.vm.launch() self.wait_until_booted() @@ -437,3 +416,6 @@ class S390CPUTopology(QemuSystemTest): self.assertEqual(res['error']['class'], 'GenericError') self.check_topology(0, 0, 0, 0, 'medium', False) + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/functional/test_sparc64_sun4u.py b/tests/functional/test_sparc64_sun4u.py new file mode 100755 index 0000000000..32e245f4ad --- /dev/null +++ b/tests/functional/test_sparc64_sun4u.py @@ -0,0 +1,41 @@ +#!/usr/bin/env python3 +# +# Functional test that boots a Linux kernel and checks the console +# +# Copyright (c) 2020 Red Hat, Inc. +# +# Author: +# Thomas Huth <thuth@redhat.com> +# +# This work is licensed under the terms of the GNU GPL, version 2 or +# later. See the COPYING file in the top-level directory. + +import os + +from qemu_test import QemuSystemTest, Asset +from qemu_test import wait_for_console_pattern +from qemu_test.utils import archive_extract + +class Sun4uMachine(QemuSystemTest): + """Boots the Linux kernel and checks that the console is operational""" + + timeout = 90 + + ASSET_IMAGE = Asset( + ('https://qemu-advcal.gitlab.io/qac-best-of-multiarch/download/' + 'day23.tar.xz'), + 'a3ed92450704af244178351afd0e769776e7decb298e95a63abfd9a6e3f6c854') + + def test_sparc64_sun4u(self): + self.set_machine('sun4u') + file_path = self.ASSET_IMAGE.fetch() + kernel_name = 'day23/vmlinux' + archive_extract(file_path, self.workdir, kernel_name) + self.vm.set_console() + self.vm.add_args('-kernel', os.path.join(self.workdir, kernel_name), + '-append', 'printk.time=0') + self.vm.launch() + wait_for_console_pattern(self, 'Starting logging: OK') + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/avocado/version.py b/tests/functional/test_version.py index c6139568a1..3ab3b67f7e 100644..100755 --- a/tests/avocado/version.py +++ b/tests/functional/test_version.py @@ -1,3 +1,5 @@ +#!/usr/bin/env python3 +# # Version check example test # # Copyright (c) 2018 Red Hat, Inc. @@ -9,17 +11,18 @@ # later. See the COPYING file in the top-level directory. -from avocado_qemu import QemuSystemTest +from qemu_test import QemuSystemTest class Version(QemuSystemTest): - """ - :avocado: tags=quick - :avocado: tags=machine:none - """ + def test_qmp_human_info_version(self): + self.set_machine('none') self.vm.add_args('-nodefaults') self.vm.launch() res = self.vm.cmd('human-monitor-command', command_line='info version') self.assertRegex(res, r'^(\d+\.\d+\.\d)') + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/avocado/virtio-gpu.py b/tests/functional/test_virtio_gpu.py index 6091f614a4..441cbdcf2d 100644..100755 --- a/tests/avocado/virtio-gpu.py +++ b/tests/functional/test_virtio_gpu.py @@ -1,14 +1,16 @@ +#!/usr/bin/env python3 +# # virtio-gpu tests # # This work is licensed under the terms of the GNU GPL, version 2 or # later. See the COPYING file in the top-level directory. -from avocado_qemu import BUILD_DIR -from avocado_qemu import QemuSystemTest -from avocado_qemu import wait_for_console_pattern -from avocado_qemu import exec_command_and_wait_for_pattern -from avocado_qemu import is_readable_executable_file +from qemu_test import BUILD_DIR +from qemu_test import QemuSystemTest, Asset +from qemu_test import wait_for_console_pattern +from qemu_test import exec_command_and_wait_for_pattern +from qemu_test import is_readable_executable_file from qemu.utils import kvm_available @@ -28,25 +30,18 @@ def pick_default_vug_bin(): class VirtioGPUx86(QemuSystemTest): - """ - :avocado: tags=virtio-gpu - :avocado: tags=arch:x86_64 - :avocado: tags=cpu:host - """ KERNEL_COMMAND_LINE = "printk.time=0 console=ttyS0 rdinit=/bin/bash" - KERNEL_URL = ( - "https://archives.fedoraproject.org/pub/archive/fedora" - "/linux/releases/33/Everything/x86_64/os/images" - "/pxeboot/vmlinuz" - ) - KERNEL_HASH = '1433cfe3f2ffaa44de4ecfb57ec25dc2399cdecf' - INITRD_URL = ( - "https://archives.fedoraproject.org/pub/archive/fedora" - "/linux/releases/33/Everything/x86_64/os/images" - "/pxeboot/initrd.img" - ) - INITRD_HASH = 'c828d68a027b53e5220536585efe03412332c2d9' + ASSET_KERNEL = Asset( + ("https://archives.fedoraproject.org/pub/archive/fedora" + "/linux/releases/33/Everything/x86_64/os/images" + "/pxeboot/vmlinuz"), + '2dc5fb5cfe9ac278fa45640f3602d9b7a08cc189ed63fd9b162b07073e4df397') + ASSET_INITRD = Asset( + ("https://archives.fedoraproject.org/pub/archive/fedora" + "/linux/releases/33/Everything/x86_64/os/images" + "/pxeboot/initrd.img"), + 'c49b97f893a5349e4883452178763e402bdc5caa8845b226a2d1329b5f356045') def wait_for_console_pattern(self, success_message, vm=None): wait_for_console_pattern( @@ -57,16 +52,14 @@ class VirtioGPUx86(QemuSystemTest): ) def test_virtio_vga_virgl(self): - """ - :avocado: tags=device:virtio-vga-gl - """ # FIXME: should check presence of virtio, virgl etc self.require_accelerator('kvm') - kernel_path = self.fetch_asset(self.KERNEL_URL, self.KERNEL_HASH) - initrd_path = self.fetch_asset(self.INITRD_URL, self.INITRD_HASH) + kernel_path = self.ASSET_KERNEL.fetch() + initrd_path = self.ASSET_INITRD.fetch() self.vm.set_console() + self.vm.add_args("-cpu", "host") self.vm.add_args("-m", "2G") self.vm.add_args("-machine", "pc,accel=kvm") self.vm.add_args("-device", "virtio-vga-gl") @@ -83,7 +76,7 @@ class VirtioGPUx86(QemuSystemTest): self.vm.launch() except: # TODO: probably fails because we are missing the VirGL features - self.cancel("VirGL not enabled?") + self.skipTest("VirGL not enabled?") self.wait_for_console_pattern("as init process") exec_command_and_wait_for_pattern( @@ -92,18 +85,15 @@ class VirtioGPUx86(QemuSystemTest): self.wait_for_console_pattern("features: +virgl +edid") def test_vhost_user_vga_virgl(self): - """ - :avocado: tags=device:vhost-user-vga - """ # FIXME: should check presence of vhost-user-gpu, virgl, memfd etc self.require_accelerator('kvm') vug = pick_default_vug_bin() if not vug: - self.cancel("Could not find vhost-user-gpu") + self.skipTest("Could not find vhost-user-gpu") - kernel_path = self.fetch_asset(self.KERNEL_URL, self.KERNEL_HASH) - initrd_path = self.fetch_asset(self.INITRD_URL, self.INITRD_HASH) + kernel_path = self.ASSET_KERNEL.fetch() + initrd_path = self.ASSET_INITRD.fetch() # Create socketpair to connect proxy and remote processes qemu_sock, vug_sock = socket.socketpair( @@ -129,6 +119,7 @@ class VirtioGPUx86(QemuSystemTest): ) self.vm.set_console() + self.vm.add_args("-cpu", "host") self.vm.add_args("-m", "2G") self.vm.add_args("-object", "memory-backend-memfd,id=mem,size=2G") self.vm.add_args("-machine", "pc,memory-backend=mem,accel=kvm") @@ -147,7 +138,7 @@ class VirtioGPUx86(QemuSystemTest): self.vm.launch() except: # TODO: probably fails because we are missing the VirGL features - self.cancel("VirGL not enabled?") + self.skipTest("VirGL not enabled?") self.wait_for_console_pattern("as init process") exec_command_and_wait_for_pattern(self, "/usr/sbin/modprobe virtio_gpu", "features: +virgl +edid") @@ -155,3 +146,6 @@ class VirtioGPUx86(QemuSystemTest): qemu_sock.close() vugp.terminate() vugp.wait() + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/avocado/virtio_version.py b/tests/functional/test_virtio_version.py index afe5e828b5..eb23060564 100644..100755 --- a/tests/avocado/virtio_version.py +++ b/tests/functional/test_virtio_version.py @@ -1,3 +1,4 @@ +#!/usr/bin/env python3 """ Check compatibility of virtio device types """ @@ -12,7 +13,7 @@ import sys import os from qemu.machine import QEMUMachine -from avocado_qemu import QemuSystemTest +from qemu_test import QemuSystemTest # Virtio Device IDs: VIRTIO_NET = 1 @@ -60,8 +61,6 @@ class VirtioVersionCheck(QemuSystemTest): Check if virtio-version-specific device types result in the same device tree created by `disable-modern` and `disable-legacy`. - - :avocado: tags=arch:x86_64 """ # just in case there are failures, show larger diff: @@ -173,3 +172,6 @@ class VirtioVersionCheck(QemuSystemTest): self.check_modern_only('virtio-mouse-pci', VIRTIO_INPUT) self.check_modern_only('virtio-tablet-pci', VIRTIO_INPUT) self.check_modern_only('virtio-keyboard-pci', VIRTIO_INPUT) + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/avocado/x86_cpu_model_versions.py b/tests/functional/test_x86_cpu_model_versions.py index 11101e02b9..bd18acd44f 100644..100755 --- a/tests/avocado/x86_cpu_model_versions.py +++ b/tests/functional/test_x86_cpu_model_versions.py @@ -1,3 +1,4 @@ +#!/usr/bin/env python3 # # Basic validation of x86 versioned CPU models and CPU model aliases # @@ -20,15 +21,13 @@ # License along with this library; if not, see <http://www.gnu.org/licenses/>. # - -import avocado_qemu import re -class X86CPUModelAliases(avocado_qemu.QemuSystemTest): +from qemu_test import QemuSystemTest + +class X86CPUModelAliases(QemuSystemTest): """ Validation of PC CPU model versions and CPU model aliases - - :avocado: tags=arch:x86_64 """ def validate_aliases(self, cpus): for c in cpus.values(): @@ -76,9 +75,8 @@ class X86CPUModelAliases(avocado_qemu.QemuSystemTest): def test_4_0_alias_compatibility(self): """ Check if pc-*-4.0 unversioned CPU model won't be reported as aliases - - :avocado: tags=machine:pc-i440fx-4.0 """ + self.set_machine('pc-i440fx-4.0') # pc-*-4.0 won't expose non-versioned CPU models as aliases # We do this to help management software to keep compatibility # with older QEMU versions that didn't have the versioned CPU model @@ -110,9 +108,8 @@ class X86CPUModelAliases(avocado_qemu.QemuSystemTest): def test_4_1_alias(self): """ Check if unversioned CPU model is an alias pointing to right version - - :avocado: tags=machine:pc-i440fx-4.1 """ + self.set_machine('pc-i440fx-4.1') self.vm.add_args('-S') self.vm.launch() @@ -217,9 +214,8 @@ class X86CPUModelAliases(avocado_qemu.QemuSystemTest): def test_none_alias(self): """ Check if unversioned CPU model is an alias pointing to some version - - :avocado: tags=machine:none """ + self.set_machine('none') self.vm.add_args('-S') self.vm.launch() @@ -243,21 +239,16 @@ class X86CPUModelAliases(avocado_qemu.QemuSystemTest): self.validate_aliases(cpus) -class CascadelakeArchCapabilities(avocado_qemu.QemuSystemTest): +class CascadelakeArchCapabilities(QemuSystemTest): """ Validation of Cascadelake arch-capabilities - - :avocado: tags=arch:x86_64 """ def get_cpu_prop(self, prop): cpu_path = self.vm.cmd('query-cpus-fast')[0].get('qom-path') return self.vm.cmd('qom-get', path=cpu_path, property=prop) def test_4_1(self): - """ - :avocado: tags=machine:pc-i440fx-4.1 - :avocado: tags=cpu:Cascadelake-Server - """ + self.set_machine('pc-i440fx-4.1') # machine-type only: self.vm.add_args('-S') self.set_vm_arg('-cpu', @@ -268,10 +259,7 @@ class CascadelakeArchCapabilities(avocado_qemu.QemuSystemTest): 'pc-i440fx-4.1 + Cascadelake-Server should not have arch-capabilities') def test_4_0(self): - """ - :avocado: tags=machine:pc-i440fx-4.0 - :avocado: tags=cpu:Cascadelake-Server - """ + self.set_machine('pc-i440fx-4.0') self.vm.add_args('-S') self.set_vm_arg('-cpu', 'Cascadelake-Server,x-force-features=on,check=off,' @@ -281,10 +269,7 @@ class CascadelakeArchCapabilities(avocado_qemu.QemuSystemTest): 'pc-i440fx-4.0 + Cascadelake-Server should not have arch-capabilities') def test_set_4_0(self): - """ - :avocado: tags=machine:pc-i440fx-4.0 - :avocado: tags=cpu:Cascadelake-Server - """ + self.set_machine('pc-i440fx-4.0') # command line must override machine-type if CPU model is not versioned: self.vm.add_args('-S') self.set_vm_arg('-cpu', @@ -295,10 +280,7 @@ class CascadelakeArchCapabilities(avocado_qemu.QemuSystemTest): 'pc-i440fx-4.0 + Cascadelake-Server,+arch-capabilities should have arch-capabilities') def test_unset_4_1(self): - """ - :avocado: tags=machine:pc-i440fx-4.1 - :avocado: tags=cpu:Cascadelake-Server - """ + self.set_machine('pc-i440fx-4.1') self.vm.add_args('-S') self.set_vm_arg('-cpu', 'Cascadelake-Server,x-force-features=on,check=off,' @@ -308,10 +290,7 @@ class CascadelakeArchCapabilities(avocado_qemu.QemuSystemTest): 'pc-i440fx-4.1 + Cascadelake-Server,-arch-capabilities should not have arch-capabilities') def test_v1_4_0(self): - """ - :avocado: tags=machine:pc-i440fx-4.0 - :avocado: tags=cpu:Cascadelake-Server - """ + self.set_machine('pc-i440fx-4.0') # versioned CPU model overrides machine-type: self.vm.add_args('-S') self.set_vm_arg('-cpu', @@ -322,10 +301,7 @@ class CascadelakeArchCapabilities(avocado_qemu.QemuSystemTest): 'pc-i440fx-4.0 + Cascadelake-Server-v1 should not have arch-capabilities') def test_v2_4_0(self): - """ - :avocado: tags=machine:pc-i440fx-4.0 - :avocado: tags=cpu:Cascadelake-Server - """ + self.set_machine('pc-i440fx-4.0') self.vm.add_args('-S') self.set_vm_arg('-cpu', 'Cascadelake-Server-v2,x-force-features=on,check=off,' @@ -335,10 +311,7 @@ class CascadelakeArchCapabilities(avocado_qemu.QemuSystemTest): 'pc-i440fx-4.0 + Cascadelake-Server-v2 should have arch-capabilities') def test_v1_set_4_0(self): - """ - :avocado: tags=machine:pc-i440fx-4.0 - :avocado: tags=cpu:Cascadelake-Server - """ + self.set_machine('pc-i440fx-4.0') # command line must override machine-type and versioned CPU model: self.vm.add_args('-S') self.set_vm_arg('-cpu', @@ -349,10 +322,7 @@ class CascadelakeArchCapabilities(avocado_qemu.QemuSystemTest): 'pc-i440fx-4.0 + Cascadelake-Server-v1,+arch-capabilities should have arch-capabilities') def test_v2_unset_4_1(self): - """ - :avocado: tags=machine:pc-i440fx-4.1 - :avocado: tags=cpu:Cascadelake-Server - """ + self.set_machine('pc-i440fx-4.1') self.vm.add_args('-S') self.set_vm_arg('-cpu', 'Cascadelake-Server-v2,x-force-features=on,check=off,' @@ -360,3 +330,6 @@ class CascadelakeArchCapabilities(avocado_qemu.QemuSystemTest): self.vm.launch() self.assertFalse(self.get_cpu_prop('arch-capabilities'), 'pc-i440fx-4.1 + Cascadelake-Server-v2,-arch-capabilities should not have arch-capabilities') + +if __name__ == '__main__': + QemuSystemTest.main() diff --git a/tests/meson.build b/tests/meson.build index 80dd3029cf..907a4c1c98 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -85,3 +85,4 @@ subdir('unit') subdir('qapi-schema') subdir('qtest') subdir('migration') +subdir('functional') diff --git a/tests/qtest/dm163-test.c b/tests/qtest/dm163-test.c index 3161c9208d..4c8e654af2 100644 --- a/tests/qtest/dm163-test.c +++ b/tests/qtest/dm163-test.c @@ -182,6 +182,8 @@ static void test_dm163_gpio_connection(void) g_assert_false(qtest_get_irq(qts, LAT_B)); g_assert_false(qtest_get_irq(qts, SELBK)); g_assert_false(qtest_get_irq(qts, RST_B)); + + qtest_quit(qts); } int main(int argc, char **argv) diff --git a/tests/qtest/libqtest.c b/tests/qtest/libqtest.c index 1326e34291..9d07de1fbd 100644 --- a/tests/qtest/libqtest.c +++ b/tests/qtest/libqtest.c @@ -514,7 +514,12 @@ static QTestState *qtest_init_internal(const char *qemu_bin, kill(s->qemu_pid, SIGSTOP); } #endif - return s; + + /* ask endianness of the target */ + + s->big_endian = qtest_query_target_endianness(s); + + return s; } QTestState *qtest_init_without_qmp_handshake(const char *extra_args) @@ -522,21 +527,11 @@ QTestState *qtest_init_without_qmp_handshake(const char *extra_args) return qtest_init_internal(qtest_qemu_binary(NULL), extra_args); } -QTestState *qtest_init_with_env_no_handshake(const char *var, - const char *extra_args) -{ - return qtest_init_internal(qtest_qemu_binary(var), extra_args); -} - QTestState *qtest_init_with_env(const char *var, const char *extra_args) { QTestState *s = qtest_init_internal(qtest_qemu_binary(var), extra_args); QDict *greeting; - /* ask endianness of the target */ - - s->big_endian = qtest_query_target_endianness(s); - /* Read the QMP greeting and then do the handshake */ greeting = qtest_qmp_receive(s); qobject_unref(greeting); diff --git a/tests/qtest/libqtest.h b/tests/qtest/libqtest.h index c261b7e0b3..beb96b18eb 100644 --- a/tests/qtest/libqtest.h +++ b/tests/qtest/libqtest.h @@ -68,8 +68,6 @@ QTestState *qtest_init(const char *extra_args); */ QTestState *qtest_init_with_env(const char *var, const char *extra_args); -QTestState *qtest_init_with_env_no_handshake(const char *var, - const char *extra_args); /** * qtest_init_without_qmp_handshake: * @extra_args: other arguments to pass to QEMU. CAUTION: these diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index 2f0d3ef080..fc852f3d8b 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -1,4 +1,5 @@ slow_qtests = { + 'ahci-test': 150, 'aspeed_smc-test': 360, 'bios-tables-test' : 910, 'cdrom-test' : 610, diff --git a/tests/qtest/migration-helpers.c b/tests/qtest/migration-helpers.c index 84f49db85e..a43d180c80 100644 --- a/tests/qtest/migration-helpers.c +++ b/tests/qtest/migration-helpers.c @@ -82,11 +82,10 @@ static QDict *SocketAddress_to_qdict(SocketAddress *addr) return dict; } -static SocketAddress *migrate_get_socket_address(QTestState *who) +static SocketAddressList *migrate_get_socket_address(QTestState *who) { QDict *rsp; SocketAddressList *addrs; - SocketAddress *addr; Visitor *iv = NULL; QObject *object; @@ -95,36 +94,35 @@ static SocketAddress *migrate_get_socket_address(QTestState *who) iv = qobject_input_visitor_new(object); visit_type_SocketAddressList(iv, NULL, &addrs, &error_abort); - addr = addrs->value; visit_free(iv); qobject_unref(rsp); - return addr; + return addrs; } static char * migrate_get_connect_uri(QTestState *who) { - SocketAddress *addrs; + SocketAddressList *addrs; char *connect_uri; addrs = migrate_get_socket_address(who); - connect_uri = SocketAddress_to_str(addrs); + connect_uri = SocketAddress_to_str(addrs->value); - qapi_free_SocketAddress(addrs); + qapi_free_SocketAddressList(addrs); return connect_uri; } static QDict * migrate_get_connect_qdict(QTestState *who) { - SocketAddress *addrs; + SocketAddressList *addrs; QDict *connect_qdict; addrs = migrate_get_socket_address(who); - connect_qdict = SocketAddress_to_qdict(addrs); + connect_qdict = SocketAddress_to_qdict(addrs->value); - qapi_free_SocketAddress(addrs); + qapi_free_SocketAddressList(addrs); return connect_qdict; } @@ -144,7 +142,7 @@ static void migrate_set_ports(QTestState *to, QList *channel_list) qdict_haskey(addr, "port") && (strcmp(qdict_get_str(addrdict, "port"), "0") == 0)) { addr_port = qdict_get_str(addr, "port"); - qdict_put_str(addrdict, "port", g_strdup(addr_port)); + qdict_put_str(addrdict, "port", addr_port); } } diff --git a/tests/qtest/migration-test.c b/tests/qtest/migration-test.c index 6c06100d91..9d08101643 100644 --- a/tests/qtest/migration-test.c +++ b/tests/qtest/migration-test.c @@ -64,7 +64,6 @@ static QTestMigrationState dst_state; #define DIRTYLIMIT_TOLERANCE_RANGE 25 /* MB/s */ #define ANALYZE_SCRIPT "scripts/analyze-migration.py" -#define VMSTATE_CHECKER_SCRIPT "scripts/vmstate-static-checker.py" #define QEMU_VM_FILE_MAGIC 0x5145564d #define FILE_TEST_FILENAME "migfile" @@ -146,6 +145,9 @@ static char *bootpath; static void bootfile_delete(void) { + if (!bootpath) { + return; + } unlink(bootpath); g_free(bootpath); bootpath = NULL; @@ -157,10 +159,7 @@ static void bootfile_create(char *dir, bool suspend_me) unsigned char *content; size_t len; - if (bootpath) { - bootfile_delete(); - } - + bootfile_delete(); bootpath = g_strdup_printf("%s/bootsect", dir); if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { /* the assembled x86 boot sector should be exactly one sector large */ @@ -1062,12 +1061,15 @@ test_migrate_tls_x509_start_common(QTestState *from, QCRYPTO_TLS_TEST_CLIENT_HOSTILE_NAME : QCRYPTO_TLS_TEST_CLIENT_NAME, data->clientcert); + test_tls_deinit_cert(&servercertreq); } TLS_CERT_REQ_SIMPLE_SERVER(clientcertreq, cacertreq, data->servercert, args->certhostname, args->certipaddr); + test_tls_deinit_cert(&clientcertreq); + test_tls_deinit_cert(&cacertreq); qtest_qmp_assert_success(from, "{ 'execute': 'object-add'," @@ -1692,85 +1694,6 @@ static void test_analyze_script(void) test_migrate_end(from, to, false); cleanup("migfile"); } - -static void test_vmstate_checker_script(void) -{ - g_autofree gchar *cmd_src = NULL; - g_autofree gchar *cmd_dst = NULL; - g_autofree gchar *vmstate_src = NULL; - g_autofree gchar *vmstate_dst = NULL; - const char *machine_alias, *machine_opts = ""; - g_autofree char *machine = NULL; - const char *arch = qtest_get_arch(); - int pid, wstatus; - const char *python = g_getenv("PYTHON"); - - if (!getenv(QEMU_ENV_SRC) && !getenv(QEMU_ENV_DST)) { - g_test_skip("Test needs two different QEMU versions"); - return; - } - - if (!python) { - g_test_skip("PYTHON variable not set"); - return; - } - - if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { - if (g_str_equal(arch, "i386")) { - machine_alias = "pc"; - } else { - machine_alias = "q35"; - } - } else if (g_str_equal(arch, "s390x")) { - machine_alias = "s390-ccw-virtio"; - } else if (strcmp(arch, "ppc64") == 0) { - machine_alias = "pseries"; - } else if (strcmp(arch, "aarch64") == 0) { - machine_alias = "virt"; - } else { - g_assert_not_reached(); - } - - if (!qtest_has_machine(machine_alias)) { - g_autofree char *msg = g_strdup_printf("machine %s not supported", machine_alias); - g_test_skip(msg); - return; - } - - machine = resolve_machine_version(machine_alias, QEMU_ENV_SRC, - QEMU_ENV_DST); - - vmstate_src = g_strdup_printf("%s/vmstate-src", tmpfs); - vmstate_dst = g_strdup_printf("%s/vmstate-dst", tmpfs); - - cmd_dst = g_strdup_printf("-machine %s,%s -dump-vmstate %s", - machine, machine_opts, vmstate_dst); - cmd_src = g_strdup_printf("-machine %s,%s -dump-vmstate %s", - machine, machine_opts, vmstate_src); - - qtest_init_with_env_no_handshake(QEMU_ENV_SRC, cmd_src); - qtest_init_with_env_no_handshake(QEMU_ENV_DST, cmd_dst); - - pid = fork(); - if (!pid) { - close(1); - open("/dev/null", O_WRONLY); - execl(python, python, VMSTATE_CHECKER_SCRIPT, - "-s", vmstate_src, - "-d", vmstate_dst, - NULL); - g_assert_not_reached(); - } - - g_assert(waitpid(pid, &wstatus, 0) == pid); - if (!WIFEXITED(wstatus) || WEXITSTATUS(wstatus) != 0) { - g_test_message("Failed to run vmstate-static-checker.py"); - g_test_fail(); - } - - cleanup("vmstate-src"); - cleanup("vmstate-dst"); -} #endif static void test_precopy_common(MigrateCommon *args) @@ -2395,6 +2318,7 @@ static void multifd_mapped_ram_fdset_end(QTestState *from, QTestState *to, g_assert(qdict_haskey(resp, "return")); fdsets = qdict_get_qlist(resp, "return"); g_assert(fdsets && qlist_empty(fdsets)); + qobject_unref(resp); } static void *multifd_mapped_ram_fdset_dio(QTestState *from, QTestState *to) @@ -3318,6 +3242,7 @@ static void test_multifd_tcp_cancel(void) /* Make sure QEMU process "to" exited */ qtest_set_expected_status(to, EXIT_FAILURE); qtest_wait_qemu(to); + qtest_quit(to); args = (MigrateStart){ .only_target = true, @@ -3397,15 +3322,18 @@ static QDict *query_vcpu_dirty_limit(QTestState *who) static bool calc_dirtyrate_ready(QTestState *who) { QDict *rsp_return; - gchar *status; + const char *status; + bool ready; rsp_return = query_dirty_rate(who); g_assert(rsp_return); - status = g_strdup(qdict_get_str(rsp_return, "status")); + status = qdict_get_str(rsp_return, "status"); g_assert(status); + ready = g_strcmp0(status, "measuring"); + qobject_unref(rsp_return); - return g_strcmp0(status, "measuring"); + return ready; } static void wait_for_calc_dirtyrate_complete(QTestState *who, @@ -3428,7 +3356,7 @@ static void wait_for_calc_dirtyrate_complete(QTestState *who, static int64_t get_dirty_rate(QTestState *who) { QDict *rsp_return; - gchar *status; + const char *status; QList *rates; const QListEntry *entry; QDict *rate; @@ -3437,7 +3365,7 @@ static int64_t get_dirty_rate(QTestState *who) rsp_return = query_dirty_rate(who); g_assert(rsp_return); - status = g_strdup(qdict_get_str(rsp_return, "status")); + status = qdict_get_str(rsp_return, "status"); g_assert(status); g_assert_cmpstr(status, ==, "measured"); @@ -3823,8 +3751,6 @@ int main(int argc, char **argv) migration_test_add("/migration/bad_dest", test_baddest); #ifndef _WIN32 migration_test_add("/migration/analyze-script", test_analyze_script); - migration_test_add("/migration/vmstate-checker-script", - test_vmstate_checker_script); #endif if (is_x86) { @@ -4026,8 +3952,10 @@ int main(int argc, char **argv) if (g_str_equal(arch, "x86_64") && has_kvm && kvm_dirty_ring_supported()) { migration_test_add("/migration/dirty_ring", test_precopy_unix_dirty_ring); - migration_test_add("/migration/vcpu_dirty_limit", - test_vcpu_dirty_limit); + if (qtest_has_machine("pc")) { + migration_test_add("/migration/vcpu_dirty_limit", + test_vcpu_dirty_limit); + } } ret = g_test_run(); diff --git a/tests/qtest/stm32l4x5_usart-test.c b/tests/qtest/stm32l4x5_usart-test.c index 8902518233..c175ff3064 100644 --- a/tests/qtest/stm32l4x5_usart-test.c +++ b/tests/qtest/stm32l4x5_usart-test.c @@ -202,6 +202,8 @@ static void test_write_read(void) qtest_writel(qts, USART1_BASE_ADDR + A_TDR, 0xFFFFFFFF); const uint32_t tdr = qtest_readl(qts, USART1_BASE_ADDR + A_TDR); g_assert_cmpuint(tdr, ==, 0x000001FF); + + qtest_quit(qts); } static void test_receive_char(void) diff --git a/tests/unit/crypto-tls-x509-helpers.c b/tests/unit/crypto-tls-x509-helpers.c index 3e74ec5b5d..2daecc416c 100644 --- a/tests/unit/crypto-tls-x509-helpers.c +++ b/tests/unit/crypto-tls-x509-helpers.c @@ -135,6 +135,7 @@ void test_tls_init(const char *keyfile) void test_tls_cleanup(const char *keyfile) { asn1_delete_structure(&pkix_asn1); + gnutls_x509_privkey_deinit(privkey); unlink(keyfile); } @@ -502,8 +503,7 @@ void test_tls_write_cert_chain(const char *filename, g_free(buffer); } - -void test_tls_discard_cert(QCryptoTLSTestCertReq *req) +void test_tls_deinit_cert(QCryptoTLSTestCertReq *req) { if (!req->crt) { return; @@ -511,6 +511,15 @@ void test_tls_discard_cert(QCryptoTLSTestCertReq *req) gnutls_x509_crt_deinit(req->crt); req->crt = NULL; +} + +void test_tls_discard_cert(QCryptoTLSTestCertReq *req) +{ + if (!req->crt) { + return; + } + + test_tls_deinit_cert(req); if (getenv("QEMU_TEST_DEBUG_CERTS") == NULL) { unlink(req->filename); diff --git a/tests/unit/crypto-tls-x509-helpers.h b/tests/unit/crypto-tls-x509-helpers.h index 562c160653..2a0f7c04fd 100644 --- a/tests/unit/crypto-tls-x509-helpers.h +++ b/tests/unit/crypto-tls-x509-helpers.h @@ -73,6 +73,12 @@ void test_tls_generate_cert(QCryptoTLSTestCertReq *req, void test_tls_write_cert_chain(const char *filename, gnutls_x509_crt_t *certs, size_t ncerts); +/* + * Deinitialize the QCryptoTLSTestCertReq, but don't delete the certificate + * file on disk. (The caller is then responsible for doing that themselves. + */ +void test_tls_deinit_cert(QCryptoTLSTestCertReq *req); +/* Deinit the QCryptoTLSTestCertReq, and delete the certificate file */ void test_tls_discard_cert(QCryptoTLSTestCertReq *req); void test_tls_init(const char *keyfile); |